diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..818c534 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Eclipse stuff +.classpath +.project +.settings/ + +# netbeans +nbproject/ +nbactions.xml + +# we use maven! +build.xml + +# maven +target/ +dependency-reduced-pom.xml + +# vim +.*.sw[a-p] + +# various other potential build files +build/ +bin/ +dist/ +manifest.mf + +# Mac filesystem dust +.DS_Store/ + +# intellij +*.iml +*.ipr +*.iws +.idea/ + +# Linux temp files +*~ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5654132 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "spigot-api"] + path = spigot-api + url = git@github.com:AlfieC/spigot-api.git +[submodule "spigot-server"] + path = spigot-server + url = git@github.com:AlfieC/spigot-server.git diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..06abd55 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,17 @@ +node { + stage 'Git Clone' + checkout scm + stage 'Git Submodule Update' + sh 'git submodule update --init' + stage 'Apply Patches' + sh './applyPatches.sh' + stage 'Maven Compile' + if (env.BRANCH_NAME == 'master') { + sh 'mvn clean deploy -U' + } else { + sh 'mvn clean package -U' + } + stage 'Jenkins Archive' + step([$class: 'ArtifactArchiver', artifacts: 'mspigot-api/target/*.jar', fingerprint: true]) + step([$class: 'ArtifactArchiver', artifacts: 'mspigot-server/target/*.jar', fingerprint: true]) +} diff --git a/applyPatches.sh b/applyPatches.sh new file mode 100644 index 0000000..a0c4200 --- /dev/null +++ b/applyPatches.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +PS1="$" +basedir=`pwd` +echo "Rebuilding Forked projects.... " + +function applyPatch { + what=$1 + target=$2 + cd "$basedir/$what" + git branch -f upstream >/dev/null + + cd "$basedir" + if [ ! -d "$basedir/$target" ]; then + git clone $1 $target -b upstream + fi + cd "$basedir/$target" + echo "Resetting $target to $what..." + git remote rm upstream 2>/dev/null 2>&1 + git remote add upstream ../$what >/dev/null 2>&1 + git checkout master >/dev/null 2>&1 + git fetch upstream >/dev/null 2>&1 + git reset --hard upstream/upstream + echo " Applying patches to $target..." + git am --abort + git am --3way "$basedir/${what}-Patches/"*.patch + if [ "$?" != "0" ]; then + echo " Something did not apply cleanly to $target." + echo " Please review above details and finish the apply then" + echo " save the changes with rebuildPatches.sh" + exit 1 + else + echo " Patches applied cleanly to $target" + fi +} + +applyPatch spigot-api mspigot-api && applyPatch spigot-server mspigot-server diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cf55a10 --- /dev/null +++ b/pom.xml @@ -0,0 +1,36 @@ + + 4.0.0 + + net.valorhcf + vspigot-parent + dev-SNAPSHOT + pom + + vSpigot + Production version of Spigot for Valor servers. + + + vspigot-server + vspigot-api + + + + UTF-8 + + + + + md_5-releases + http://repo.md-5.net/content/repositories/releases/ + + + bungeecord-releases + https://oss.sonatype.org/content/repositories/snapshots + + + papermc + https://papermc.io/repo/repository/maven-public/ + + + diff --git a/rebuildPatches.sh b/rebuildPatches.sh new file mode 100644 index 0000000..c22230c --- /dev/null +++ b/rebuildPatches.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +( +PS1="$" +basedir="$(cd "$1" && pwd -P)" +workdir="$basedir/work" +echo "Rebuilding patch files from current fork state..." +git config core.safecrlf false + +function cleanupPatches { + cd "$1" + for patch in *.patch; do + echo "$patch" + gitver=$(tail -n 2 "$patch" | grep -ve "^$" | tail -n 1) + diffs=$(git diff --staged "$patch" | grep -E "^(\+|\-)" | grep -Ev "(From [a-z0-9]{32,}|\-\-\- a|\+\+\+ b|.index)") + + testver=$(echo "$diffs" | tail -n 2 | grep -ve "^$" | tail -n 1 | grep "$gitver") + if [ "x$testver" != "x" ]; then + diffs=$(echo "$diffs" | sed 'N;$!P;$!D;$d') + fi + + if [ "x$diffs" == "x" ] ; then + git reset HEAD "$patch" >/dev/null + git checkout -- "$patch" >/dev/null + fi + done +} + +function savePatches { + what=$1 + what_name=$(basename "$what") + target=$2 + echo "Formatting patches for $what..." + + cd "$basedir/${what_name}-Patches/" + if [ -d "$basedir/$target/.git/rebase-apply" ]; then + # in middle of a rebase, be smarter + echo "REBASE DETECTED - PARTIAL SAVE" + last=$(cat "$basedir/$target/.git/rebase-apply/last") + next=$(cat "$basedir/$target/.git/rebase-apply/next") + for i in $(seq -f "%04g" 1 1 $last) + do + if [ $i -lt $next ]; then + rm ${i}-*.patch + fi + done + else + rm -rf *.patch + fi + + cd "$basedir/$target" + + git format-patch --no-stat -N -o "$basedir/${what_name}-Patches/" upstream/upstream >/dev/null + cd "$basedir" + git add -A "$basedir/${what_name}-Patches" + cleanupPatches "$basedir/${what_name}-Patches" + echo " Patches saved for $what to $what_name-Patches/" +} + +savePatches "$workdir/spigot-api" "mspigot-api" +savePatches "$workdir/spigot-server" "mspigot-server" +) diff --git a/rebuildPatchesAPI.sh b/rebuildPatchesAPI.sh new file mode 100644 index 0000000..2cd1e9e --- /dev/null +++ b/rebuildPatchesAPI.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +PS1="$" +basedir=`pwd` +echo "Rebuilding patch files from current fork state..." + +function cleanupPatches { + cd "$1" + for patch in *.patch; do + gitver=$(tail -n 2 $patch | grep -ve "^$" | tail -n 1) + diffs=$(git diff --staged $patch | grep -E "^(\+|\-)" | grep -Ev "(From [a-z0-9]{32,}|\-\-\- a|\+\+\+ b|.index)") + + testver=$(echo "$diffs" | tail -n 2 | grep -ve "^$" | tail -n 1 | grep "$gitver") + if [ "x$testver" != "x" ]; then + mingw=$(uname -s | grep "MINGW") + if [ "x$mingw" != "x" ]; then + diffs=$(echo "$diffs" | head -n $(($(echo "$diffs" | wc -l | sed -r 's/^ +//' | cut -d ' ' -f 1) - 2))) + else + diffs=$(echo "$diffs" | head -n -2) + fi + fi + + + if [ "x$diffs" == "x" ] ; then + git reset HEAD $patch >/dev/null + git checkout -- $patch >/dev/null + fi + done +} + +function savePatches { + what=$1 + target=$2 + cd "$basedir/$target" + git format-patch --no-stat -N -o "$basedir/${what}-Patches/" upstream/upstream + cd "$basedir" + git add "$basedir/${what}-Patches" + cleanupPatches "$basedir/${what}-Patches" + echo " Patches saved for $what to $what-Patches/" +} + +savePatches spigot-api mspigot-api diff --git a/rebuildPatchesServer.sh b/rebuildPatchesServer.sh new file mode 100644 index 0000000..64762ab --- /dev/null +++ b/rebuildPatchesServer.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +PS1="$" +basedir=`pwd` +echo "Rebuilding patch files from current fork state..." + +function cleanupPatches { + cd "$1" + for patch in *.patch; do + gitver=$(tail -n 2 $patch | grep -ve "^$" | tail -n 1) + diffs=$(git diff --staged $patch | grep -E "^(\+|\-)" | grep -Ev "(From [a-z0-9]{32,}|\-\-\- a|\+\+\+ b|.index)") + + testver=$(echo "$diffs" | tail -n 2 | grep -ve "^$" | tail -n 1 | grep "$gitver") + if [ "x$testver" != "x" ]; then + mingw=$(uname -s | grep "MINGW") + if [ "x$mingw" != "x" ]; then + diffs=$(echo "$diffs" | head -n $(($(echo "$diffs" | wc -l | sed -r 's/^ +//' | cut -d ' ' -f 1) - 2))) + else + diffs=$(echo "$diffs" | head -n -2) + fi + fi + + + if [ "x$diffs" == "x" ] ; then + git reset HEAD $patch >/dev/null + git checkout -- $patch >/dev/null + fi + done +} + +function savePatches { + what=$1 + target=$2 + cd "$basedir/$target" + git format-patch --no-stat -N -o "$basedir/${what}-Patches/" upstream/upstream + cd "$basedir" + git add "$basedir/${what}-Patches" + cleanupPatches "$basedir/${what}-Patches" + echo " Patches saved for $what to $what-Patches/" +} + +savePatches spigot-server mspigot-server diff --git a/vspigot-api/.gitignore b/vspigot-api/.gitignore new file mode 100644 index 0000000..818c534 --- /dev/null +++ b/vspigot-api/.gitignore @@ -0,0 +1,36 @@ +# Eclipse stuff +.classpath +.project +.settings/ + +# netbeans +nbproject/ +nbactions.xml + +# we use maven! +build.xml + +# maven +target/ +dependency-reduced-pom.xml + +# vim +.*.sw[a-p] + +# various other potential build files +build/ +bin/ +dist/ +manifest.mf + +# Mac filesystem dust +.DS_Store/ + +# intellij +*.iml +*.ipr +*.iws +.idea/ + +# Linux temp files +*~ diff --git a/vspigot-api/CONTRIBUTING.md b/vspigot-api/CONTRIBUTING.md new file mode 100644 index 0000000..3031b97 --- /dev/null +++ b/vspigot-api/CONTRIBUTING.md @@ -0,0 +1,497 @@ +# How to Contribute + +The Bukkit project prides itself on being community built and driven. We love it when members of our community want to jump right in and get involved, so here's what you need to know. + +## Quick Guide +1. Create or find an issue to address on our [JIRA issue tracker](http://leaky.bukkit.org). +- Does your proposed change [fit Bukkit's goals](#does-the-change-fit-bukkits-goals)? +- Fork the repository if you haven't done so already. +- Make your changes in a new branch (if your change affects both Bukkit and CraftBukkit, we highly suggest you use the same name for your branches in both repos). +- Test your changes. +- Push to your fork and submit a pull request. +- **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +![Life Cycle of a Bukkit Improvement](http://i.imgur.com/Ed6T7AE.png) + +## Getting Started +- You'll need a free [JIRA account](http://leaky.bukkit.org) (on our issue tracker affectionately called Leaky). +- You'll need a free [GitHub account](https://github.com/signup/free). +- Make sure you have a JIRA ticket for your issue at hand. + * Either search the list of current issues and find an appropriate issue. + * Or create one yourself if one does not already exist. + * When creating an issue, make sure to clearly describe the issue (including steps to reproduce it if it is a bug). +- Fork the repository on GitHub. +- **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +## Does the Change Fit Bukkit's Goals? +As a rough guideline, ask yourself the following questions to determine if your proposed change fits the Bukkit project's goals. Please remember that this is only a rough guideline and may or may not reflect the definitive answer to this question. + +* Does it expose an implementation detail of the server software, the protocol or file formats? + + If your change revolves around an implementation detail then it is not proper API design. Examples of bad API design would be along the lines of a packet API, an NBT storage API, or basing an enum on implementation values. + +* Does it result in unexpected behaviour as defined by the Vanilla specification? + + One of the goals of the Bukkit project is to be an extended Minecraft vanilla server - meaning: if you choose to run the Bukkit server without any plugins, it should function exactly as the Minecraft server would with some rare exceptions. If your change alters the behaviour of the server in such a way that you would not have the same experience as you would in Vanilla, your change does not fit the Bukkit project's goals. + +* Does it expose an issue or vulnerability when operating within the Vanilla environment? + + One of the goals of the Bukkit project is to be able to operate within the limitations of the Vanilla environment. If your change results in or exposes the ability to, for example, crash the client when invalid data is set, it does not fit the Bukkit project's needs. + +If you answered yes to any of these questions, chances are high your change does not fit the Bukkit project's goals and will most likely not be accepted. Regardless, there are a few other important questions you need to ask yourself before you start working on a change: + +* Is this change reasonably supportable and maintainable? + +* Is this change future proof? + +## Making the Changes +* Create a branch on your fork where you'll be making your changes. + * Name your branch something relevant to the change you are looking to make. + * Note: if your changes affect both Bukkit and CraftBukkit, it is highly suggested you use the same branch name on both repos. + * To create a branch in Git; + * `git branch relevantBranchName` + * Then checkout the new branch with `git checkout relevantBranchName` +* Check for unnecessary whitespace with `git diff --check` before committing. +* Make sure your code meets [our requirements](#code-requirements). +* If the work you want to do involves editing Minecraft classes, be sure to read over the [Using Minecraft Internals](#using-minecraft-internals) section. +* Make sure your commit messages are in the [proper format](#commit-message-example). +* Test your changes to make sure it actually addresses the issue it should. +* Make sure your code compiles under Java 6, as that is what the project has to be built with. + +### Code Requirements +* We generally follow the [Sun/Oracle coding standards](http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html). + +* No tabs; use 4 spaces instead. + +* No trailing whitespaces. + +* No CRLF line endings, LF only, set your Gits 'core.autocrlf' to 'true'. + + These whitespace requirements are easily and often overlooked. They are critical formatting requirements designed to help simplify a shared heterogeneous development environment. Learn how your IDE functions in order to show you these characters and verify them. Analyse the git diff closely to verify every character and if the PR should include the character change. It is tedious and it is critical. + + Eclipse: http://stackoverflow.com/a/11596227/532590 + NetBeans: http://stackoverflow.com/a/1866385/532590 + +* No 80 column limit or 'weird' midstatement newlines. + +* Any major additions should have documentation ready and provided if applicable (this is usually the case). + +* Try to follow test driven development where applicable. + + Bukkit employs JUnit (http://www.vogella.com/articles/JUnit/article.html) for testing and PRs should attempt to integrate with that framework as appropriate. Bukkit is a large project and what seems simple to a PR author at the time of writing a PR may easily be overlooked later by other authors and updates. Including unit tests with your PR will help to ensure the PR can be easily maintained over time and encourage the Bukkit Team to pull the PR. + +* There needs to be a new line at the end of every file. + +* Imports should be organised by alphabetical order, separated and grouped by package. + + **For example:** + + ```java + import java.io.ByteArrayInputStream; + import java.io.DataInputStream; + import java.io.IOException; + import java.util.ArrayList; + import java.util.Iterator; + import java.util.Random; + import java.util.concurrent.Callable; + + // CraftBukkit start + import java.io.UnsupportedEncodingException; + import java.util.concurrent.ExecutionException; + import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + import java.util.logging.Level; + import java.util.HashSet; + + import org.bukkit.Bukkit; + import org.bukkit.Location; + import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.craftbukkit.inventory.CraftInventoryView; + import org.bukkit.craftbukkit.inventory.CraftItemStack; + import org.bukkit.craftbukkit.util.LazyPlayerSet; + import org.bukkit.craftbukkit.util.Waitable; + import org.bukkit.craftbukkit.entity.CraftPlayer; + import org.bukkit.craftbukkit.event.CraftEventFactory; + import org.bukkit.entity.Player; + import org.bukkit.event.Event; + import org.bukkit.event.block.Action; + import org.bukkit.event.block.SignChangeEvent; + import org.bukkit.event.player.AsyncPlayerChatEvent; + import org.bukkit.event.player.PlayerAnimationEvent; + import org.bukkit.event.player.PlayerChatEvent; + import org.bukkit.event.player.PlayerCommandPreprocessEvent; + import org.bukkit.event.player.PlayerInteractEntityEvent; + import org.bukkit.event.player.PlayerItemHeldEvent; + import org.bukkit.event.player.PlayerKickEvent; + import org.bukkit.event.player.PlayerMoveEvent; + import org.bukkit.event.player.PlayerTeleportEvent; + import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + import org.bukkit.event.player.PlayerToggleSneakEvent; + import org.bukkit.event.player.PlayerToggleSprintEvent; + import org.bukkit.event.inventory.*; + import org.bukkit.event.inventory.InventoryType.SlotType; + import org.bukkit.event.player.PlayerPortalEvent; + import org.bukkit.event.player.PlayerToggleFlightEvent; + import org.bukkit.inventory.CraftingInventory; + import org.bukkit.inventory.InventoryView; + // CraftBukkit end + ``` + +### Using Minecraft Internals +#### Importing a New Minecraft Class +When contributing to the Bukkit project, you will likely find that you need to edit a Minecraft class that isn't already found within the project. In this case, you need to look at [our mc-dev repository](https://github.com/Bukkit/mc-dev), find the class you need, add it to the CraftBukkit repo and include it in its own special commit separate from your other changes. The commit message of this special commit should simply be "Add x for diff visibility", where x is the name of the file you are adding from mc-dev. + +If, however, you need to import multiple files from mc-dev into the Bukkit project, they should all be contained in the same special commit with the commit message "Add files for diff visibility". Note how the commit message no longer specifically mentions any class names. + +#### Making Changes to Minecraft Classes +The Bukkit project employs a Minimal Diff policy to help guide when changes should be made to Minecraft classes and what those changes should be. This is to ensure that any changes made have the smallest impact possible on the update process we go through whenever a Minecraft update is released. As well as keeping the Minimal Diff policy in mind, every change made to a Minecraft class needs to be marked as such with the appropriate CraftBukkit comment. + +##### Minimal Diff Policy +The Minimal Diff policy is a really important part of the project as it reminds us that every change to the Minecraft Internals has an impact on our update process. When people think of the phrase "minimal diffs", they often take it to the extreme - they go completely out of their way to abstract the changes they are trying to make away from editing Minecraft's classes as much as possible. However, this isn't what we mean by "minimal diffs". Instead, when trying to understand the minimal diffs policy, it helps to keep in mind its end goal: to reduce the impact changes we make to Minecraft's internals have on our update process. + +Put simply, the Minimal Diffs Policy simply means to make the smallest change in a Minecraft class possible without duplicating logic. + +Here are a few tips you should keep in mind or common areas you should focus on: + +* Try to avoid duplicating logic or code when making changes. + +* Try to keep your changes easily discernible - don't nest or group several unrelated changes together. + +* If you only use an import once within a class, don't import it and use fully qualified names instead. + +* Try to employ "short circuiting" of logic if at all possible. This means that you should force a conditional to be the value needed to side-step the code block if you would like to ignore that block of code. + + **For example, to short circuit this:** + + ```java + if (!this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { + this.die(); + this.h(); + } + ``` + **You would do this:** + + ```java + if (false && !this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { // CraftBukkit - not needed + this.die(); + this.h(); + } + ``` + +* When adding a validation check, see if the Validate package we already use has a better, more concise method you can use instead. + + **For example, you should use:** + + ```java + Validate.notNull(sender, "Sender cannot be null"); + ``` + + **Instead of:** + + ```java + if (sender == null) { + throw new IllegalArgumentException("Sender cannot be null"); + } + ``` + +* When the change you are attempting to make involves removing code, instead of removing it outright, you should comment it out. + + **For example:** + + ```java + // CraftBukkit start - special case dropping so we can get info from the tile entity + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (world.random.nextFloat() < f) { + ItemStack itemstack = new ItemStack(Item.SKULL.id, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } + } + // CraftBukkit end + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild) { + l |= 8; + world.setData(i, j, k, l, 4); + } + + super.a(world, i, j, k, l, entityhuman); + } + + public void remove(World world, int i, int j, int k, int l, int i1) { + if (!world.isStatic) { + /* CraftBukkit start - drop item in code above, not here + if ((i1 & 8) == 0) { + ItemStack itemstack = new ItemStack(Item.SKULL.id, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } + // CraftBukkit end */ + + super.remove(world, i, j, k, l, i1); + } + } + ``` + +##### General Guidelines +When editing Minecraft's classes, we have a set of rules and guidelines that need to be followed to keep us sane when it comes time for us to update Bukkit. + +**CraftBukkit comments** +Changes to a Minecraft class should be clearly marked using CraftBukkit comments. Here are a few tips to help explain what kind of CraftBukkit comment to use and where to use them: + +* Regardless of what kind of CraftBukkit comment you use, please take care to be explicit and exact with your usage. If the "C" in "CraftBukkit" is capitalised in the example, you should capitalise it when you use it. If the "start" begins with a lowercase "s", you should make sure yours does too. + +* If the change only affects one line of code, you should use an end of line CraftBukkit comment. + + **Examples:** + + If the change is obvious when looking at the diff, then you just need a simple end of line CraftBukkit comment. + + ```java + if (true || minecraftserver.getAllowNether()) { // CraftBukkit + ``` + + If, however, the change is something important to note or difficult to discern, you should include a reason at the end of the end of line CraftBukkit comment. + + ```java + public int fireTicks; // CraftBukkit - private -> public + ``` + + If adding the CraftBukkit comment to the end of the line negatively affects the readability of the code, then you should place the CraftBukkit comment on a new line above the change you made. + + ```java + // CraftBukkit + if (!isEffect && !world.isStatic && world.difficulty >= 2 && world.areChunksLoaded(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2), 10)) { + ``` + +* If the change affects more than one line, you should use a multi-line CraftBukkit comment. + + **Examples:** + + The majority of the time multi-line changes should be accompanied by a reason since they're usually much more complicated than a single line change. We'd like to suggest you follow the same rule as above: if the change is something important to note or difficult to discern, you should include a reason at the end of the end of line CraftBukkit comment, however it is not always clear if this is the case. Looking through the code in the project, you'll see that we sometimes include a reason when we should have left it off and vice versa. + + ```java + // CraftBukkit start - special case dropping so we can get info from the tile entity + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (world.random.nextFloat() < f) { + ItemStack itemstack = new ItemStack(Item.SKULL.id, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } + } + // CraftBukkit end + ```` + + Otherwise, just use a multi-line CraftBukkit comment without a reason. + + ```java + // CraftBukkit start + BlockIgniteEvent event = new BlockIgniteEvent(this.cworld.getBlockAt(i, j, k), BlockIgniteEvent.IgniteCause.LIGHTNING, null); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + world.setTypeIdUpdate(i, j, k, Block.FIRE.id); + } + // CraftBukkit end + ``` + +* CraftBukkit comments should be on the same indentation level of the code block it is in. + + **For example:** + + ```java + if (j == 1) { + // CraftBukkit start - store a reference + ItemStack itemstack4 = playerinventory.getCarried(); + if (itemstack4.count > 0) { + entityhuman.drop(itemstack4.a(1)); + } + + if (itemstack4.count == 0) { + // CraftBukkit end + playerinventory.setCarried((ItemStack) null); + } + } + ``` + +**Other guidelines** + +* When adding imports to a Minecraft class, they should be organised by alphabetical order, separated and grouped by package. + + **For example:** + + ```java + import java.io.ByteArrayInputStream; + import java.io.DataInputStream; + import java.io.IOException; + import java.util.ArrayList; + import java.util.Iterator; + import java.util.Random; + import java.util.concurrent.Callable; + + // CraftBukkit start + import java.io.UnsupportedEncodingException; + import java.util.concurrent.ExecutionException; + import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + import java.util.logging.Level; + import java.util.HashSet; + + import org.bukkit.Bukkit; + import org.bukkit.Location; + import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.craftbukkit.inventory.CraftInventoryView; + import org.bukkit.craftbukkit.inventory.CraftItemStack; + import org.bukkit.craftbukkit.util.LazyPlayerSet; + import org.bukkit.craftbukkit.util.Waitable; + import org.bukkit.craftbukkit.entity.CraftPlayer; + import org.bukkit.craftbukkit.event.CraftEventFactory; + import org.bukkit.entity.Player; + import org.bukkit.event.Event; + import org.bukkit.event.block.Action; + import org.bukkit.event.block.SignChangeEvent; + import org.bukkit.event.player.AsyncPlayerChatEvent; + import org.bukkit.event.player.PlayerAnimationEvent; + import org.bukkit.event.player.PlayerChatEvent; + import org.bukkit.event.player.PlayerCommandPreprocessEvent; + import org.bukkit.event.player.PlayerInteractEntityEvent; + import org.bukkit.event.player.PlayerItemHeldEvent; + import org.bukkit.event.player.PlayerKickEvent; + import org.bukkit.event.player.PlayerMoveEvent; + import org.bukkit.event.player.PlayerTeleportEvent; + import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + import org.bukkit.event.player.PlayerToggleSneakEvent; + import org.bukkit.event.player.PlayerToggleSprintEvent; + import org.bukkit.event.inventory.*; + import org.bukkit.event.inventory.InventoryType.SlotType; + import org.bukkit.event.player.PlayerPortalEvent; + import org.bukkit.event.player.PlayerToggleFlightEvent; + import org.bukkit.inventory.CraftingInventory; + import org.bukkit.inventory.InventoryView; + // CraftBukkit end + ``` + +* Do not remove unused imports if they are not marked by CraftBukkit comments. + +### Commit Message Example +> Provide an example commit for CONTRIBUTING.md. Fixes BUKKIT-1 +> +> The CONTRIBUTING.md is missing an example commit message. Without this +> commit, we are unable to provide potential contributors with a helpful example, +> forcing developers to guess at what an acceptable commit message would look +> like. This commit fixes this issue by providing a clear and informative example +> for contributors to base their work off of. + +### Commit Message Expectations +The first line in a commit message is an imperative statement briefly explaining what the commit is achieving with an associated ticket number from our JIRA, in the form of BUKKIT-#. See the list of acceptable keywords to reference tickets with for more information on this. + +The body of the commit message needs to describe how the code behaves without this change, why this is a problem and how this commit addresses it. The body of the commit message should be restricted by a 78 character, plus newline, limit per line (meaning: once you hit about 78 characters, you should explicitly start a new line in the commit message). + +Acceptable keywords to reference tickets with: + +* **Fixes** BUKKIT-1 - this commit fixes the bug detailed in BUKKIT-1 +* **Adds** BUKKIT-2 - this commit adds the new feature requested by BUKKIT-2 + +You can reference multiple tickets in a single commit message, for example: "Fixes BUKKIT-1, BUKKIT-2" or "Adds BUKKIT-1, BUKKIT-2" without closing punctuation. + +## Submitting the Changes + +* Push your changes to a topic branch in your fork of the repository. +* Submit a pull request to the relevant repository in the Bukkit organization. + * Make sure your pull request meets [our expectations](#pull-request-formatting-expectations) before submitting. + * No merges should be included in any pull requests. +* Update your JIRA ticket to reflect that you have submitted a pull request and are ready for it to be reviewed. + * Include a link to the pull request in the ticket. +* Follow our [Tips to Get Your Pull Request Accepted](#tips-to-get-your-pull-request-accepted). +* **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +### Pull Request Formatting Expectations +#### Title +> [PR Type] Brief summary. Fixes BUKKIT-#### +> PR Type can be B for Bukkit, C for CraftBukkit, B+C for a PR in both sides +> +> Title Example: +> [B+C] Provide an example commit for CONTRIBUTING.md. Fixes BUKKIT-1 + +#### Description: +> ##### The Issue: +> Paragraphs explaining what the issue the PR is meant to be addressing. +> +> ##### Justification for this PR: +> Paragraphs providing justification for the PR +> +> ##### PR Breakdown: +> Paragraphs breaking down what the PR is doing, in detail. +> +> ##### Testing Results and Materials: +> Paragraphs describing what you did to test the code in this PR and links to pre-compiled test binaries and source. +> +> ##### Relevant PR(s): +> This should be links to accompanying PRs, or alternate PRs that attempted to perform the task. Each reference should have a reason attached as to why it is being referenced (for example: "Similar to PR ### but won't empty your Bukkits"). Accompanying PRs need no explanation, but still need to be linked. +> +> B-#### - https://github.com/Bukkit/Bukkit/pull/#### - Reason +> CB-#### - https://github.com/Bukkit/CraftBukkit/pull/#### - Reason +> +> ##### JIRA Ticket: +> BUKKIT-#### - https://bukkit.atlassian.net/browse/BUKKIT-#### +> +> ##### Pull Request Check List (For Your Use): +> +>**General:** +> +>- [ ] Fits Bukkit's Goals +>- [ ] Leaky Ticket Ready +>- [ ] Code Meets Requirements +>- [ ] Code is Documented +>- [ ] Code Addresses Leaky Ticket +>- [ ] Followed Pull Request Format +>- [ ] Tested Code +>- [ ] Included Test Material and Source +> +>**If Applicable:** +> +>- [ ] Importing New Minecraft Classes In Special Commit +>- [ ] Follows Minimal Diff Policy +>- [ ] Uses Proper CraftBukkit Comments +>- [ ] Imports Are Ordered, Separated and Organised Properly + +### Tips to Get Your Pull Request Accepted +Making sure you follow the above conventions is important, but just the beginning. Follow these tips to better the chances of your pull request being accepted and pulled. + +* Your change should [fit with Bukkit's goals](#does-the-change-fit-bukkits-goals). +* Make sure you follow all of our conventions to the letter. +* Make sure your code compiles under Java 6. +* Check for misplaced whitespaces. It may be invisible, but [we notice](https://github.com/Bukkit/CraftBukkit/pull/1070). +* Provide proper JavaDocs where appropriate. + * JavaDocs should detail every limitation, caveat and gotcha the code has. +* Provide proper accompanying documentation where appropriate. +* Test your code and provide testing material. + * For example: adding an event? Test it with a test plugin and provide us with that plugin and its source. +* Make sure to follow coding best practises. +* Your pull request should adhere to our [Pull Request Formatting Expectations](#pull-request-formatting-expectations). +* **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +## Useful Resources +* [An example pull request demonstrating the things we look out for](https://github.com/Bukkit/CraftBukkit/pull/1070) +* [Handy gist version of our Pull Request Format Template](https://gist.github.com/EvilSeph/35bb477eaa1dffc5f1d7) +* [More information on contributing](http://wiki.bukkit.org/Getting_Involved) +* [Leaky, Our Issue Tracker (JIRA)](http://leaky.bukkit.org) +* [General GitHub documentation](http://help.github.com/) +* [GitHub pull request documentation](http://help.github.com/send-pull-requests/) +* [Join us on IRC - #bukkitdev @ irc.esper.net](http://wiki.bukkit.org/IRC) diff --git a/vspigot-api/LICENCE.txt b/vspigot-api/LICENCE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/vspigot-api/LICENCE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/vspigot-api/README.md b/vspigot-api/README.md new file mode 100644 index 0000000..1905b8d --- /dev/null +++ b/vspigot-api/README.md @@ -0,0 +1,16 @@ +Spigot-API +====== + +A Minecraft Server API. + +Website: [http://spigotmc.org](http://spigotmc.org) +Bugs/Suggestions: [http://www.spigotmc.org/forums/bugs-feature-requests.8/](http://www.spigotmc.org/forums/bugs-feature-requests.8/) +Contributing Guidelines: [CONTRIBUTING.md](https://github.com/SpigotMC/Spigot-API/blob/master/CONTRIBUTING.md) + +Compilation +----------- + +We use maven to handle our dependencies. + +* Install [Maven 3](http://maven.apache.org/download.html) +* Check out this repo and: `mvn clean install` diff --git a/vspigot-api/pom.xml b/vspigot-api/pom.xml new file mode 100644 index 0000000..14e4dfc --- /dev/null +++ b/vspigot-api/pom.xml @@ -0,0 +1,126 @@ + + 4.0.0 + vspigot-api + 1.7.10-R0.1-SNAPSHOT + vspigot-api + https://valorhcf.net + + + UTF-8 + + + + net.valorhcf + vspigot-parent + dev-SNAPSHOT + ../pom.xml + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.0 + + + package + + shade + + + + + + + + + + net.md-5 + bungeecord-chat + 1.8-SNAPSHOT + jar + compile + + + org.yaml + snakeyaml + 1.12 + jar + compile + + + + com.googlecode.json-simple + json-simple + 1.1.1 + jar + compile + + + com.google.code.gson + gson + + + net.sf.trove4j + trove4j + + + + + org.avaje + ebean + 2.8.1 + jar + compile + + + com.google.code.gson + gson + 2.8.0 + compile + + + com.google.guava + guava + 10.0.1 + jar + compile + + + commons-lang + commons-lang + 2.6 + + + org.projectlombok + lombok + 1.16.10 + compile + + + + + junit + junit + 4.11 + test + + + org.hamcrest + hamcrest-library + 1.3 + test + + + diff --git a/vspigot-api/src/main/java/net/valorhcf/ChunkSnapshot.java b/vspigot-api/src/main/java/net/valorhcf/ChunkSnapshot.java new file mode 100644 index 0000000..a15dabd --- /dev/null +++ b/vspigot-api/src/main/java/net/valorhcf/ChunkSnapshot.java @@ -0,0 +1,5 @@ +package net.valorhcf; + +public interface ChunkSnapshot { + +} diff --git a/vspigot-api/src/main/java/net/valorhcf/knockback/Knockback.java b/vspigot-api/src/main/java/net/valorhcf/knockback/Knockback.java new file mode 100644 index 0000000..4a872de --- /dev/null +++ b/vspigot-api/src/main/java/net/valorhcf/knockback/Knockback.java @@ -0,0 +1,31 @@ +package net.valorhcf.knockback; + +public interface Knockback { + + String getName(); + + double getFriction(); + + void setFriction(double friction); + + double getHorizontal(); + + void setHorizontal(double horizontal); + + double getVertical(); + + void setVertical(double vertical); + + double getVerticalLimit(); + + void setVerticalLimit(double verticalLimit); + + double getExtraHorizontal(); + + void setExtraHorizontal(double extraHorizontal); + + double getExtraVertical(); + + void setExtraVertical(double extraVertical); + +} diff --git a/vspigot-api/src/main/java/net/valorhcf/utils/DateUtil.java b/vspigot-api/src/main/java/net/valorhcf/utils/DateUtil.java new file mode 100644 index 0000000..0408440 --- /dev/null +++ b/vspigot-api/src/main/java/net/valorhcf/utils/DateUtil.java @@ -0,0 +1,68 @@ +package net.valorhcf.utils; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +public class DateUtil { + private static final int MAX_YEARS = 100000; + + private DateUtil() { + } + + private static int dateDiff(int type, Calendar fromDate, Calendar toDate, boolean future) { + int year = Calendar.YEAR; + + int fromYear = fromDate.get(year); + int toYear = toDate.get(year); + if (Math.abs(fromYear - toYear) > DateUtil.MAX_YEARS) { + toDate.set(year, fromYear + + (future ? DateUtil.MAX_YEARS : -DateUtil.MAX_YEARS)); + } + + int diff = 0; + long savedDate = fromDate.getTimeInMillis(); + while ((future && !fromDate.after(toDate)) || (!future && !fromDate.before(toDate))) { + savedDate = fromDate.getTimeInMillis(); + fromDate.add(type, future ? 1 : -1); + diff++; + } + diff--; + fromDate.setTimeInMillis(savedDate); + return diff; + } + + public static String formatDateDiff(long date) { + Calendar c = new GregorianCalendar(); + c.setTimeInMillis(date); + Calendar now = new GregorianCalendar(); + return DateUtil.formatDateDiff(now, c); + } + + private static String formatDateDiff(Calendar fromDate, Calendar toDate) { + boolean future = false; + if (toDate.equals(fromDate)) { + return "now"; + } + if (toDate.after(fromDate)) { + future = true; + } + StringBuilder sb = new StringBuilder(); + int[] types = new int[]{Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND}; + String[] names = new String[]{"year", "years", "month", "months", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds"}; + int accuracy = 0; + for (int i = 0; i < types.length; i++) { + if (accuracy > 2) { + break; + } + int diff = dateDiff(types[i], fromDate, toDate, future); + if (diff > 0) { + accuracy++; + sb.append(" ").append(diff).append(" ").append(names[i * 2 + (diff > 1 ? 1 : 0)]); + } + } + if (sb.length() == 0) { + return "now"; + } + return sb.toString().trim(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Achievement.java b/vspigot-api/src/main/java/org/bukkit/Achievement.java new file mode 100644 index 0000000..928b6d5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Achievement.java @@ -0,0 +1,69 @@ +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), + ; + + 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; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Art.java b/vspigot-api/src/main/java/org/bukkit/Art.java new file mode 100644 index 0000000..ba66f16 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Art.java @@ -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 BY_NAME = Maps.newHashMap(); + private static final HashMap 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 + *

+ * 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/BanEntry.java b/vspigot-api/src/main/java/org/bukkit/BanEntry.java new file mode 100644 index 0000000..986120e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/BanEntry.java @@ -0,0 +1,127 @@ +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. + *

+ * Ban entries include the following properties: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
PropertyDescription
Target Name / IP AddressThe target name or IP address
Creation DateThe creation date of the ban
SourceThe source of the ban, such as a player, console, plugin, etc
Expiration DateThe expiration date of the ban
ReasonThe reason for the ban
+ *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * Saving the ban entry of an unbanned player will cause the player to be + * banned once again. + */ + public void save(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/BanList.java b/vspigot-api/src/main/java/org/bukkit/BanList.java new file mode 100644 index 0000000..c21b858 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/BanList.java @@ -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 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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/BlockChangeDelegate.java b/vspigot-api/src/main/java/org/bukkit/BlockChangeDelegate.java new file mode 100644 index 0000000..e6b9f0e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/BlockChangeDelegate.java @@ -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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/Bukkit.java b/vspigot-api/src/main/java/org/bukkit/Bukkit.java new file mode 100644 index 0000000..5336f37 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Bukkit.java @@ -0,0 +1,806 @@ +package org.bukkit; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Warning.WarningState; +import org.bukkit.command.CommandException; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.help.HelpMap; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.Recipe; +import org.bukkit.map.MapView; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.ServicesManager; +import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scoreboard.ScoreboardManager; +import org.bukkit.util.CachedServerIcon; + +import com.avaje.ebean.config.ServerConfig; + +/** + * Represents the Bukkit core, for version and Server singleton handling + */ +public final class Bukkit { + private static Server server; + + /** + * Static class cannot be initialized. + */ + private Bukkit() {} + + /** + * Gets the current {@link Server} singleton + * + * @return Server instance being ran + */ + public static Server getServer() { + return server; + } + + /** + * Attempts to set the {@link Server} singleton. + *

+ * This cannot be done if the Server is already set. + * + * @param server Server instance + */ + public static void setServer(Server server) { + if (Bukkit.server != null) { + throw new UnsupportedOperationException("Cannot redefine singleton Server"); + } + + Bukkit.server = server; + server.getLogger().info("This server is running " + getName() + " version " + getVersion() + " (Implementing API version " + getBukkitVersion() + ")"); + } + + /** + * @see Server#getName() + */ + public static String getName() { + return server.getName(); + } + + /** + * @see Server#getVersion() + */ + public static String getVersion() { + return server.getVersion(); + } + + /** + * @see Server#getBukkitVersion() + */ + public static String getBukkitVersion() { + return server.getBukkitVersion(); + } + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + * + * @Deprecated + * @see Server#_INVALID_getOnlinePlayers() + */ + @Deprecated + public static Player[] _INVALID_getOnlinePlayers() { + return server._INVALID_getOnlinePlayers(); + } + + /** + * @see Server#getOnlinePlayers() + */ + public static Collection getOnlinePlayers() { + return server.getOnlinePlayers(); + } + + /** + * @see Server#getMaxPlayers() + */ + public static int getMaxPlayers() { + return server.getMaxPlayers(); + } + + /** + * @see Server#getPort() + */ + public static int getPort() { + return server.getPort(); + } + + /** + * @see Server#getViewDistance() + */ + public static int getViewDistance() { + return server.getViewDistance(); + } + + /** + * @see Server#getIp() + */ + public static String getIp() { + return server.getIp(); + } + + /** + * @see Server#getServerName() + */ + public static String getServerName() { + return server.getServerName(); + } + + /** + * @see Server#getServerGroup() + */ + public static String getServerGroup() { + return server.getServerGroup(); + } + + /** + * @see Server#getServerId() + */ + public static String getServerId() { + return server.getServerId(); + } + + /** + * @see Server#getWorldType() + */ + public static String getWorldType() { + return server.getWorldType(); + } + + /** + * @see Server#getGenerateStructures() + */ + public static boolean getGenerateStructures() { + return server.getGenerateStructures(); + } + + /** + * @see Server#getAllowNether() + */ + public static boolean getAllowNether() { + return server.getAllowNether(); + } + + /** + * @see Server#hasWhitelist() + */ + public static boolean hasWhitelist() { + return server.hasWhitelist(); + } + + /** + * @see Server#broadcastMessage(String message) + */ + public static int broadcastMessage(String message) { + return server.broadcastMessage(message); + } + + /** + * @see Server#broadcastTranslate(String message) + */ + public static int broadcastTranslate(String message) { + return server.broadcastMessage(ChatColor.translate(message)); + } + + /** + * @see Server#getUpdateFolder() + */ + public static String getUpdateFolder() { + return server.getUpdateFolder(); + } + + /** + * @see Server#getPlayer(String name) + */ + public static Player getPlayer(String name) { + return server.getPlayer(name); + } + + /** + * @see Server#matchPlayer(String name) + */ + public static List matchPlayer(String name) { + return server.matchPlayer(name); + } + + /** + * @see Server#getPlayer(java.util.UUID) + */ + public static Player getPlayer(UUID id) { + return server.getPlayer(id); + } + + /** + * @see Server#getPluginManager() + */ + public static PluginManager getPluginManager() { + return server.getPluginManager(); + } + + /** + * @see Server#getScheduler() + */ + public static BukkitScheduler getScheduler() { + return server.getScheduler(); + } + + /** + * @see Server#getServicesManager() + */ + public static ServicesManager getServicesManager() { + return server.getServicesManager(); + } + + /** + * @see Server#getWorlds() + */ + public static List getWorlds() { + return server.getWorlds(); + } + + /** + * @see Server#createWorld(WorldCreator options) + */ + public static World createWorld(WorldCreator options) { + return server.createWorld(options); + } + + /** + * @see Server#unloadWorld(String name, boolean save) + */ + public static boolean unloadWorld(String name, boolean save) { + return server.unloadWorld(name, save); + } + + /** + * @see Server#unloadWorld(World world, boolean save) + */ + public static boolean unloadWorld(World world, boolean save) { + return server.unloadWorld(world, save); + } + + /** + * @see Server#getWorld(String name) + */ + public static World getWorld(String name) { + return server.getWorld(name); + } + + /** + * @see Server#getWorld(UUID uid) + */ + public static World getWorld(UUID uid) { + return server.getWorld(uid); + } + + /** + * @see Server#getMap(short id) + * @deprecated Magic value + */ + @Deprecated + public static MapView getMap(short id) { + return server.getMap(id); + } + + /** + * @see Server#createMap(World world) + */ + public static MapView createMap(World world) { + return server.createMap(world); + } + + /** + * @see Server#reload() + */ + public static void reload() { + server.reload(); + org.spigotmc.CustomTimingsHandler.reload(); // Spigot + } + + /** + * @see Server#getLogger() + */ + public static Logger getLogger() { + return server.getLogger(); + } + + /** + * @see Server#getPluginCommand(String name) + */ + public static PluginCommand getPluginCommand(String name) { + return server.getPluginCommand(name); + } + + /** + * @see Server#savePlayers() + */ + public static void savePlayers() { + server.savePlayers(); + } + + /** + * @see Server#dispatchCommand(CommandSender sender, String commandLine) + */ + public static boolean dispatchCommand(CommandSender sender, String commandLine) throws CommandException { + return server.dispatchCommand(sender, commandLine); + } + + /** + * @see Server#configureDbConfig(ServerConfig config) + */ + public static void configureDbConfig(ServerConfig config) { + server.configureDbConfig(config); + } + + /** + * @see Server#addRecipe(Recipe recipe) + */ + public static boolean addRecipe(Recipe recipe) { + return server.addRecipe(recipe); + } + + /** + * @see Server#getRecipesFor(ItemStack result) + */ + public static List getRecipesFor(ItemStack result) { + return server.getRecipesFor(result); + } + + /** + * @see Server#recipeIterator() + */ + public static Iterator recipeIterator() { + return server.recipeIterator(); + } + + /** + * @see Server#clearRecipes() + */ + public static void clearRecipes() { + server.clearRecipes(); + } + + /** + * @see Server#resetRecipes() + */ + public static void resetRecipes() { + server.resetRecipes(); + } + + /** + * @see Server#getCommandAliases() + */ + public static Map getCommandAliases() { + return server.getCommandAliases(); + } + + /** + * @see Server#getSpawnRadius() + */ + public static int getSpawnRadius() { + return server.getSpawnRadius(); + } + + /** + * @see Server#setSpawnRadius(int value) + */ + public static void setSpawnRadius(int value) { + server.setSpawnRadius(value); + } + + /** + * @see Server#getOnlineMode() + */ + public static boolean getOnlineMode() { + return server.getOnlineMode(); + } + + /** + * @see Server#getAllowFlight() + */ + public static boolean getAllowFlight() { + return server.getAllowFlight(); + } + + /** + * @see Server#isHardcore() + */ + public static boolean isHardcore() { + return server.isHardcore(); + } + + /** + * @see Server#shutdown() + */ + public static void shutdown() { + server.shutdown(); + } + + /** + * @see Server#broadcast(String message, String permission) + */ + public static int broadcast(String message, String permission) { + return server.broadcast(message, permission); + } + + /** + * @see Server#getOfflinePlayer(String name) + */ + @Deprecated + public static OfflinePlayer getOfflinePlayer(String name) { + return server.getOfflinePlayer(name); + } + + /** + * @see Server#getOfflinePlayer(java.util.UUID) + */ + public static OfflinePlayer getOfflinePlayer(UUID id) { + return server.getOfflinePlayer(id); + } + + /** + * @see Server#getPlayerExact(String name) + */ + public static Player getPlayerExact(String name) { + return server.getPlayerExact(name); + } + + /** + * @see Server#getIPBans() + */ + public static Set getIPBans() { + return server.getIPBans(); + } + + /** + * @see Server#banIP(String address) + */ + public static void banIP(String address) { + server.banIP(address); + } + + /** + * @see Server#unbanIP(String address) + */ + public static void unbanIP(String address) { + server.unbanIP(address); + } + + /** + * @see Server#getBannedPlayers() + */ + public static Set getBannedPlayers() { + return server.getBannedPlayers(); + } + + /** + * @see Server#getBanList(BanList.Type) + */ + public static BanList getBanList(BanList.Type type){ + return server.getBanList(type); + } + + /** + * @see Server#setWhitelist(boolean value) + */ + public static void setWhitelist(boolean value) { + server.setWhitelist(value); + } + + /** + * @see Server#getWhitelistedPlayers() + */ + public static Set getWhitelistedPlayers() { + return server.getWhitelistedPlayers(); + } + + /** + * @see Server#reloadWhitelist() + */ + public static void reloadWhitelist() { + server.reloadWhitelist(); + } + + /** + * @see Server#getConsoleSender() + */ + public static ConsoleCommandSender getConsoleSender() { + return server.getConsoleSender(); + } + + /** + * @see Server#getOperators() + */ + public static Set getOperators() { + return server.getOperators(); + } + + /** + * @see Server#getWorldContainer() + */ + public static File getWorldContainer() { + return server.getWorldContainer(); + } + + /** + * @see Server#getMessenger() + */ + public static Messenger getMessenger() { + return server.getMessenger(); + } + + /** + * @see Server#getAllowEnd() + */ + public static boolean getAllowEnd() { + return server.getAllowEnd(); + } + + /** + * @see Server#getUpdateFolderFile() + */ + public static File getUpdateFolderFile() { + return server.getUpdateFolderFile(); + } + + /** + * @see Server#getConnectionThrottle() + */ + public static long getConnectionThrottle() { + return server.getConnectionThrottle(); + } + + /** + * @see Server#getTicksPerAnimalSpawns() + */ + public static int getTicksPerAnimalSpawns() { + return server.getTicksPerAnimalSpawns(); + } + + /** + * @see Server#getTicksPerMonsterSpawns() + */ + public static int getTicksPerMonsterSpawns() { + return server.getTicksPerMonsterSpawns(); + } + + /** + * @see Server#useExactLoginLocation() + */ + public static boolean useExactLoginLocation() { + return server.useExactLoginLocation(); + } + + /** + * @see Server#getDefaultGameMode() + */ + public static GameMode getDefaultGameMode() { + return server.getDefaultGameMode(); + } + + /** + * @see Server#setDefaultGameMode(GameMode mode) + */ + public static void setDefaultGameMode(GameMode mode) { + server.setDefaultGameMode(mode); + } + + /** + * @see Server#getOfflinePlayers() + */ + public static OfflinePlayer[] getOfflinePlayers() { + return server.getOfflinePlayers(); + } + + /** + * @see Server#createInventory(InventoryHolder owner, InventoryType type) + */ + public static Inventory createInventory(InventoryHolder owner, InventoryType type) { + return server.createInventory(owner, type); + } + + /** + * @see Server#createInventory(InventoryHolder owner, InventoryType type, String title) + */ + public static Inventory createInventory(InventoryHolder owner, InventoryType type, String title) { + return server.createInventory(owner, type, title); + } + + /** + * @see Server#createInventory(InventoryHolder owner, int size) + */ + public static Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException { + return server.createInventory(owner, size); + } + + /** + * @see Server#createInventory(InventoryHolder owner, int size, String + * title) + */ + public static Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException { + return server.createInventory(owner, size, title); + } + + /** + * @see Server#getHelpMap() + */ + public static HelpMap getHelpMap() { + return server.getHelpMap(); + } + + /** + * @see Server#getMonsterSpawnLimit() + */ + public static int getMonsterSpawnLimit() { + return server.getMonsterSpawnLimit(); + } + + /** + * @see Server#getAnimalSpawnLimit() + */ + public static int getAnimalSpawnLimit() { + return server.getAnimalSpawnLimit(); + } + + /** + * @see Server#getWaterAnimalSpawnLimit() + */ + public static int getWaterAnimalSpawnLimit() { + return server.getWaterAnimalSpawnLimit(); + } + + /** + * @see Server#getAmbientSpawnLimit() + */ + public static int getAmbientSpawnLimit() { + return server.getAmbientSpawnLimit(); + } + + /** + * @see Server#isPrimaryThread() + */ + public static boolean isPrimaryThread() { + return server.isPrimaryThread(); + } + + /** + * @see Server#getMotd() + */ + public static String getMotd() { + return server.getMotd(); + } + + /** + * @see Server#getShutdownMessage() + */ + public static String getShutdownMessage() { + return server.getShutdownMessage(); + } + + /** + * @see Server#getWarningState() + */ + public static WarningState getWarningState() { + return server.getWarningState(); + } + + /** + * @see Server#getItemFactory() + */ + public static ItemFactory getItemFactory() { + return server.getItemFactory(); + } + + /** + * @see Server#getScoreboardManager() + */ + public static ScoreboardManager getScoreboardManager() { + return server.getScoreboardManager(); + } + + /** + * @see Server#getServerIcon() + */ + public static CachedServerIcon getServerIcon() { + return server.getServerIcon(); + } + + /** + * @see Server#loadServerIcon(File) + */ + public static CachedServerIcon loadServerIcon(File file) throws IllegalArgumentException, Exception { + return server.loadServerIcon(file); + } + + /** + * @see Server#loadServerIcon(BufferedImage) + */ + public static CachedServerIcon loadServerIcon(BufferedImage image) throws IllegalArgumentException, Exception { + return server.loadServerIcon(image); + } + + /** + * @see Server#setIdleTimeout(int) + */ + public static void setIdleTimeout(int threshold) { + server.setIdleTimeout(threshold); + } + + /** + * @see Server#getIdleTimeout() + */ + public static int getIdleTimeout() { + return server.getIdleTimeout(); + } + + // Guardian start + /** + * @see Server#isGuardianEnabled() + */ + public static boolean isGuardianEnabled() { + return server.isGuardianEnabled(); + } + + /** + * @see Server#setGuardianEnabled(boolean) + */ + public static void setGuardianEnabled(boolean enabled) { + server.setGuardianEnabled(enabled); + } + + /** + * @see Server#shouldGuardianAct() + */ + public static boolean shouldGuardianAct() { + return server.shouldGuardianAct(); + } + // Guardian end + + // MineHQ start + /** + * @see Server#getPlayerByDisguise(String name) + */ + public static Player getPlayerByDisguise(String name) { + return server.getPlayerByDisguise(name); + } + + /** + * @see Server#getPlayerExactByDisguise(String name) + */ + public static Player getPlayerExactByDisguise(String name) { + return server.getPlayerExactByDisguise(name); + } + // MineHQ end + + /** + * @see Server#getUnsafe() + */ + @Deprecated + public static UnsafeValues getUnsafe() { + return server.getUnsafe(); + } + + public static Server.Spigot spigot() + { + return server.spigot(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/ChatColor.java b/vspigot-api/src/main/java/org/bukkit/ChatColor.java new file mode 100644 index 0000000..29cca7b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/ChatColor.java @@ -0,0 +1,264 @@ +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), + /** + * Represents dark blue + */ + DARK_BLUE('1', 0x1), + /** + * Represents dark green + */ + DARK_GREEN('2', 0x2), + /** + * Represents dark blue (aqua) + */ + DARK_AQUA('3', 0x3), + /** + * Represents dark red + */ + DARK_RED('4', 0x4), + /** + * Represents dark purple + */ + DARK_PURPLE('5', 0x5), + /** + * Represents gold + */ + GOLD('6', 0x6), + /** + * Represents gray + */ + GRAY('7', 0x7), + /** + * Represents dark gray + */ + DARK_GRAY('8', 0x8), + /** + * Represents blue + */ + BLUE('9', 0x9), + /** + * Represents green + */ + GREEN('a', 0xA), + /** + * Represents aqua + */ + AQUA('b', 0xB), + /** + * Represents red + */ + RED('c', 0xC), + /** + * Represents light purple + */ + LIGHT_PURPLE('d', 0xD), + /** + * Represents yellow + */ + YELLOW('e', 0xE), + /** + * Represents white + */ + WHITE('f', 0xF), + /** + * Represents magical characters that change around randomly + */ + MAGIC('k', 0x10, true), + /** + * Makes the text bold. + */ + BOLD('l', 0x11, true), + /** + * Makes a line appear through the text. + */ + STRIKETHROUGH('m', 0x12, true), + /** + * Makes the text appear underlined. + */ + UNDERLINE('n', 0x13, true), + /** + * Makes the text italic. + */ + ITALIC('o', 0x14, true), + /** + * Resets all previous chat colors or formats. + */ + RESET('r', 0x15); + + /** + * 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 BY_ID = Maps.newHashMap(); + private final static Map 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}); + } + + /** + * 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. + */ + public boolean isFormat() { + return isFormat; + } + + /** + * Checks if this code is a color code as opposed to a format 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: & + * @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); + } + + public static String translate(String textToTranslate) { + char[] b = textToTranslate.toCharArray(); + for (int i = 0; i < b.length - 1; i++) { + if (b[i] == '&' && "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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Chunk.java b/vspigot-api/src/main/java/org/bukkit/Chunk.java new file mode 100644 index 0000000..0f73f2f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Chunk.java @@ -0,0 +1,130 @@ +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(); + + + // MineHQ start - chunk snapshot api + net.valorhcf.ChunkSnapshot takeSnapshot(); + void restoreSnapshot(net.valorhcf.ChunkSnapshot snapshot); + // MineHQ end +} diff --git a/vspigot-api/src/main/java/org/bukkit/ChunkSnapshot.java b/vspigot-api/src/main/java/org/bukkit/ChunkSnapshot.java new file mode 100644 index 0000000..83fccc8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/ChunkSnapshot.java @@ -0,0 +1,129 @@ +package org.bukkit; + +import org.bukkit.block.Biome; + +/** + * Represents a static, thread-safe snapshot of chunk of blocks. + *

+ * 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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/CoalType.java b/vspigot-api/src/main/java/org/bukkit/CoalType.java new file mode 100644 index 0000000..4fcccd2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/CoalType.java @@ -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 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Color.java b/vspigot-api/src/main/java/org/bukkit/Color.java new file mode 100644 index 0000000..76ff651 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Color.java @@ -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 >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 >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 serialize() { + return ImmutableMap.of( + "RED", getRed(), + "BLUE", getBlue(), + "GREEN", getGreen() + ); + } + + @SuppressWarnings("javadoc") + public static Color deserialize(Map map) { + return fromRGB( + asInt("RED", map), + asInt("GREEN", map), + asInt("BLUE", map) + ); + } + + private static int asInt(String string, Map 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() + "]"; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/CropState.java b/vspigot-api/src/main/java/org/bukkit/CropState.java new file mode 100644 index 0000000..ef0faf9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/CropState.java @@ -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 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Difficulty.java b/vspigot-api/src/main/java/org/bukkit/Difficulty.java new file mode 100644 index 0000000..a8a5a78 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Difficulty.java @@ -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 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/DyeColor.java b/vspigot-api/src/main/java/org/bukkit/DyeColor.java new file mode 100644 index 0000000..214806e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/DyeColor.java @@ -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 BY_COLOR; + private final static Map 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 byColor = ImmutableMap.builder(); + ImmutableMap.Builder 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(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Effect.java b/vspigot-api/src/main/java/org/bukkit/Effect.java new file mode 100644 index 0000000..37f29e2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Effect.java @@ -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 BY_ID = Maps.newHashMap(); + private static final Map 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} +} diff --git a/vspigot-api/src/main/java/org/bukkit/EntityEffect.java b/vspigot-api/src/main/java/org/bukkit/EntityEffect.java new file mode 100644 index 0000000..ec7d1e3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/EntityEffect.java @@ -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. + *

+ * This will cause client-glitches! + */ + DEATH(3), + + /** + * The smoke when taming a wolf fails. + *

+ * Without client-mods this will be ignored if the entity is not a wolf. + */ + WOLF_SMOKE(6), + + /** + * The hearts when taming a wolf succeeds. + *

+ * Without client-mods this will be ignored if the entity is not a wolf. + */ + WOLF_HEARTS(7), + + /** + * When a wolf shakes (after being wet). + *

+ * 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. + *

+ * This will not play an effect if the entity is not an iron golem. + */ + IRON_GOLEM_ROSE(11), + + /** + * Hearts from a villager. + *

+ * This will not play an effect if the entity is not a villager. + */ + VILLAGER_HEART(12), + + /** + * When a villager is angry. + *

+ * This will not play an effect if the entity is not a villager. + */ + VILLAGER_ANGRY(13), + + /** + * Happy particles from a villager. + *

+ * This will not play an effect if the entity is not a villager. + */ + VILLAGER_HAPPY(14), + + /** + * Magic particles from a witch. + *

+ * 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. + *

+ * This will not play an effect if the entity is not a zombie. + */ + ZOMBIE_TRANSFORM(16), + + /** + * When a firework explodes. + *

+ * This will not play an effect if the entity is not a firework. + */ + FIREWORK_EXPLODE(17); + + private final byte data; + private final static Map 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/FireworkEffect.java b/vspigot-api/src/main/java/org/bukkit/FireworkEffect.java new file mode 100644 index 0000000..6f2d096 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/FireworkEffect.java @@ -0,0 +1,421 @@ +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 colors = ImmutableList.builder(); + ImmutableList.Builder 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 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 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 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 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. + *

+ * 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.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 colors; + private final ImmutableList fadeColors; + private final Type type; + private String string = null; + + FireworkEffect(boolean flicker, boolean trail, ImmutableList colors, ImmutableList 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 getColors() { + return colors; + } + + /** + * Get the fade colors of the firework effect. + * + * @return An immutable list of the fade colors + */ + public List getFadeColors() { + return fadeColors; + } + + /** + * Get the type of the firework effect. + * + * @return The effect type + */ + public Type getType() { + return type; + } + + /** + * @see ConfigurationSerializable + */ + public static ConfigurationSerializable deserialize(Map 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(); + } + + public Map serialize() { + return ImmutableMap.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); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/GameMode.java b/vspigot-api/src/main/java/org/bukkit/GameMode.java new file mode 100644 index 0000000..f85ed0b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/GameMode.java @@ -0,0 +1,66 @@ +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); + + private final int value; + private final static Map 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/GrassSpecies.java b/vspigot-api/src/main/java/org/bukkit/GrassSpecies.java new file mode 100644 index 0000000..1111515 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/GrassSpecies.java @@ -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 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Instrument.java b/vspigot-api/src/main/java/org/bukkit/Instrument.java new file mode 100644 index 0000000..891a2b1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Instrument.java @@ -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 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Location.java b/vspigot-api/src/main/java/org/bukkit/Location.java new file mode 100644 index 0000000..5c18507 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Location.java @@ -0,0 +1,560 @@ +package org.bukkit; + +import org.bukkit.block.Block; +import org.bukkit.util.NumberConversions; +import org.bukkit.util.Vector; + +/** + * Represents a 3-dimensional position in a world + */ +public class Location implements Cloneable { + 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. + *

    + *
  • A yaw of 0 or 360 represents the positive z direction. + *
  • A yaw of 180 represents the negative z direction. + *
  • A yaw of 90 represents the negative x direction. + *
  • A yaw of 270 represents the positive x direction. + *
+ * 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. + *
    + *
  • A yaw of 0 or 360 represents the positive z direction. + *
  • A yaw of 180 represents the negative z direction. + *
  • A yaw of 90 represents the negative x direction. + *
  • A yaw of 270 represents the positive x direction. + *
+ * 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. + *
    + *
  • A pitch of 0 represents level forward facing. + *
  • A pitch of 90 represents downward facing, or negative y + * direction. + *
  • A pitch of -90 represents upward facing, or positive y direction. + *
      + * 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. + *
        + *
      • A pitch of 0 represents level forward facing. + *
      • A pitch of 90 represents downward facing, or negative y + * direction. + *
      • A pitch of -90 represents upward facing, or positive y direction. + *
          + * 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. + */ + 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); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Material.java b/vspigot-api/src/main/java/org/bukkit/Material.java new file mode 100644 index 0000000..c45c180 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Material.java @@ -0,0 +1,1034 @@ +package org.bukkit; + +import java.lang.reflect.Constructor; +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.bukkit.map.MapView; +import org.bukkit.material.Bed; +import org.bukkit.material.Button; +import org.bukkit.material.Cake; +import org.bukkit.material.Cauldron; +import org.bukkit.material.Chest; +import org.bukkit.material.Coal; +import org.bukkit.material.CocoaPlant; +import org.bukkit.material.Command; +import org.bukkit.material.Crops; +import org.bukkit.material.DetectorRail; +import org.bukkit.material.Diode; +import org.bukkit.material.Dispenser; +import org.bukkit.material.Door; +import org.bukkit.material.Dye; +import org.bukkit.material.EnderChest; +import org.bukkit.material.FlowerPot; +import org.bukkit.material.Furnace; +import org.bukkit.material.Gate; +import org.bukkit.material.Ladder; +import org.bukkit.material.Lever; +import org.bukkit.material.LongGrass; +import org.bukkit.material.MaterialData; +import org.bukkit.material.MonsterEggs; +import org.bukkit.material.Mushroom; +import org.bukkit.material.NetherWarts; +import org.bukkit.material.PistonBaseMaterial; +import org.bukkit.material.PistonExtensionMaterial; +import org.bukkit.material.PoweredRail; +import org.bukkit.material.PressurePlate; +import org.bukkit.material.Pumpkin; +import org.bukkit.material.Rails; +import org.bukkit.material.RedstoneTorch; +import org.bukkit.material.RedstoneWire; +import org.bukkit.material.Sandstone; +import org.bukkit.material.Sign; +import org.bukkit.material.Skull; +import org.bukkit.material.SmoothBrick; +import org.bukkit.material.SpawnEgg; +import org.bukkit.material.Stairs; +import org.bukkit.material.Step; +import org.bukkit.material.Torch; +import org.bukkit.material.TrapDoor; +import org.bukkit.material.Tree; +import org.bukkit.material.Tripwire; +import org.bukkit.material.TripwireHook; +import org.bukkit.material.Vine; +import org.bukkit.material.WoodenStep; +import org.bukkit.material.Wool; +import org.bukkit.potion.Potion; +import org.bukkit.util.Java15Compat; + +import com.google.common.collect.Maps; + +/** + * An enum of all material IDs accepted by the official server and client + */ +public enum Material { + AIR(0, 0), + STONE(1), + GRASS(2), + DIRT(3), + COBBLESTONE(4), + WOOD(5, Tree.class), + SAPLING(6, Tree.class), + BEDROCK(7), + WATER(8, MaterialData.class), + STATIONARY_WATER(9, MaterialData.class), + LAVA(10, MaterialData.class), + STATIONARY_LAVA(11, MaterialData.class), + SAND(12), + GRAVEL(13), + GOLD_ORE(14), + IRON_ORE(15), + COAL_ORE(16), + LOG(17, Tree.class), + LEAVES(18, Tree.class), + SPONGE(19), + GLASS(20), + LAPIS_ORE(21), + LAPIS_BLOCK(22), + DISPENSER(23, Dispenser.class), + SANDSTONE(24, Sandstone.class), + NOTE_BLOCK(25), + BED_BLOCK(26, Bed.class), + POWERED_RAIL(27, PoweredRail.class), + DETECTOR_RAIL(28, DetectorRail.class), + PISTON_STICKY_BASE(29, PistonBaseMaterial.class), + WEB(30), + LONG_GRASS(31, LongGrass.class), + DEAD_BUSH(32), + PISTON_BASE(33, PistonBaseMaterial.class), + PISTON_EXTENSION(34, PistonExtensionMaterial.class), + WOOL(35, Wool.class), + PISTON_MOVING_PIECE(36), + YELLOW_FLOWER(37), + RED_ROSE(38), + BROWN_MUSHROOM(39), + RED_MUSHROOM(40), + GOLD_BLOCK(41), + IRON_BLOCK(42), + DOUBLE_STEP(43, Step.class), + STEP(44, Step.class), + BRICK(45), + TNT(46), + BOOKSHELF(47), + MOSSY_COBBLESTONE(48), + OBSIDIAN(49), + TORCH(50, Torch.class), + FIRE(51), + MOB_SPAWNER(52), + WOOD_STAIRS(53, Stairs.class), + CHEST(54, Chest.class), + REDSTONE_WIRE(55, RedstoneWire.class), + DIAMOND_ORE(56), + DIAMOND_BLOCK(57), + WORKBENCH(58), + CROPS(59, Crops.class), + SOIL(60, MaterialData.class), + FURNACE(61, Furnace.class), + BURNING_FURNACE(62, Furnace.class), + SIGN_POST(63, 64, Sign.class), + WOODEN_DOOR(64, Door.class), + LADDER(65, Ladder.class), + RAILS(66, Rails.class), + COBBLESTONE_STAIRS(67, Stairs.class), + WALL_SIGN(68, 64, Sign.class), + LEVER(69, Lever.class), + STONE_PLATE(70, PressurePlate.class), + IRON_DOOR_BLOCK(71, Door.class), + WOOD_PLATE(72, PressurePlate.class), + REDSTONE_ORE(73), + GLOWING_REDSTONE_ORE(74), + REDSTONE_TORCH_OFF(75, RedstoneTorch.class), + REDSTONE_TORCH_ON(76, RedstoneTorch.class), + STONE_BUTTON(77, Button.class), + SNOW(78), + ICE(79), + SNOW_BLOCK(80), + CACTUS(81, MaterialData.class), + CLAY(82), + SUGAR_CANE_BLOCK(83, MaterialData.class), + JUKEBOX(84), + FENCE(85), + PUMPKIN(86, Pumpkin.class), + NETHERRACK(87), + SOUL_SAND(88), + GLOWSTONE(89), + PORTAL(90), + JACK_O_LANTERN(91, Pumpkin.class), + CAKE_BLOCK(92, 64, Cake.class), + DIODE_BLOCK_OFF(93, Diode.class), + DIODE_BLOCK_ON(94, Diode.class), + @Deprecated + LOCKED_CHEST(95), + STAINED_GLASS(95), + TRAP_DOOR(96, TrapDoor.class), + MONSTER_EGGS(97, MonsterEggs.class), + SMOOTH_BRICK(98, SmoothBrick.class), + HUGE_MUSHROOM_1(99, Mushroom.class), + HUGE_MUSHROOM_2(100, Mushroom.class), + IRON_FENCE(101), + THIN_GLASS(102), + MELON_BLOCK(103), + PUMPKIN_STEM(104, MaterialData.class), + MELON_STEM(105, MaterialData.class), + VINE(106, Vine.class), + FENCE_GATE(107, Gate.class), + BRICK_STAIRS(108, Stairs.class), + SMOOTH_STAIRS(109, Stairs.class), + MYCEL(110), + WATER_LILY(111), + NETHER_BRICK(112), + NETHER_FENCE(113), + NETHER_BRICK_STAIRS(114, Stairs.class), + NETHER_WARTS(115, NetherWarts.class), + ENCHANTMENT_TABLE(116), + BREWING_STAND(117, MaterialData.class), + CAULDRON(118, Cauldron.class), + ENDER_PORTAL(119), + ENDER_PORTAL_FRAME(120), + ENDER_STONE(121), + DRAGON_EGG(122), + REDSTONE_LAMP_OFF(123), + REDSTONE_LAMP_ON(124), + WOOD_DOUBLE_STEP(125, WoodenStep.class), + WOOD_STEP(126, WoodenStep.class), + COCOA(127, CocoaPlant.class), + SANDSTONE_STAIRS(128, Stairs.class), + EMERALD_ORE(129), + ENDER_CHEST(130, EnderChest.class), + TRIPWIRE_HOOK(131, TripwireHook.class), + TRIPWIRE(132, Tripwire.class), + EMERALD_BLOCK(133), + SPRUCE_WOOD_STAIRS(134, Stairs.class), + BIRCH_WOOD_STAIRS(135, Stairs.class), + JUNGLE_WOOD_STAIRS(136, Stairs.class), + COMMAND(137, Command.class), + BEACON(138), + COBBLE_WALL(139), + FLOWER_POT(140, FlowerPot.class), + CARROT(141), + POTATO(142), + WOOD_BUTTON(143, Button.class), + SKULL(144, Skull.class), + ANVIL(145), + TRAPPED_CHEST(146), + GOLD_PLATE(147), + IRON_PLATE(148), + REDSTONE_COMPARATOR_OFF(149), + REDSTONE_COMPARATOR_ON(150), + DAYLIGHT_DETECTOR(151), + REDSTONE_BLOCK(152), + QUARTZ_ORE(153), + HOPPER(154), + QUARTZ_BLOCK(155), + QUARTZ_STAIRS(156, Stairs.class), + ACTIVATOR_RAIL(157, PoweredRail.class), + DROPPER(158, Dispenser.class), + STAINED_CLAY(159), + STAINED_GLASS_PANE(160), + LEAVES_2(161), + LOG_2(162), + ACACIA_STAIRS(163, Stairs.class), + DARK_OAK_STAIRS(164, Stairs.class), + HAY_BLOCK(170), + CARPET(171), + HARD_CLAY(172), + COAL_BLOCK(173), + PACKED_ICE(174), + DOUBLE_PLANT(175), + // ----- Item Separator ----- + IRON_SPADE(256, 1, 250), + IRON_PICKAXE(257, 1, 250), + IRON_AXE(258, 1, 250), + FLINT_AND_STEEL(259, 1, 64), + APPLE(260), + BOW(261, 1, 384), + ARROW(262), + COAL(263, Coal.class), + DIAMOND(264), + IRON_INGOT(265), + GOLD_INGOT(266), + IRON_SWORD(267, 1, 250), + WOOD_SWORD(268, 1, 59), + WOOD_SPADE(269, 1, 59), + WOOD_PICKAXE(270, 1, 59), + WOOD_AXE(271, 1, 59), + STONE_SWORD(272, 1, 131), + STONE_SPADE(273, 1, 131), + STONE_PICKAXE(274, 1, 131), + STONE_AXE(275, 1, 131), + DIAMOND_SWORD(276, 1, 1561), + DIAMOND_SPADE(277, 1, 1561), + DIAMOND_PICKAXE(278, 1, 1561), + DIAMOND_AXE(279, 1, 1561), + STICK(280), + BOWL(281), + MUSHROOM_SOUP(282, 1), + GOLD_SWORD(283, 1, 32), + GOLD_SPADE(284, 1, 32), + GOLD_PICKAXE(285, 1, 32), + GOLD_AXE(286, 1, 32), + STRING(287), + FEATHER(288), + SULPHUR(289), + WOOD_HOE(290, 1, 59), + STONE_HOE(291, 1, 131), + IRON_HOE(292, 1, 250), + DIAMOND_HOE(293, 1, 1561), + GOLD_HOE(294, 1, 32), + SEEDS(295), + WHEAT(296), + BREAD(297), + LEATHER_HELMET(298, 1, 55), + LEATHER_CHESTPLATE(299, 1, 80), + LEATHER_LEGGINGS(300, 1, 75), + LEATHER_BOOTS(301, 1, 65), + CHAINMAIL_HELMET(302, 1, 165), + CHAINMAIL_CHESTPLATE(303, 1, 240), + CHAINMAIL_LEGGINGS(304, 1, 225), + CHAINMAIL_BOOTS(305, 1, 195), + IRON_HELMET(306, 1, 165), + IRON_CHESTPLATE(307, 1, 240), + IRON_LEGGINGS(308, 1, 225), + IRON_BOOTS(309, 1, 195), + DIAMOND_HELMET(310, 1, 363), + DIAMOND_CHESTPLATE(311, 1, 528), + DIAMOND_LEGGINGS(312, 1, 495), + DIAMOND_BOOTS(313, 1, 429), + GOLD_HELMET(314, 1, 77), + GOLD_CHESTPLATE(315, 1, 112), + GOLD_LEGGINGS(316, 1, 105), + GOLD_BOOTS(317, 1, 91), + FLINT(318), + PORK(319), + GRILLED_PORK(320), + PAINTING(321), + GOLDEN_APPLE(322), + SIGN(323, 16), + WOOD_DOOR(324, 1), + BUCKET(325, 16), + WATER_BUCKET(326, 1), + LAVA_BUCKET(327, 1), + MINECART(328, 1), + SADDLE(329, 1), + IRON_DOOR(330, 1), + REDSTONE(331), + SNOW_BALL(332, 16), + BOAT(333, 1), + LEATHER(334), + MILK_BUCKET(335, 1), + CLAY_BRICK(336), + CLAY_BALL(337), + SUGAR_CANE(338), + PAPER(339), + BOOK(340), + SLIME_BALL(341), + STORAGE_MINECART(342, 1), + POWERED_MINECART(343, 1), + EGG(344, 16), + COMPASS(345), + FISHING_ROD(346, 1, 64), + WATCH(347), + GLOWSTONE_DUST(348), + RAW_FISH(349), + COOKED_FISH(350), + INK_SACK(351, Dye.class), + BONE(352), + SUGAR(353), + CAKE(354, 1), + BED(355, 1), + DIODE(356), + COOKIE(357), + /** + * @see MapView + */ + MAP(358, MaterialData.class), + SHEARS(359, 1, 238), + MELON(360), + PUMPKIN_SEEDS(361), + MELON_SEEDS(362), + RAW_BEEF(363), + COOKED_BEEF(364), + RAW_CHICKEN(365), + COOKED_CHICKEN(366), + ROTTEN_FLESH(367), + ENDER_PEARL(368, 16), + BLAZE_ROD(369), + GHAST_TEAR(370), + GOLD_NUGGET(371), + NETHER_STALK(372), + /** + * @see Potion + */ + POTION(373, 1, MaterialData.class), + GLASS_BOTTLE(374), + SPIDER_EYE(375), + FERMENTED_SPIDER_EYE(376), + BLAZE_POWDER(377), + MAGMA_CREAM(378), + BREWING_STAND_ITEM(379), + CAULDRON_ITEM(380), + EYE_OF_ENDER(381), + SPECKLED_MELON(382), + MONSTER_EGG(383, 64, SpawnEgg.class), + EXP_BOTTLE(384, 64), + FIREBALL(385, 64), + BOOK_AND_QUILL(386, 1), + WRITTEN_BOOK(387, 16), + EMERALD(388, 64), + ITEM_FRAME(389), + FLOWER_POT_ITEM(390), + CARROT_ITEM(391), + POTATO_ITEM(392), + BAKED_POTATO(393), + POISONOUS_POTATO(394), + EMPTY_MAP(395), + GOLDEN_CARROT(396), + SKULL_ITEM(397), + CARROT_STICK(398, 1, 25), + NETHER_STAR(399), + PUMPKIN_PIE(400), + FIREWORK(401), + FIREWORK_CHARGE(402), + ENCHANTED_BOOK(403, 1), + REDSTONE_COMPARATOR(404), + NETHER_BRICK_ITEM(405), + QUARTZ(406), + EXPLOSIVE_MINECART(407, 1), + HOPPER_MINECART(408, 1), + IRON_BARDING(417, 1), + GOLD_BARDING(418, 1), + DIAMOND_BARDING(419, 1), + LEASH(420), + NAME_TAG(421), + COMMAND_MINECART(422, 1), + GOLD_RECORD(2256, 1), + GREEN_RECORD(2257, 1), + RECORD_3(2258, 1), + RECORD_4(2259, 1), + RECORD_5(2260, 1), + RECORD_6(2261, 1), + RECORD_7(2262, 1), + RECORD_8(2263, 1), + RECORD_9(2264, 1), + RECORD_10(2265, 1), + RECORD_11(2266, 1), + RECORD_12(2267, 1), + ; + + private final int id; + private final Constructor ctor; + private static Material[] byId = new Material[383]; + private final static Map BY_NAME = Maps.newHashMap(); + private final int maxStack; + private final short durability; + + private Material(final int id) { + this(id, 64); + } + + private Material(final int id, final int stack) { + this(id, stack, MaterialData.class); + } + + private Material(final int id, final int stack, final int durability) { + this(id, stack, durability, MaterialData.class); + } + + private Material(final int id, final Class data) { + this(id, 64, data); + } + + private Material(final int id, final int stack, final Class data) { + this(id, stack, 0, data); + } + + private Material(final int id, final int stack, final int durability, final Class data) { + this.id = id; + this.durability = (short) durability; + this.maxStack = stack; + // try to cache the constructor for this material + try { + this.ctor = data.getConstructor(int.class, byte.class); + } catch (NoSuchMethodException ex) { + throw new AssertionError(ex); + } catch (SecurityException ex) { + throw new AssertionError(ex); + } + } + + /** + * Gets the item ID or block ID of this Material + * + * @return ID of this material + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + + /** + * Gets the maximum amount of this material that can be held in a stack + * + * @return Maximum stack size for this material + */ + public int getMaxStackSize() { + return maxStack; + } + + /** + * Gets the maximum durability of this material + * + * @return Maximum durability for this material + */ + public short getMaxDurability() { + return durability; + } + + /** + * Gets the MaterialData class associated with this Material + * + * @return MaterialData associated with this Material + */ + public Class getData() { + return ctor.getDeclaringClass(); + } + + /** + * Constructs a new MaterialData relevant for this Material, with the + * given initial data + * + * @param raw Initial data to construct the MaterialData with + * @return New MaterialData with the given data + * @deprecated Magic value + */ + @Deprecated + public MaterialData getNewData(final byte raw) { + try { + return ctor.newInstance(id, raw); + } catch (InstantiationException ex) { + final Throwable t = ex.getCause(); + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + if (t instanceof Error) { + throw (Error) t; + } + throw new AssertionError(t); + } catch (Throwable t) { + throw new AssertionError(t); + } + } + + /** + * Checks if this Material is a placable block + * + * @return true if this material is a block + */ + public boolean isBlock() { + return id < 256; + } + + /** + * Checks if this Material is edible. + * + * @return true if this Material is edible. + */ + public boolean isEdible() { + switch (this) { + case BREAD: + case CARROT_ITEM: + case BAKED_POTATO: + case POTATO_ITEM: + case POISONOUS_POTATO: + case GOLDEN_CARROT: + case PUMPKIN_PIE: + case COOKIE: + case MELON: + case MUSHROOM_SOUP: + case RAW_CHICKEN: + case COOKED_CHICKEN: + case RAW_BEEF: + case COOKED_BEEF: + case RAW_FISH: + case COOKED_FISH: + case PORK: + case GRILLED_PORK: + case APPLE: + case GOLDEN_APPLE: + case ROTTEN_FLESH: + case SPIDER_EYE: + return true; + default: + return false; + } + } + + /** + * Attempts to get the Material with the given ID + * + * @param id ID of the material to get + * @return Material if found, or null + * @deprecated Magic value + */ + @Deprecated + public static Material getMaterial(final int id) { + if (byId.length > id && id >= 0) { + return byId[id]; + } else { + return null; + } + } + + /** + * Attempts to get the Material with the given name. + *

          + * This is a normal lookup, names must be the precise name they are given + * in the enum. + * + * @param name Name of the material to get + * @return Material if found, or null + */ + public static Material getMaterial(final String name) { + return BY_NAME.get(name); + } + + /** + * Attempts to match the Material with the given name. + *

          + * This is a match lookup; names will be converted to uppercase, then + * stripped of special characters in an attempt to format it like the + * enum. + *

          + * Using this for match by ID is deprecated. + * + * @param name Name of the material to get + * @return Material if found, or null + */ + public static Material matchMaterial(final String name) { + Validate.notNull(name, "Name cannot be null"); + + Material result = null; + + try { + result = getMaterial(Integer.parseInt(name)); + } catch (NumberFormatException ex) {} + + if (result == null) { + String filtered = name.toUpperCase(); + + filtered = filtered.replaceAll("\\s+", "_").replaceAll("\\W", ""); + result = BY_NAME.get(filtered); + } + + return result; + } + + static { + for (Material material : values()) { + if (byId.length > material.id) { + byId[material.id] = material; + } else { + byId = Java15Compat.Arrays_copyOfRange(byId, 0, material.id + 2); + byId[material.id] = material; + } + BY_NAME.put(material.name(), material); + } + } + + /** + * @return True if this material represents a playable music disk. + */ + public boolean isRecord() { + return id >= GOLD_RECORD.id && id <= RECORD_12.id; + } + + /** + * Check if the material is a block and solid (cannot be passed through by + * a player) + * + * @return True if this material is a block and solid + */ + public boolean isSolid() { + if (!isBlock() || id == 0) { + return false; + } + switch (this) { + case STONE: + case GRASS: + case DIRT: + case COBBLESTONE: + case WOOD: + case BEDROCK: + case SAND: + case GRAVEL: + case GOLD_ORE: + case IRON_ORE: + case COAL_ORE: + case LOG: + case LEAVES: + case SPONGE: + case GLASS: + case LAPIS_ORE: + case LAPIS_BLOCK: + case DISPENSER: + case SANDSTONE: + case NOTE_BLOCK: + case BED_BLOCK: + case PISTON_STICKY_BASE: + case PISTON_BASE: + case PISTON_EXTENSION: + case WOOL: + case PISTON_MOVING_PIECE: + case GOLD_BLOCK: + case IRON_BLOCK: + case DOUBLE_STEP: + case STEP: + case BRICK: + case TNT: + case BOOKSHELF: + case MOSSY_COBBLESTONE: + case OBSIDIAN: + case MOB_SPAWNER: + case WOOD_STAIRS: + case CHEST: + case DIAMOND_ORE: + case DIAMOND_BLOCK: + case WORKBENCH: + case SOIL: + case FURNACE: + case BURNING_FURNACE: + case SIGN_POST: + case WOODEN_DOOR: + case COBBLESTONE_STAIRS: + case WALL_SIGN: + case STONE_PLATE: + case IRON_DOOR_BLOCK: + case WOOD_PLATE: + case REDSTONE_ORE: + case GLOWING_REDSTONE_ORE: + case ICE: + case SNOW_BLOCK: + case CACTUS: + case CLAY: + case JUKEBOX: + case FENCE: + case PUMPKIN: + case NETHERRACK: + case SOUL_SAND: + case GLOWSTONE: + case JACK_O_LANTERN: + case CAKE_BLOCK: + case LOCKED_CHEST: + case STAINED_GLASS: + case TRAP_DOOR: + case MONSTER_EGGS: + case SMOOTH_BRICK: + case HUGE_MUSHROOM_1: + case HUGE_MUSHROOM_2: + case IRON_FENCE: + case THIN_GLASS: + case MELON_BLOCK: + case FENCE_GATE: + case BRICK_STAIRS: + case SMOOTH_STAIRS: + case MYCEL: + case NETHER_BRICK: + case NETHER_FENCE: + case NETHER_BRICK_STAIRS: + case ENCHANTMENT_TABLE: + case BREWING_STAND: + case CAULDRON: + case ENDER_PORTAL_FRAME: + case ENDER_STONE: + case DRAGON_EGG: + case REDSTONE_LAMP_OFF: + case REDSTONE_LAMP_ON: + case WOOD_DOUBLE_STEP: + case WOOD_STEP: + case SANDSTONE_STAIRS: + case EMERALD_ORE: + case ENDER_CHEST: + case EMERALD_BLOCK: + case SPRUCE_WOOD_STAIRS: + case BIRCH_WOOD_STAIRS: + case JUNGLE_WOOD_STAIRS: + case COMMAND: + case BEACON: + case COBBLE_WALL: + case ANVIL: + case TRAPPED_CHEST: + case GOLD_PLATE: + case IRON_PLATE: + case DAYLIGHT_DETECTOR: + case REDSTONE_BLOCK: + case QUARTZ_ORE: + case HOPPER: + case QUARTZ_BLOCK: + case QUARTZ_STAIRS: + case DROPPER: + case STAINED_CLAY: + case HAY_BLOCK: + case HARD_CLAY: + case COAL_BLOCK: + case STAINED_GLASS_PANE: + case LEAVES_2: + case LOG_2: + case ACACIA_STAIRS: + case DARK_OAK_STAIRS: + case PACKED_ICE: + return true; + default: + return false; + } + } + + /** + * Check if the material is a block and does not block any light + * + * @return True if this material is a block and does not block any light + */ + public boolean isTransparent() { + if (!isBlock()) { + return false; + } + switch (this) { + case AIR: + case SAPLING: + case POWERED_RAIL: + case DETECTOR_RAIL: + case LONG_GRASS: + case DEAD_BUSH: + case YELLOW_FLOWER: + case RED_ROSE: + case BROWN_MUSHROOM: + case RED_MUSHROOM: + case TORCH: + case FIRE: + case REDSTONE_WIRE: + case CROPS: + case LADDER: + case RAILS: + case LEVER: + case REDSTONE_TORCH_OFF: + case REDSTONE_TORCH_ON: + case STONE_BUTTON: + case SNOW: + case SUGAR_CANE_BLOCK: + case PORTAL: + case DIODE_BLOCK_OFF: + case DIODE_BLOCK_ON: + case PUMPKIN_STEM: + case MELON_STEM: + case VINE: + case WATER_LILY: + case NETHER_WARTS: + case ENDER_PORTAL: + case COCOA: + case TRIPWIRE_HOOK: + case TRIPWIRE: + case FLOWER_POT: + case CARROT: + case POTATO: + case WOOD_BUTTON: + case SKULL: + case REDSTONE_COMPARATOR_OFF: + case REDSTONE_COMPARATOR_ON: + case ACTIVATOR_RAIL: + case CARPET: + case DOUBLE_PLANT: + return true; + default: + return false; + } + } + + /** + * Check if the material is a block and can catch fire + * + * @return True if this material is a block and can catch fire + */ + public boolean isFlammable() { + if (!isBlock()) { + return false; + } + switch (this) { + case WOOD: + case LOG: + case LEAVES: + case NOTE_BLOCK: + case BED_BLOCK: + case LONG_GRASS: + case DEAD_BUSH: + case WOOL: + case TNT: + case BOOKSHELF: + case WOOD_STAIRS: + case CHEST: + case WORKBENCH: + case SIGN_POST: + case WOODEN_DOOR: + case WALL_SIGN: + case WOOD_PLATE: + case JUKEBOX: + case FENCE: + case TRAP_DOOR: + case HUGE_MUSHROOM_1: + case HUGE_MUSHROOM_2: + case VINE: + case FENCE_GATE: + case WOOD_DOUBLE_STEP: + case WOOD_STEP: + case SPRUCE_WOOD_STAIRS: + case BIRCH_WOOD_STAIRS: + case JUNGLE_WOOD_STAIRS: + case TRAPPED_CHEST: + case DAYLIGHT_DETECTOR: + case CARPET: + case LEAVES_2: + case LOG_2: + case ACACIA_STAIRS: + case DARK_OAK_STAIRS: + return true; + default: + return false; + } + } + + /** + * Check if the material is a block and can burn away + * + * @return True if this material is a block and can burn away + */ + public boolean isBurnable() { + if (!isBlock()) { + return false; + } + switch (this) { + case WOOD: + case LOG: + case LEAVES: + case LONG_GRASS: + case WOOL: + case YELLOW_FLOWER: + case RED_ROSE: + case TNT: + case BOOKSHELF: + case WOOD_STAIRS: + case FENCE: + case VINE: + case WOOD_DOUBLE_STEP: + case WOOD_STEP: + case SPRUCE_WOOD_STAIRS: + case BIRCH_WOOD_STAIRS: + case JUNGLE_WOOD_STAIRS: + case HAY_BLOCK: + case COAL_BLOCK: + case LEAVES_2: + case LOG_2: + case CARPET: + case DOUBLE_PLANT: + return true; + default: + return false; + } + } + + /** + * Check if the material is a block and completely blocks vision + * + * @return True if this material is a block and completely blocks vision + */ + public boolean isOccluding() { + if (!isBlock()) { + return false; + } + switch (this) { + case STONE: + case GRASS: + case DIRT: + case COBBLESTONE: + case WOOD: + case BEDROCK: + case SAND: + case GRAVEL: + case GOLD_ORE: + case IRON_ORE: + case COAL_ORE: + case LOG: + case SPONGE: + case LAPIS_ORE: + case LAPIS_BLOCK: + case DISPENSER: + case SANDSTONE: + case NOTE_BLOCK: + case WOOL: + case GOLD_BLOCK: + case IRON_BLOCK: + case DOUBLE_STEP: + case BRICK: + case BOOKSHELF: + case MOSSY_COBBLESTONE: + case OBSIDIAN: + case MOB_SPAWNER: + case DIAMOND_ORE: + case DIAMOND_BLOCK: + case WORKBENCH: + case FURNACE: + case BURNING_FURNACE: + case REDSTONE_ORE: + case GLOWING_REDSTONE_ORE: + case SNOW_BLOCK: + case CLAY: + case JUKEBOX: + case PUMPKIN: + case NETHERRACK: + case SOUL_SAND: + case JACK_O_LANTERN: + case MONSTER_EGGS: + case SMOOTH_BRICK: + case HUGE_MUSHROOM_1: + case HUGE_MUSHROOM_2: + case MELON_BLOCK: + case MYCEL: + case NETHER_BRICK: + case ENDER_PORTAL_FRAME: + case ENDER_STONE: + case REDSTONE_LAMP_OFF: + case REDSTONE_LAMP_ON: + case WOOD_DOUBLE_STEP: + case EMERALD_ORE: + case EMERALD_BLOCK: + case COMMAND: + case QUARTZ_ORE: + case QUARTZ_BLOCK: + case DROPPER: + case STAINED_CLAY: + case HAY_BLOCK: + case HARD_CLAY: + case COAL_BLOCK: + case LOG_2: + case PACKED_ICE: + return true; + default: + return false; + } + } + + /** + * @return True if this material is affected by gravity. + */ + public boolean hasGravity() { + if (!isBlock()) { + return false; + } + switch (this) { + case SAND: + case GRAVEL: + case ANVIL: + return true; + default: + return false; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/NetherWartsState.java b/vspigot-api/src/main/java/org/bukkit/NetherWartsState.java new file mode 100644 index 0000000..f43209c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/NetherWartsState.java @@ -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; +} diff --git a/vspigot-api/src/main/java/org/bukkit/Note.java b/vspigot-api/src/main/java/org/bukkit/Note.java new file mode 100644 index 0000000..417936f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Note.java @@ -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 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; 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() ? "#" : "") + "}"; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/OfflinePlayer.java b/vspigot-api/src/main/java/org/bukkit/OfflinePlayer.java new file mode 100644 index 0000000..e98706a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/OfflinePlayer.java @@ -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 + *

          + * 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 + *

          + * 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. + *

          + * 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. + *

          + * 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(); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/PortalType.java b/vspigot-api/src/main/java/org/bukkit/PortalType.java new file mode 100644 index 0000000..427cfbb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/PortalType.java @@ -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; +} diff --git a/vspigot-api/src/main/java/org/bukkit/Rotation.java b/vspigot-api/src/main/java/org/bukkit/Rotation.java new file mode 100644 index 0000000..dfdb0e5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Rotation.java @@ -0,0 +1,47 @@ +package org.bukkit; + +/** + * An enum to specify a rotation based orientation, like that on a clock. + *

          + * It represents how something is viewed, as opposed to cardinal directions. + */ +public enum Rotation { + + /** + * No rotation + */ + NONE, + /** + * Rotated clockwise by 90 degrees + */ + CLOCKWISE, + /** + * Flipped upside-down, a 180 degree rotation + */ + FLIPPED, + /** + * Rotated counter-clockwise by 90 degrees + */ + COUNTER_CLOCKWISE, + ; + + private static final Rotation [] rotations = values(); + + /** + * Rotate clockwise by 90 degrees. + * + * @return the relative rotation + */ + public Rotation rotateClockwise() { + return rotations[(this.ordinal() + 1) & 0x3]; + } + + /** + * Rotate counter-clockwise by 90 degrees. + * + * @return the relative rotation + */ + public Rotation rotateCounterClockwise() { + return rotations[(this.ordinal() - 1) & 0x3]; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/SandstoneType.java b/vspigot-api/src/main/java/org/bukkit/SandstoneType.java new file mode 100644 index 0000000..a9ac16e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/SandstoneType.java @@ -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 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/Server.java b/vspigot-api/src/main/java/org/bukkit/Server.java new file mode 100644 index 0000000..9ae80fd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Server.java @@ -0,0 +1,1010 @@ +package org.bukkit; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Warning.WarningState; +import org.bukkit.command.CommandException; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.server.ServerListPingEvent; +import org.bukkit.help.HelpMap; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; +import org.bukkit.map.MapView; +import org.bukkit.permissions.Permissible; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.ServicesManager; +import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.plugin.messaging.PluginMessageRecipient; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scoreboard.ScoreboardManager; +import org.bukkit.util.CachedServerIcon; + +import com.avaje.ebean.config.ServerConfig; +import com.google.common.collect.ImmutableList; + +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; + +/** + * Represents a server implementation. + */ +public interface Server extends PluginMessageRecipient { + + /** + * Used for all administrative messages, such as an operator using a + * command. + *

          + * For use in {@link #broadcast(java.lang.String, java.lang.String)}. + */ + public static final String BROADCAST_CHANNEL_ADMINISTRATIVE = "bukkit.broadcast.admin"; + + /** + * Used for all announcement messages, such as informing users that a + * player has joined. + *

          + * For use in {@link #broadcast(java.lang.String, java.lang.String)}. + */ + public static final String BROADCAST_CHANNEL_USERS = "bukkit.broadcast.user"; + + /** + * Gets the name of this server implementation. + * + * @return name of this server implementation + */ + public String getName(); + + /** + * Gets the version string of this server implementation. + * + * @return version of this server implementation + */ + public String getVersion(); + + /** + * Gets the Bukkit version that this server is running. + * + * @return version of Bukkit + */ + public String getBukkitVersion(); + + /** + * Gets an array copy of all currently logged in players. + *

          + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + * + * @Deprecated superseded by {@link #getOnlinePlayers()} + * @return an array of Players that are currently online + */ + @Deprecated + public Player[] _INVALID_getOnlinePlayers(); + + /** + * Gets a view of all currently logged in players. This {@linkplain + * Collections#unmodifiableCollection(Collection) view} is a reused + * object, making some operations like {@link Collection#size()} + * zero-allocation. + *

          + * The collection is a view backed by the internal representation, such + * that, changes to the internal state of the server will be reflected + * immediately. However, the reuse of the returned collection (identity) + * is not strictly guaranteed for future or all implementations. Casting + * the collection, or relying on interface implementations (like {@link + * Serializable} or {@link List}), is deprecated. + *

          + * Iteration behavior is undefined outside of self-contained main-thread + * uses. Normal and immediate iterator use without consequences that + * affect the collection are fully supported. The effects following + * (non-exhaustive) {@link Entity#teleport(Location) teleportation}, + * {@link Player#setHealth(double) death}, and {@link Player#kickPlayer( + * String) kicking} are undefined. Any use of this collection from + * asynchronous threads is unsafe. + *

          + * For safe consequential iteration or mimicking the old array behavior, + * using {@link Collection#toArray(Object[])} is recommended. For making + * snapshots, {@link ImmutableList#copyOf(Collection)} is recommended. + * + * @return a view of currently online players. + */ + public Collection getOnlinePlayers(); + + /** + * Get the maximum amount of players which can login to this server. + * + * @return the amount of players this server allows + */ + public int getMaxPlayers(); + + /** + * Get the game port that the server runs on. + * + * @return the port number of this server + */ + public int getPort(); + + /** + * Get the view distance from this server. + * + * @return the view distance from this server. + */ + public int getViewDistance(); + + /** + * Get the IP that this server is bound to, or empty string if not + * specified. + * + * @return the IP string that this server is bound to, otherwise empty + * string + */ + public String getIp(); + + /** + * Get the name of this server. + * + * @return the name of this server + */ + public String getServerName(); + + /** + * Get the server group of this server. + * + * @return the server group of this server + */ + public String getServerGroup(); + + /** + * Get an ID of this server. The ID is a simple generally alphanumeric ID + * that can be used for uniquely identifying this server. + * + * @return the ID of this server + */ + public String getServerId(); + + /** + * Get world type (level-type setting) for default world. + * + * @return the value of level-type (e.g. DEFAULT, FLAT, DEFAULT_1_1) + */ + public String getWorldType(); + + /** + * Get generate-structures setting. + * + * @return true if structure generation is enabled, false otherwise + */ + public boolean getGenerateStructures(); + + /** + * Gets whether this server allows the End or not. + * + * @return whether this server allows the End or not + */ + public boolean getAllowEnd(); + + /** + * Gets whether this server allows the Nether or not. + * + * @return whether this server allows the Nether or not + */ + public boolean getAllowNether(); + + /** + * Gets whether this server has a whitelist or not. + * + * @return whether this server has a whitelist or not + */ + public boolean hasWhitelist(); + + /** + * Sets if the server is whitelisted. + * + * @param value true for whitelist on, false for off + */ + public void setWhitelist(boolean value); + + /** + * Gets a list of whitelisted players. + * + * @return a set containing all whitelisted players + */ + public Set getWhitelistedPlayers(); + + /** + * Reloads the whitelist from disk. + */ + public void reloadWhitelist(); + + /** + * Broadcast a message to all players. + *

          + * This is the same as calling {@link #broadcast(java.lang.String, + * java.lang.String)} to {@link #BROADCAST_CHANNEL_USERS} + * + * @param message the message + * @return the number of players + */ + public int broadcastMessage(String message); + + /** + * Broadcast a message to all players. With chatcolor translate + *

          + * This is the same as calling {@link #broadcast(java.lang.String, + * java.lang.String)} to {@link #BROADCAST_CHANNEL_USERS} + * + * @param message the message + * @return the number of players + */ + public int broadcastTranslate(String message); + + /** + * Gets the name of the update folder. The update folder is used to safely + * update plugins at the right moment on a plugin load. + *

          + * The update folder name is relative to the plugins folder. + * + * @return the name of the update folder + */ + public String getUpdateFolder(); + + /** + * Gets the update folder. The update folder is used to safely update + * plugins at the right moment on a plugin load. + * + * @return the update folder + */ + public File getUpdateFolderFile(); + + /** + * Gets the value of the connection throttle setting. + * + * @return the value of the connection throttle setting + */ + public long getConnectionThrottle(); + + /** + * Gets default ticks per animal spawns value. + *

          + * Example Usage: + *

            + *
          • A value of 1 will mean the server will attempt to spawn monsters + * every tick. + *
          • A value of 400 will mean the server will attempt to spawn monsters + * every 400th tick. + *
          • A value below 0 will be reset back to Minecraft's default. + *
          + *

          + * Note: If set to 0, animal spawning will be disabled. We + * recommend using spawn-animals to control this instead. + *

          + * Minecraft default: 400. + * + * @return the default ticks per animal spawns value + */ + public int getTicksPerAnimalSpawns(); + + /** + * Gets the default ticks per monster spawns value. + *

          + * Example Usage: + *

            + *
          • A value of 1 will mean the server will attempt to spawn monsters + * every tick. + *
          • A value of 400 will mean the server will attempt to spawn monsters + * every 400th tick. + *
          • A value below 0 will be reset back to Minecraft's default. + *
          + *

          + * Note: If set to 0, monsters spawning will be disabled. We + * recommend using spawn-monsters to control this instead. + *

          + * Minecraft default: 1. + * + * @return the default ticks per monsters spawn value + */ + public int getTicksPerMonsterSpawns(); + + /** + * Gets a player object by the given username. + *

          + * This method may not return objects for offline players. + * + * @param name the name to look up + * @return a player if one was found, null otherwise + */ + public Player getPlayer(String name); + + /** + * Gets the player with the exact given name, case insensitive. + * + * @param name Exact name of the player to retrieve + * @return a player object if one was found, null otherwise + */ + public Player getPlayerExact(String name); + + /** + * Attempts to match any players with the given name, and returns a list + * of all possibly matches. + *

          + * This list is not sorted in any particular order. If an exact match is + * found, the returned list will only contain a single result. + * + * @param name the (partial) name to match + * @return list of all possible players + */ + public List matchPlayer(String name); + + /** + * Gets the player with the given UUID. + * + * @param id UUID of the player to retrieve + * @return a player object if one was found, null otherwise + */ + public Player getPlayer(UUID id); + + /** + * Gets the plugin manager for interfacing with plugins. + * + * @return a plugin manager for this Server instance + */ + public PluginManager getPluginManager(); + + /** + * Gets the scheduler for managing scheduled events. + * + * @return a scheduling service for this server + */ + public BukkitScheduler getScheduler(); + + /** + * Gets a services manager. + * + * @return s services manager + */ + public ServicesManager getServicesManager(); + + /** + * Gets a list of all worlds on this server. + * + * @return a list of worlds + */ + public List getWorlds(); + + /** + * Creates or loads a world with the given name using the specified + * options. + *

          + * If the world is already loaded, it will just return the equivalent of + * getWorld(creator.name()). + * + * @param creator the options to use when creating the world + * @return newly created or loaded world + */ + public World createWorld(WorldCreator creator); + + /** + * Unloads a world with the given name. + * + * @param name Name of the world to unload + * @param save whether to save the chunks before unloading + * @return true if successful, false otherwise + */ + public boolean unloadWorld(String name, boolean save); + + /** + * Unloads the given world. + * + * @param world the world to unload + * @param save whether to save the chunks before unloading + * @return true if successful, false otherwise + */ + public boolean unloadWorld(World world, boolean save); + + /** + * Gets the world with the given name. + * + * @param name the name of the world to retrieve + * @return a world with the given name, or null if none exists + */ + public World getWorld(String name); + + /** + * Gets the world from the given Unique ID. + * + * @param uid a unique-id of the world to retrieve + * @return a world with the given Unique ID, or null if none exists + */ + public World getWorld(UUID uid); + + /** + * Gets the map from the given item ID. + * + * @param id the id of the map to get + * @return a map view if it exists, or null otherwise + * @deprecated Magic value + */ + @Deprecated + public MapView getMap(short id); + + /** + * Create a new map with an automatically assigned ID. + * + * @param world the world the map will belong to + * @return a newly created map view + */ + public MapView createMap(World world); + + /** + * Reloads the server, refreshing settings and plugin information. + */ + public void reload(); + + /** + * Returns the primary logger associated with this server instance. + * + * @return Logger associated with this server + */ + public Logger getLogger(); + + /** + * Gets a {@link PluginCommand} with the given name or alias. + * + * @param name the name of the command to retrieve + * @return a plugin command if found, null otherwise + */ + public PluginCommand getPluginCommand(String name); + + /** + * Writes loaded players to disk. + */ + public void savePlayers(); + + /** + * Dispatches a command on this server, and executes it if found. + * + * @param sender the apparent sender of the command + * @param commandLine the command + arguments. Example: test abc + * 123 + * @return returns false if no target is found + * @throws CommandException thrown when the executor for the given command + * fails with an unhandled exception + */ + public boolean dispatchCommand(CommandSender sender, String commandLine) throws CommandException; + + /** + * Populates a given {@link ServerConfig} with values attributes to this + * server. + * + * @param config the server config to populate + */ + public void configureDbConfig(ServerConfig config); + + /** + * Adds a recipe to the crafting manager. + * + * @param recipe the recipe to add + * @return true if the recipe was added, false if it wasn't for some + * reason + */ + public boolean addRecipe(Recipe recipe); + + /** + * Get a list of all recipes for a given item. The stack size is ignored + * in comparisons. If the durability is -1, it will match any data value. + * + * @param result the item to match against recipe results + * @return a list of recipes with the given result + */ + public List getRecipesFor(ItemStack result); + + /** + * Get an iterator through the list of crafting recipes. + * + * @return an iterator + */ + public Iterator recipeIterator(); + + /** + * Clears the list of crafting recipes. + */ + public void clearRecipes(); + + /** + * Resets the list of crafting recipes to the default. + */ + public void resetRecipes(); + + /** + * Gets a list of command aliases defined in the server properties. + * + * @return a map of aliases to command names + */ + public Map getCommandAliases(); + + /** + * Gets the radius, in blocks, around each worlds spawn point to protect. + * + * @return spawn radius, or 0 if none + */ + public int getSpawnRadius(); + + /** + * Sets the radius, in blocks, around each worlds spawn point to protect. + * + * @param value new spawn radius, or 0 if none + */ + public void setSpawnRadius(int value); + + /** + * Gets whether the Server is in online mode or not. + * + * @return true if the server authenticates clients, false otherwise + */ + public boolean getOnlineMode(); + + /** + * Gets whether this server allows flying or not. + * + * @return true if the server allows flight, false otherwise + */ + public boolean getAllowFlight(); + + /** + * Gets whether the server is in hardcore mode or not. + * + * @return true if the server mode is hardcore, false otherwise + */ + public boolean isHardcore(); + + /** + * Gets whether to use vanilla (false) or exact behaviour (true). + * + *

            + *
          • Vanilla behaviour: check for collisions and move the player if + * needed. + *
          • Exact behaviour: spawn players exactly where they should be. + *
          + * + * @return true if exact location locations are used for spawning, false + * for vanilla collision detection or otherwise + */ + public boolean useExactLoginLocation(); + + /** + * Shutdowns the server, stopping everything. + */ + public void shutdown(); + + /** + * Broadcasts the specified message to every user with the given + * permission name. + * + * @param message message to broadcast + * @param permission the required permission {@link Permissible + * permissibles} must have to receive the broadcast + * @return number of message recipients + */ + public int broadcast(String message, String permission); + + /** + * Gets the player by the given name, regardless if they are offline or + * online. + *

          + * This method may involve a blocking web request to get the UUID for the + * given name. + *

          + * This will return an object even if the player does not exist. To this + * method, all players will exist. + * + * @deprecated Persistent storage of users should be by UUID as names are no longer + * unique past a single session. + * @param name the name the player to retrieve + * @return an offline player + * @see #getOfflinePlayer(java.util.UUID) + */ + @Deprecated + public OfflinePlayer getOfflinePlayer(String name); + + /** + * Gets the player by the given UUID, regardless if they are offline or + * online. + *

          + * This will return an object even if the player does not exist. To this + * method, all players will exist. + * + * @param id the UUID of the player to retrieve + * @return an offline player + */ + public OfflinePlayer getOfflinePlayer(UUID id); + + /** + * Gets a set containing all current IPs that are banned. + * + * @return a set containing banned IP addresses + */ + public Set getIPBans(); + + /** + * Bans the specified address from the server. + * + * @param address the IP address to ban + */ + public void banIP(String address); + + /** + * Unbans the specified address from the server. + * + * @param address the IP address to unban + */ + public void unbanIP(String address); + + /** + * Gets a set containing all banned players. + * + * @return a set containing banned players + */ + public Set getBannedPlayers(); + + /** + * Gets a ban list for the supplied type. + *

          + * Bans by name are no longer supported and this method will return + * null when trying to request them. The replacement is bans by UUID. + * + * @param type the type of list to fetch, cannot be null + * @return a ban list of the specified type + */ + public BanList getBanList(BanList.Type type); + + /** + * Gets a set containing all player operators. + * + * @return a set containing player operators + */ + public Set getOperators(); + + /** + * Gets the default {@link GameMode} for new players. + * + * @return the default game mode + */ + public GameMode getDefaultGameMode(); + + /** + * Sets the default {@link GameMode} for new players. + * + * @param mode the new game mode + */ + public void setDefaultGameMode(GameMode mode); + + /** + * Gets a {@link ConsoleCommandSender} that may be used as an input source + * for this server. + * + * @return a console command sender + */ + public ConsoleCommandSender getConsoleSender(); + + /** + * Gets the folder that contains all of the various {@link World}s. + * + * @return folder that contains all worlds + */ + public File getWorldContainer(); + + /** + * Gets every player that has ever played on this server. + * + * @return an array containing all previous players + */ + public OfflinePlayer[] getOfflinePlayers(); + + /** + * Gets the {@link Messenger} responsible for this server. + * + * @return messenger responsible for this server + */ + public Messenger getMessenger(); + + /** + * Gets the {@link HelpMap} providing help topics for this server. + * + * @return a help map for this server + */ + public HelpMap getHelpMap(); + + /** + * Creates an empty inventory of the specified type. If the type is {@link + * InventoryType#CHEST}, the new inventory has a size of 27; otherwise the + * new inventory has the normal size for its type. + * + * @param owner the holder of the inventory, or null to indicate no holder + * @param type the type of inventory to create + * @return a new inventory + */ + Inventory createInventory(InventoryHolder owner, InventoryType type); + + /** + * Creates an empty inventory with the specified type and title. If the type + * is {@link InventoryType#CHEST}, the new inventory has a size of 27; + * otherwise the new inventory has the normal size for its type.
          + * It should be noted that some inventory types do not support titles and + * may not render with said titles on the Minecraft client. + * + * @param owner The holder of the inventory; can be null if there's no holder. + * @param type The type of inventory to create. + * @param title The title of the inventory, to be displayed when it is viewed. + * @return The new inventory. + */ + Inventory createInventory(InventoryHolder owner, InventoryType type, String title); + + /** + * Creates an empty inventory of type {@link InventoryType#CHEST} with the + * specified size. + * + * @param owner the holder of the inventory, or null to indicate no holder + * @param size a multiple of 9 as the size of inventory to create + * @return a new inventory + * @throws IllegalArgumentException if the size is not a multiple of 9 + */ + Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException; + + /** + * Creates an empty inventory of type {@link InventoryType#CHEST} with the + * specified size and title. + * + * @param owner the holder of the inventory, or null to indicate no holder + * @param size a multiple of 9 as the size of inventory to create + * @param title the title of the inventory, displayed when inventory is + * viewed + * @return a new inventory + * @throws IllegalArgumentException if the size is not a multiple of 9 + */ + Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException; + + /** + * Gets user-specified limit for number of monsters that can spawn in a + * chunk. + * + * @return the monster spawn limit + */ + int getMonsterSpawnLimit(); + + /** + * Gets user-specified limit for number of animals that can spawn in a + * chunk. + * + * @return the animal spawn limit + */ + int getAnimalSpawnLimit(); + + /** + * Gets user-specified limit for number of water animals that can spawn in + * a chunk. + * + * @return the water animal spawn limit + */ + int getWaterAnimalSpawnLimit(); + + /** + * Gets user-specified limit for number of ambient mobs that can spawn in + * a chunk. + * + * @return the ambient spawn limit + */ + int getAmbientSpawnLimit(); + + /** + * Checks the current thread against the expected primary thread for the + * server. + *

          + * Note: this method should not be used to indicate the current + * synchronized state of the runtime. A current thread matching the main + * thread indicates that it is synchronized, but a mismatch does not + * preclude the same assumption. + * + * @return true if the current thread matches the expected primary thread, + * false otherwise + */ + boolean isPrimaryThread(); + + /** + * Gets the message that is displayed on the server list. + * + * @return the servers MOTD + */ + String getMotd(); + + /** + * Gets the default message that is displayed when the server is stopped. + * + * @return the shutdown message + */ + String getShutdownMessage(); + + /** + * Gets the current warning state for the server. + * + * @return the configured warning state + */ + public WarningState getWarningState(); + + /** + * Gets the instance of the item factory (for {@link ItemMeta}). + * + * @return the item factory + * @see ItemFactory + */ + ItemFactory getItemFactory(); + + /** + * Gets the instance of the scoreboard manager. + *

          + * This will only exist after the first world has loaded. + * + * @return the scoreboard manager or null if no worlds are loaded. + */ + ScoreboardManager getScoreboardManager(); + + /** + * Gets an instance of the server's default server-icon. + * + * @return the default server-icon; null values may be used by the + * implementation to indicate no defined icon, but this behavior is + * not guaranteed + */ + CachedServerIcon getServerIcon(); + + /** + * Loads an image from a file, and returns a cached image for the specific + * server-icon. + *

          + * Size and type are implementation defined. An incompatible file is + * guaranteed to throw an implementation-defined {@link Exception}. + * + * @param file the file to load the from + * @throws IllegalArgumentException if image is null + * @throws Exception if the image does not meet current server server-icon + * specifications + * @return a cached server-icon that can be used for a {@link + * ServerListPingEvent#setServerIcon(CachedServerIcon)} + */ + CachedServerIcon loadServerIcon(File file) throws IllegalArgumentException, Exception; + + /** + * Creates a cached server-icon for the specific image. + *

          + * Size and type are implementation defined. An incompatible file is + * guaranteed to throw an implementation-defined {@link Exception}. + * + * @param image the image to use + * @throws IllegalArgumentException if image is null + * @throws Exception if the image does not meet current server + * server-icon specifications + * @return a cached server-icon that can be used for a {@link + * ServerListPingEvent#setServerIcon(CachedServerIcon)} + */ + CachedServerIcon loadServerIcon(BufferedImage image) throws IllegalArgumentException, Exception; + + /** + * Set the idle kick timeout. Any players idle for the specified amount of + * time will be automatically kicked. + *

          + * A value of 0 will disable the idle kick timeout. + * + * @param threshold the idle timeout in minutes + */ + public void setIdleTimeout(int threshold); + + /** + * Gets the idle kick timeout. + * + * @return the idle timeout in minutes + */ + public int getIdleTimeout(); + + // Guardian start + + /** + * @return Whether Guardian is enabled or not. + */ + public boolean isGuardianEnabled(); + + /** + * Set whether Guardian is enabled or not. + * + * @param enabled - The new state to set to + */ + public void setGuardianEnabled(boolean enabled); + + /** + * @return Whether Guardian should do checks in the server's + * current conditions or not. + */ + public boolean shouldGuardianAct(); + // Guardian end + + // MineHQ start + /** + * Gets a player object by the given disguised name. + *

          + * This method may not return objects for disguises not in use. + * + * @param name the disguised name to look up + * @return a player if one was found, null otherwise + */ + public Player getPlayerByDisguise(String name); + + /** + * Gets the player with the exact given disguise name, case insensitive + *

          + * This method may not return objects for disguises not in use. + * + * @param name the exact disguised name of a player + * @return a player if one was found, null otherwise + */ + public Player getPlayerExactByDisguise(String name); + // MineHQ end + + /** + * @see UnsafeValues + */ + @Deprecated + UnsafeValues getUnsafe(); + + public class Spigot + { + + public org.bukkit.configuration.file.YamlConfiguration getConfig() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Sends the component to the player + * + * @param component the components to send + */ + public void broadcast(net.md_5.bungee.api.chat.BaseComponent component) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Sends an array of components as a single message to the + * player + * + * @param components the components to send + */ + public void broadcast(net.md_5.bungee.api.chat.BaseComponent ...components) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + // PaperSpigot start - Add getTPS method + public double[] getTPS() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + // PaperSpigot end + } + + Spigot spigot(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/SkullType.java b/vspigot-api/src/main/java/org/bukkit/SkullType.java new file mode 100644 index 0000000..abd07c2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/SkullType.java @@ -0,0 +1,12 @@ +package org.bukkit; + +/** + * Represents the different types of skulls. + */ +public enum SkullType { + SKELETON, + WITHER, + ZOMBIE, + PLAYER, + CREEPER; +} diff --git a/vspigot-api/src/main/java/org/bukkit/Sound.java b/vspigot-api/src/main/java/org/bukkit/Sound.java new file mode 100644 index 0000000..0913530 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Sound.java @@ -0,0 +1,211 @@ +package org.bukkit; + +/** + * An Enum of Sounds the server is able to send to players. + *

          + * 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, +} diff --git a/vspigot-api/src/main/java/org/bukkit/Statistic.java b/vspigot-api/src/main/java/org/bukkit/Statistic.java new file mode 100644 index 0000000..57df72b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Statistic.java @@ -0,0 +1,108 @@ +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, + 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); + + 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. + *

          + * A substatistic exists en masse for each block, item, or entitytype, depending on + * {@link #getType()}. + *

          + * This is a redundant method and equivalent to checking + * getType() != Type.UNTYPED + * + * @return true if this is a substatistic + */ + public boolean isSubstatistic() { + return type != Type.UNTYPED; + } + + /** + * Checks if this is a substatistic dealing with blocks. + *

          + * This is a redundant method and equivalent to checking + * getType() == Type.BLOCK + * + * @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; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/TravelAgent.java b/vspigot-api/src/main/java/org/bukkit/TravelAgent.java new file mode 100644 index 0000000..2dfeffa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/TravelAgent.java @@ -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. + *

          + * 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. + *

          + * In the case of a Nether portal teleportation, this will attempt to + * create a Nether portal. + *

          + * 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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/TreeSpecies.java b/vspigot-api/src/main/java/org/bukkit/TreeSpecies.java new file mode 100644 index 0000000..f29062a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/TreeSpecies.java @@ -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 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/TreeType.java b/vspigot-api/src/main/java/org/bukkit/TreeType.java new file mode 100644 index 0000000..5a5c4d9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/TreeType.java @@ -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, +} diff --git a/vspigot-api/src/main/java/org/bukkit/UnsafeValues.java b/vspigot-api/src/main/java/org/bukkit/UnsafeValues.java new file mode 100644 index 0000000..fc8a179 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/UnsafeValues.java @@ -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). + *

          + * 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. + *

          + * This interface is unsupported and only for internal use. + * + * @deprecated Unsupported & internal use only + */ +@Deprecated +public interface UnsafeValues { + + Material getMaterialFromInternalName(String name); + + List tabCompleteInternalMaterialName(String token, List completions); + + ItemStack modifyItemStack(ItemStack stack, String arguments); + + Statistic getStatisticFromInternalName(String name); + + Achievement getAchievementFromInternalName(String name); + + List tabCompleteInternalStatisticOrAchievementName(String token, List completions); +} diff --git a/vspigot-api/src/main/java/org/bukkit/Utility.java b/vspigot-api/src/main/java/org/bukkit/Utility.java new file mode 100644 index 0000000..da66853 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Utility.java @@ -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. + *

          + * 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 { +} diff --git a/vspigot-api/src/main/java/org/bukkit/Warning.java b/vspigot-api/src/main/java/org/bukkit/Warning.java new file mode 100644 index 0000000..6a2a3b0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/Warning.java @@ -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. + *

          + * 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 values = ImmutableMap.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

            + *
          • ON is always True + *
          • OFF is always false + *
          • DEFAULT is false if and only if annotation is not null and + * specifies false for {@link Warning#value()}, true otherwise. + *
          + */ + 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 ""; +} diff --git a/vspigot-api/src/main/java/org/bukkit/WeatherType.java b/vspigot-api/src/main/java/org/bukkit/WeatherType.java new file mode 100644 index 0000000..36b993f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/WeatherType.java @@ -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, + ; +} diff --git a/vspigot-api/src/main/java/org/bukkit/World.java b/vspigot-api/src/main/java/org/bukkit/World.java new file mode 100644 index 0000000..7c94319 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/World.java @@ -0,0 +1,1309 @@ +package org.bukkit; + +import java.io.File; +import org.bukkit.generator.ChunkGenerator; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.entity.*; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.Metadatable; +import org.bukkit.plugin.messaging.PluginMessageRecipient; +import org.bukkit.util.Vector; + +/** + * Represents a world, which may contain entities, chunks and blocks + */ +public interface World extends PluginMessageRecipient, Metadatable { + + /** + * Gets the {@link Block} at the given coordinates + * + * @param x X-coordinate of the block + * @param y Y-coordinate of the block + * @param z Z-coordinate of the block + * @return Block at the given coordinates + * @see #getBlockTypeIdAt(int, int, int) Returns the current type ID of + * the block + */ + public Block getBlockAt(int x, int y, int z); + + /** + * Gets the {@link Block} at the given {@link Location} + * + * @param location Location of the block + * @return Block at the given location + * @see #getBlockTypeIdAt(org.bukkit.Location) Returns the current type ID + * of the block + */ + public Block getBlockAt(Location location); + + /** + * Gets the block type ID at the given coordinates + * + * @param x X-coordinate of the block + * @param y Y-coordinate of the block + * @param z Z-coordinate of the block + * @return Type ID of the block at the given coordinates + * @see #getBlockAt(int, int, int) Returns a live Block object at the + * given location + * @deprecated Magic value + */ + @Deprecated + public int getBlockTypeIdAt(int x, int y, int z); + + /** + * Gets the block type ID at the given {@link Location} + * + * @param location Location of the block + * @return Type ID of the block at the given location + * @see #getBlockAt(org.bukkit.Location) Returns a live Block object at + * the given location + * @deprecated Magic value + */ + @Deprecated + public int getBlockTypeIdAt(Location location); + + /** + * 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 + */ + public int getHighestBlockYAt(int x, int z); + + /** + * Gets the highest non-air coordinate at the given {@link Location} + * + * @param location Location of the blocks + * @return Y-coordinate of the highest non-air block + */ + public int getHighestBlockYAt(Location location); + + /** + * Gets the highest non-empty block at the given coordinates + * + * @param x X-coordinate of the block + * @param z Z-coordinate of the block + * @return Highest non-empty block + */ + public Block getHighestBlockAt(int x, int z); + + /** + * Gets the highest non-empty block at the given coordinates + * + * @param location Coordinates to get the highest block + * @return Highest non-empty block + */ + public Block getHighestBlockAt(Location location); + + /** + * Gets the {@link Chunk} at the given coordinates + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return Chunk at the given coordinates + */ + public Chunk getChunkAt(int x, int z); + + /** + * Gets the {@link Chunk} at the given {@link Location} + * + * @param location Location of the chunk + * @return Chunk at the given location + */ + public Chunk getChunkAt(Location location); + + /** + * Gets the {@link Chunk} that contains the given {@link Block} + * + * @param block Block to get the containing chunk from + * @return The chunk that contains the given block + */ + public Chunk getChunkAt(Block block); + + // PaperSpigot start - Async chunk load API + public static interface ChunkLoadCallback { + public void onLoad(Chunk chunk); + } + public void getChunkAtAsync(int x, int z, ChunkLoadCallback cb); + public void getChunkAtAsync(Location location, ChunkLoadCallback cb); + public void getChunkAtAsync(Block block, ChunkLoadCallback cb); + // PaperSpigot end + + /** + * Checks if the specified {@link Chunk} is loaded + * + * @param chunk The chunk to check + * @return true if the chunk is loaded, otherwise false + */ + public boolean isChunkLoaded(Chunk chunk); + + /** + * Gets an array of all loaded {@link Chunk}s + * + * @return Chunk[] containing all loaded chunks + */ + public Chunk[] getLoadedChunks(); + + /** + * Loads the specified {@link Chunk} + * + * @param chunk The chunk to load + */ + public void loadChunk(Chunk chunk); + + /** + * Checks if the {@link Chunk} at the specified coordinates is loaded + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return true if the chunk is loaded, otherwise false + */ + public boolean isChunkLoaded(int x, int z); + + /** + * Checks if the {@link Chunk} at the specified coordinates is loaded and + * in use by one or more players + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return true if the chunk is loaded and in use by one or more players, + * otherwise false + */ + public boolean isChunkInUse(int x, int z); + + /** + * Loads the {@link Chunk} at the specified coordinates + *

          + * If the chunk does not exist, it will be generated. + *

          + * This method is analogous to {@link #loadChunk(int, int, boolean)} where + * generate is true. + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + */ + public void loadChunk(int x, int z); + + /** + * Loads the {@link Chunk} at the specified coordinates + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of 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 + */ + public boolean loadChunk(int x, int z, boolean generate); + + /** + * Safely unloads and saves the {@link Chunk} at the specified coordinates + *

          + * This method is analogous to {@link #unloadChunk(int, int, boolean, + * boolean)} where safe and saveis true + * + * @param chunk the chunk to unload + * @return true if the chunk has unloaded successfully, otherwise false + */ + public boolean unloadChunk(Chunk chunk); + + /** + * Safely unloads and saves the {@link Chunk} at the specified coordinates + *

          + * This method is analogous to {@link #unloadChunk(int, int, boolean, + * boolean)} where safe and saveis true + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return true if the chunk has unloaded successfully, otherwise false + */ + public boolean unloadChunk(int x, int z); + + /** + * Safely unloads and optionally saves the {@link Chunk} at the specified + * coordinates + *

          + * This method is analogous to {@link #unloadChunk(int, int, boolean, + * boolean)} where save is true + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @param save Whether or not to save the chunk + * @return true if the chunk has unloaded successfully, otherwise false + */ + public boolean unloadChunk(int x, int z, boolean save); + + /** + * Unloads and optionally saves the {@link Chunk} at the specified + * coordinates + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of 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 + */ + public boolean unloadChunk(int x, int z, boolean save, boolean safe); + + /** + * Safely queues the {@link Chunk} at the specified coordinates for + * unloading + *

          + * This method is analogous to {@link #unloadChunkRequest(int, int, + * boolean)} where safe is true + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return true is the queue attempt was successful, otherwise false + */ + public boolean unloadChunkRequest(int x, int z); + + /** + * Queues the {@link Chunk} at the specified coordinates for unloading + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @param safe Controls whether to queue the chunk when players are nearby + * @return Whether the chunk was actually queued + */ + public boolean unloadChunkRequest(int x, int z, boolean safe); + + /** + * Regenerates the {@link Chunk} at the specified coordinates + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return Whether the chunk was actually regenerated + */ + public boolean regenerateChunk(int x, int z); + + /** + * Resends the {@link Chunk} to all clients + * + * @param x X-coordinate of the chunk + * @param z Z-coordinate of the chunk + * @return Whether the chunk was actually refreshed + */ + public boolean refreshChunk(int x, int z); + + /** + * Drops an item at the specified {@link Location} + * + * @param location Location to drop the item + * @param item ItemStack to drop + * @return ItemDrop entity created as a result of this method + */ + public Item dropItem(Location location, ItemStack item); + + /** + * Drops an item at the specified {@link Location} with a random offset + * + * @param location Location to drop the item + * @param item ItemStack to drop + * @return ItemDrop entity created as a result of this method + */ + public Item dropItemNaturally(Location location, ItemStack item); + + /** + * Creates an {@link Arrow} entity at the given {@link Location} + * + * @param location Location to spawn the arrow + * @param direction Direction to shoot the arrow in + * @param speed Speed of the arrow. A recommend speed is 0.6 + * @param spread Spread of the arrow. A recommend spread is 12 + * @return Arrow entity spawned as a result of this method + */ + public Arrow spawnArrow(Location location, Vector direction, float speed, float spread); + + /** + * Creates a tree at the given {@link Location} + * + * @param location Location to spawn the tree + * @param type Type of the tree to create + * @return true if the tree was created successfully, otherwise false + */ + public boolean generateTree(Location location, TreeType type); + + /** + * Creates a tree at the given {@link Location} + * + * @param loc Location to spawn the tree + * @param type Type of the tree to create + * @param delegate A class to call for each block changed as a result of + * this method + * @return true if the tree was created successfully, otherwise false + */ + public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate); + + /** + * Creates a entity at the given {@link Location} + * + * @param loc The location to spawn the entity + * @param type The entity to spawn + * @return Resulting Entity of this method, or null if it was unsuccessful + */ + public Entity spawnEntity(Location loc, EntityType type); + + /** + * Creates a creature at the given {@link Location} + * + * @param loc The location to spawn the creature + * @param type The creature to spawn + * @return Resulting LivingEntity of this method, or null if it was + * unsuccessful + * @deprecated Has issues spawning non LivingEntities. Use {@link + * #spawnEntity(Location, EntityType) spawnEntity} instead. + */ + @Deprecated + public LivingEntity spawnCreature(Location loc, EntityType type); + + /** + * Creates a creature at the given {@link Location} + * + * @param loc The location to spawn the creature + * @param type The creature to spawn + * @return Resulting LivingEntity of this method, or null if it was + * unsuccessful + */ + @Deprecated + public LivingEntity spawnCreature(Location loc, CreatureType type); + + /** + * Strikes lightning at the given {@link Location} + * + * @param loc The location to strike lightning + * @return The lightning entity. + */ + public LightningStrike strikeLightning(Location loc); + + /** + * Strikes lightning at the given {@link Location} without doing damage + * + * @param loc The location to strike lightning + * @return The lightning entity. + */ + public LightningStrike strikeLightningEffect(Location loc); + + /** + * Get a list of all entities in this World + * + * @return A List of all Entities currently residing in this world + */ + public List getEntities(); + + /** + * Get a list of all living entities in this World + * + * @return A List of all LivingEntities currently residing in this world + */ + public List getLivingEntities(); + + /** + * Get a collection of all entities in this World matching the given + * class/interface + * + * @param classes The classes representing the types of entity to match + * @return A List of all Entities currently residing in this world that + * match the given class/interface + */ + @Deprecated + public Collection getEntitiesByClass(Class... classes); + + /** + * Get a collection of all entities in this World matching the given + * class/interface + * + * @param cls The class representing the type of entity to match + * @return A List of all Entities currently residing in this world that + * match the given class/interface + */ + public Collection getEntitiesByClass(Class cls); + + /** + * Get a collection of all entities in this World matching any of the + * given classes/interfaces + * + * @param classes The classes representing the types of entity to match + * @return A List of all Entities currently residing in this world that + * match one or more of the given classes/interfaces + */ + public Collection getEntitiesByClasses(Class... classes); + + /** + * Get a list of all players in this World + * + * @return A list of all Players currently residing in this world + */ + public List getPlayers(); + + /** + * Gets the unique name of this world + * + * @return Name of this world + */ + public String getName(); + + /** + * Gets the Unique ID of this world + * + * @return Unique ID of this world. + */ + public UUID getUID(); + + /** + * Gets the default spawn {@link Location} of this world + * + * @return The spawn location of this world + */ + public Location getSpawnLocation(); + + /** + * Sets the spawn location of the world + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @return True if it was successfully set. + */ + public boolean setSpawnLocation(int x, int y, int z); + + // Poweruser start + /** + * Sets the spawn location of the world + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @param yaw left-right rotation + * @param pitch up-down rotation + * @return True if it was successfully set. + */ + public boolean setSpawnLocation(int x, int y, int z, float yaw, float pitch); + // Poweruser end + + /** + * Gets the relative in-game time of this world. + *

          + * The relative time is analogous to hours * 1000 + * + * @return The current relative time + * @see #getFullTime() Returns an absolute time of this world + */ + public long getTime(); + + /** + * Sets the relative in-game time on the server. + *

          + * The relative time is analogous to hours * 1000 + *

          + * Note that setting the relative time below the current relative time + * will actually move the clock forward a day. If you require to rewind + * time, please see {@link #setFullTime(long)} + * + * @param time The new relative time to set the in-game time to (in + * hours*1000) + * @see #setFullTime(long) Sets the absolute time of this world + */ + public void setTime(long time); + + /** + * Gets the full in-game time on this world + * + * @return The current absolute time + * @see #getTime() Returns a relative time of this world + */ + public long getFullTime(); + + /** + * Sets the in-game time on the server + *

          + * Note that this sets the full time of the world, which may cause adverse + * effects such as breaking redstone clocks and any scheduled events + * + * @param time The new absolute time to set this world to + * @see #setTime(long) Sets the relative time of this world + */ + public void setFullTime(long time); + + /** + * Returns whether the world has an ongoing storm. + * + * @return Whether there is an ongoing storm + */ + public boolean hasStorm(); + + /** + * Set whether there is a storm. A duration will be set for the new + * current conditions. + * + * @param hasStorm Whether there is rain and snow + */ + public void setStorm(boolean hasStorm); + + /** + * Get the remaining time in ticks of the current conditions. + * + * @return Time in ticks + */ + public int getWeatherDuration(); + + /** + * Set the remaining time in ticks of the current conditions. + * + * @param duration Time in ticks + */ + public void setWeatherDuration(int duration); + + /** + * Returns whether there is thunder. + * + * @return Whether there is thunder + */ + public boolean isThundering(); + + /** + * Set whether it is thundering. + * + * @param thundering Whether it is thundering + */ + public void setThundering(boolean thundering); + + /** + * Get the thundering duration. + * + * @return Duration in ticks + */ + public int getThunderDuration(); + + /** + * Set the thundering duration. + * + * @param duration Duration in ticks + */ + public void setThunderDuration(int duration); + + /** + * Creates explosion at given coordinates with given power + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @param power The power of explosion, where 4F is TNT + * @return false if explosion was canceled, otherwise true + */ + public boolean createExplosion(double x, double y, double z, float power); + + /** + * Creates explosion at given coordinates with given power and optionally + * setting blocks on fire. + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @param power The power of explosion, where 4F is TNT + * @param setFire Whether or not to set blocks on fire + * @return false if explosion was canceled, otherwise true + */ + public boolean createExplosion(double x, double y, double z, float power, boolean setFire); + + /** + * Creates explosion at given coordinates with given power and optionally + * setting blocks on fire or breaking blocks. + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @param power The power of explosion, where 4F is TNT + * @param setFire Whether or not to set blocks on fire + * @param breakBlocks Whether or not to have blocks be destroyed + * @return false if explosion was canceled, otherwise true + */ + public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks); + + /** + * Creates explosion at given coordinates with given power + * + * @param loc Location to blow up + * @param power The power of explosion, where 4F is TNT + * @return false if explosion was canceled, otherwise true + */ + public boolean createExplosion(Location loc, float power); + + /** + * Creates explosion at given coordinates with given power and optionally + * setting blocks on fire. + * + * @param loc Location to blow up + * @param power The power of explosion, where 4F is TNT + * @param setFire Whether or not to set blocks on fire + * @return false if explosion was canceled, otherwise true + */ + public boolean createExplosion(Location loc, float power, boolean setFire); + + /** + * Gets the {@link Environment} type of this world + * + * @return This worlds Environment type + */ + public Environment getEnvironment(); + + /** + * Gets the Seed for this world. + * + * @return This worlds Seed + */ + public long getSeed(); + + /** + * Gets the current PVP setting for this world. + * + * @return True if PVP is enabled + */ + public boolean getPVP(); + + /** + * Sets the PVP setting for this world. + * + * @param pvp True/False whether PVP should be Enabled. + */ + public void setPVP(boolean pvp); + + /** + * Gets the chunk generator for this world + * + * @return ChunkGenerator associated with this world + */ + public ChunkGenerator getGenerator(); + + /** + * Saves world to disk + */ + public void save(); + + /** + * Gets a list of all applied {@link BlockPopulator}s for this World + * + * @return List containing any or none BlockPopulators + */ + public List getPopulators(); + + /** + * Spawn an entity of a specific class at the given {@link Location} + * + * @param location the {@link Location} to spawn the entity at + * @param clazz the class of the {@link Entity} to spawn + * @param the class of the {@link Entity} to spawn + * @return an instance of the spawned {@link Entity} + * @throws IllegalArgumentException if either parameter is null or the + * {@link Entity} requested cannot be spawned + */ + public T spawn(Location location, Class clazz) throws IllegalArgumentException; + + /** + * Spawn a {@link FallingBlock} entity at the given {@link Location} of + * the specified {@link Material}. The material dictates what is falling. + * When the FallingBlock hits the ground, it will place that block. + *

          + * The Material must be a block type, check with {@link Material#isBlock() + * material.isBlock()}. The Material may not be air. + * + * @param location The {@link Location} to spawn the FallingBlock + * @param material The block {@link Material} type + * @param data The block data + * @return The spawned {@link FallingBlock} instance + * @throws IllegalArgumentException if {@link Location} or {@link + * Material} are null or {@link Material} is not a block + * @deprecated Magic value + */ + @Deprecated + public FallingBlock spawnFallingBlock(Location location, Material material, byte data) throws IllegalArgumentException; + + /** + * Spawn a {@link FallingBlock} entity at the given {@link Location} of + * the specified blockId (converted to {@link Material}) + * + * @param location The {@link Location} to spawn the FallingBlock + * @param blockId The id of the intended material + * @param blockData The block data + * @return The spawned FallingBlock instance + * @throws IllegalArgumentException if location is null, or blockId is + * invalid + * @see #spawnFallingBlock(org.bukkit.Location, org.bukkit.Material, byte) + * @deprecated Magic value + */ + @Deprecated + public FallingBlock spawnFallingBlock(Location location, int blockId, byte blockData) throws IllegalArgumentException; + + /** + * Plays an effect to all players within a default radius around a given + * location. + * + * @param location the {@link Location} around which players must be to + * hear the sound + * @param effect the {@link Effect} + * @param data a data bit needed for some effects + */ + public void playEffect(Location location, Effect effect, int data); + + /** + * Plays an effect to all players within a given radius around a location. + * + * @param location the {@link Location} around which players must be to + * hear the effect + * @param effect the {@link Effect} + * @param data a data bit needed for some effects + * @param radius the radius around the location + */ + public void playEffect(Location location, Effect effect, int data, int radius); + + /** + * Plays an effect to all players within a default radius around a given + * location. + * + * @param location the {@link Location} around which players must be to + * hear the sound + * @param effect the {@link Effect} + * @param data a data bit needed for some effects + */ + public void playEffect(Location location, Effect effect, T data); + + /** + * Plays an effect to all players within a given radius around a location. + * + * @param location the {@link Location} around which players must be to + * hear the effect + * @param effect the {@link Effect} + * @param data a data bit needed for some effects + * @param radius the radius around the location + */ + public void playEffect(Location location, Effect effect, T data, int radius); + + /** + * Get empty chunk snapshot (equivalent to all air blocks), optionally + * including valid biome data. Used for representing an ungenerated chunk, + * or for fetching only biome data without loading a chunk. + * + * @param x - chunk x coordinate + * @param z - chunk z coordinate + * @param includeBiome - if true, snapshot includes per-coordinate biome + * type + * @param includeBiomeTempRain - if true, snapshot includes per-coordinate + * raw biome temperature and rainfall + * @return The empty snapshot. + */ + public ChunkSnapshot getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain); + + /** + * Sets the spawn flags for this. + * + * @param allowMonsters - if true, monsters are allowed to spawn in this + * world. + * @param allowAnimals - if true, animals are allowed to spawn in this + * world. + */ + public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals); + + /** + * Gets whether animals can spawn in this world. + * + * @return whether animals can spawn in this world. + */ + public boolean getAllowAnimals(); + + /** + * Gets whether monsters can spawn in this world. + * + * @return whether monsters can spawn in this world. + */ + public boolean getAllowMonsters(); + + /** + * Gets the biome for the given block coordinates. + * + * @param x X coordinate of the block + * @param z Z coordinate of the block + * @return Biome of the requested block + */ + Biome getBiome(int x, int z); + + /** + * Sets the biome for the given block coordinates + * + * @param x X coordinate of the block + * @param z Z coordinate of the block + * @param bio new Biome type for this block + */ + void setBiome(int x, int z, Biome bio); + + /** + * Gets the temperature for the given block coordinates. + *

          + * It is safe to run this method when the block does not exist, it will + * not create the block. + * + * @param x X coordinate of the block + * @param z Z coordinate of the block + * @return Temperature of the requested block + */ + public double getTemperature(int x, int z); + + /** + * Gets the humidity for the given block coordinates. + *

          + * It is safe to run this method when the block does not exist, it will + * not create the block. + * + * @param x X coordinate of the block + * @param z Z coordinate of the block + * @return Humidity of the requested block + */ + public double getHumidity(int x, int z); + + /** + * Gets the maximum height of this world. + *

          + * If the max height is 100, there are only blocks from y=0 to y=99. + * + * @return Maximum height of the world + */ + public int getMaxHeight(); + + /** + * Gets the sea level for this world. + *

          + * This is often half of {@link #getMaxHeight()} + * + * @return Sea level + */ + public int getSeaLevel(); + + /** + * Gets whether the world's spawn area should be kept loaded into memory + * or not. + * + * @return true if the world's spawn area will be kept loaded into memory. + */ + public boolean getKeepSpawnInMemory(); + + /** + * Sets whether the world's spawn area should be kept loaded into memory + * or not. + * + * @param keepLoaded if true then the world's spawn area will be kept + * loaded into memory. + */ + public void setKeepSpawnInMemory(boolean keepLoaded); + + /** + * Gets whether or not the world will automatically save + * + * @return true if the world will automatically save, otherwise false + */ + public boolean isAutoSave(); + + /** + * Sets whether or not the world will automatically save + * + * @param value true if the world should automatically save, otherwise + * false + */ + public void setAutoSave(boolean value); + + /** + * Sets the Difficulty of the world. + * + * @param difficulty the new difficulty you want to set the world to + */ + public void setDifficulty(Difficulty difficulty); + + /** + * Gets the Difficulty of the world. + * + * @return The difficulty of the world. + */ + public Difficulty getDifficulty(); + + /** + * Gets the folder of this world on disk. + * + * @return The folder of this world. + */ + public File getWorldFolder(); + + /** + * Gets the type of this world. + * + * @return Type of this world. + */ + public WorldType getWorldType(); + + /** + * Gets whether or not structures are being generated. + * + * @return True if structures are being generated. + */ + public boolean canGenerateStructures(); + + /** + * Gets the world's ticks per animal spawns value + *

          + * This value determines how many ticks there are between attempts to + * spawn animals. + *

          + * Example Usage: + *

            + *
          • A value of 1 will mean the server will attempt to spawn animals in + * this world every tick. + *
          • A value of 400 will mean the server will attempt to spawn animals + * in this world every 400th tick. + *
          • A value below 0 will be reset back to Minecraft's default. + *
          + *

          + * Note: + * If set to 0, animal spawning will be disabled for this world. We + * recommend using {@link #setSpawnFlags(boolean, boolean)} to control + * this instead. + *

          + * Minecraft default: 400. + * + * @return The world's ticks per animal spawns value + */ + public long getTicksPerAnimalSpawns(); + + /** + * Sets the world's ticks per animal spawns value + *

          + * This value determines how many ticks there are between attempts to + * spawn animals. + *

          + * Example Usage: + *

            + *
          • A value of 1 will mean the server will attempt to spawn animals in + * this world every tick. + *
          • A value of 400 will mean the server will attempt to spawn animals + * in this world every 400th tick. + *
          • A value below 0 will be reset back to Minecraft's default. + *
          + *

          + * Note: + * If set to 0, animal spawning will be disabled for this world. We + * recommend using {@link #setSpawnFlags(boolean, boolean)} to control + * this instead. + *

          + * Minecraft default: 400. + * + * @param ticksPerAnimalSpawns the ticks per animal spawns value you want + * to set the world to + */ + public void setTicksPerAnimalSpawns(int ticksPerAnimalSpawns); + + /** + * Gets the world's ticks per monster spawns value + *

          + * This value determines how many ticks there are between attempts to + * spawn monsters. + *

          + * Example Usage: + *

            + *
          • A value of 1 will mean the server will attempt to spawn monsters in + * this world every tick. + *
          • A value of 400 will mean the server will attempt to spawn monsters + * in this world every 400th tick. + *
          • A value below 0 will be reset back to Minecraft's default. + *
          + *

          + * Note: + * If set to 0, monsters spawning will be disabled for this world. We + * recommend using {@link #setSpawnFlags(boolean, boolean)} to control + * this instead. + *

          + * Minecraft default: 1. + * + * @return The world's ticks per monster spawns value + */ + public long getTicksPerMonsterSpawns(); + + /** + * Sets the world's ticks per monster spawns value + *

          + * This value determines how many ticks there are between attempts to + * spawn monsters. + *

          + * Example Usage: + *

            + *
          • A value of 1 will mean the server will attempt to spawn monsters in + * this world on every tick. + *
          • A value of 400 will mean the server will attempt to spawn monsters + * in this world every 400th tick. + *
          • A value below 0 will be reset back to Minecraft's default. + *
          + *

          + * Note: + * If set to 0, monsters spawning will be disabled for this world. We + * recommend using {@link #setSpawnFlags(boolean, boolean)} to control + * this instead. + *

          + * Minecraft default: 1. + * + * @param ticksPerMonsterSpawns the ticks per monster spawns value you + * want to set the world to + */ + public void setTicksPerMonsterSpawns(int ticksPerMonsterSpawns); + + /** + * Gets limit for number of monsters that can spawn in a chunk in this + * world + * + * @return The monster spawn limit + */ + int getMonsterSpawnLimit(); + + /** + * Sets the limit for number of monsters that can spawn in a chunk in this + * world + *

          + * Note: If set to a negative number the world will use the + * server-wide spawn limit instead. + */ + void setMonsterSpawnLimit(int limit); + + /** + * Gets the limit for number of animals that can spawn in a chunk in this + * world + * + * @return The animal spawn limit + */ + int getAnimalSpawnLimit(); + + /** + * Sets the limit for number of animals that can spawn in a chunk in this + * world + *

          + * Note: If set to a negative number the world will use the + * server-wide spawn limit instead. + */ + void setAnimalSpawnLimit(int limit); + + /** + * Gets the limit for number of water animals that can spawn in a chunk in + * this world + * + * @return The water animal spawn limit + */ + int getWaterAnimalSpawnLimit(); + + /** + * Sets the limit for number of water animals that can spawn in a chunk in + * this world + *

          + * Note: If set to a negative number the world will use the + * server-wide spawn limit instead. + */ + void setWaterAnimalSpawnLimit(int limit); + + /** + * Gets the limit for number of ambient mobs that can spawn in a chunk in + * this world + * + * @return The ambient spawn limit + */ + int getAmbientSpawnLimit(); + + /** + * Sets the limit for number of ambient mobs that can spawn in a chunk in + * this world + *

          + * Note: If set to a negative number the world will use the + * server-wide spawn limit instead. + */ + void setAmbientSpawnLimit(int limit); + + /** + * Play a Sound at the provided Location in the World + *

          + * This function will fail silently if Location or Sound are null. + * + * @param location The location to play the sound + * @param sound The sound to play + * @param volume The volume of the sound + * @param pitch The pitch of the sound + */ + void playSound(Location location, Sound sound, float volume, float pitch); + + /** + * Get existing rules + * + * @return An array of rules + */ + public String[] getGameRules(); + + /** + * Gets the current state of the specified rule + *

          + * Will return null if rule passed is null + * + * @param rule Rule to look up value of + * @return String value of rule + */ + public String getGameRuleValue(String rule); + + /** + * Set the specified gamerule to specified value. + *

          + * The rule may attempt to validate the value passed, will return true if + * value was set. + *

          + * If rule is null, the function will return false. + * + * @param rule Rule to set + * @param value Value to set rule to + * @return True if rule was set + */ + public boolean setGameRuleValue(String rule, String value); + + /** + * Checks if string is a valid game rule + * + * @param rule Rule to check + * @return True if rule exists + */ + public boolean isGameRule(String rule); + + // Spigot start + public class Spigot + { + + /** + * Plays an effect to all players within a default radius around a given + * location. + * + * @param location the {@link Location} around which players must be to + * see the effect + * @param effect the {@link Effect} + * @throws IllegalArgumentException if the location or effect is null. + * It also throws when the effect requires a material or a material data + */ + public void playEffect(Location location, Effect effect) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Plays an effect to all players within a default radius around a given + * location. The effect will use the provided material (and material + * data if required). The particle's position on the client will be the + * given location, adjusted on each axis by a normal distribution with + * mean 0 and standard deviation given in the offset parameters, each + * particle has independently calculated offsets. The effect will have + * the given speed and particle count if the effect is a particle. Some + * effect will create multiple particles. + * + * @param location the {@link Location} around which players must be to + * see the effect + * @param effect effect the {@link Effect} + * @param id the item/block/data id for the effect + * @param data the data value of the block/item for the effect + * @param offsetX the amount to be randomly offset by in the X axis + * @param offsetY the amount to be randomly offset by in the Y axis + * @param offsetZ the amount to be randomly offset by in the Z axis + * @param speed the speed of the particles + * @param particleCount the number of particles + * @param radius the radius around the location + */ + public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Strikes lightning at the given {@link Location} and possibly without sound + * + * @param loc The location to strike lightning + * @param isSilent Whether this strike makes no sound + * @return The lightning entity. + */ + public LightningStrike strikeLightning(Location loc, boolean isSilent) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Strikes lightning at the given {@link Location} without doing damage and possibly without sound + * + * @param loc The location to strike lightning + * @param isSilent Whether this strike makes no sound + * @return The lightning entity. + */ + public LightningStrike strikeLightningEffect(Location loc, boolean isSilent) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); + // Spigot end + + /** + * Represents various map environment types that a world may be + */ + public enum Environment { + + /** + * Represents the "normal"/"surface world" map + */ + NORMAL(0), + /** + * Represents a nether based map ("hell") + */ + NETHER(-1), + /** + * Represents the "end" map + */ + THE_END(1); + + private final int id; + private static final Map lookup = new HashMap(); + + private Environment(int id) { + this.id = id; + } + + /** + * Gets the dimension ID of this environment + * + * @return dimension ID + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + + /** + * Get an environment by ID + * + * @param id The ID of the environment + * @return The environment + * @deprecated Magic value + */ + @Deprecated + public static Environment getEnvironment(int id) { + return lookup.get(id); + } + + static { + for (Environment env : values()) { + lookup.put(env.getId(), env); + } + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/WorldCreator.java b/vspigot-api/src/main/java/org/bukkit/WorldCreator.java new file mode 100644 index 0000000..9a5afd2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/WorldCreator.java @@ -0,0 +1,295 @@ +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; + + /** + * 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. + *

          + * 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. + *

          + * 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. + *

          + * This may be null, in which case the "natural" generator for this + * environment will be used. + *

          + * 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. + *

          + * This may be null, in which case the "natural" generator for this + * environment will be used. + *

          + * 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 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. + *

          + * 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. + *

          + * If the generator is not found, null will be returned and a message will + * be printed to the specified {@link CommandSender} explaining why. + *

          + * 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; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/WorldType.java b/vspigot-api/src/main/java/org/bukkit/WorldType.java new file mode 100644 index 0000000..201852d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/WorldType.java @@ -0,0 +1,47 @@ +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"); + + private final static Map 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); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Beacon.java b/vspigot-api/src/main/java/org/bukkit/block/Beacon.java new file mode 100644 index 0000000..2de0583 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Beacon.java @@ -0,0 +1,9 @@ +package org.bukkit.block; + +import org.bukkit.inventory.InventoryHolder; + +/** + * Represents a beacon. + */ +public interface Beacon extends BlockState, InventoryHolder { +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Biome.java b/vspigot-api/src/main/java/org/bukkit/block/Biome.java new file mode 100644 index 0000000..8b902f4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Biome.java @@ -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, +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Block.java b/vspigot-api/src/main/java/org/bukkit/block/Block.java new file mode 100644 index 0000000..4a53109 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Block.java @@ -0,0 +1,384 @@ +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 + *

          + * 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 + *

          + * For example, the following method places water at 100,102,100; two + * blocks above 100,100,100. + * + *

          +     * Block block = world.getBlockAt(100, 100, 100);
          +     * Block shower = block.getRelative(BlockFace.UP, 2);
          +     * shower.setType(Material.WATER);
          +     * 
          + * + * @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. + *

          + * 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. + *

          + * 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. + *

          + * If the provided Location is null this method does nothing and returns + * null. + * + * @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-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 + *

          + * For example: + *

          +     * Block current = world.getBlockAt(100, 100, 100);
          +     * Block target = world.getBlockAt(100, 101, 100);
          +     *
          +     * current.getFace(target) == BlockFace.Up;
          +     * 
          + *
          + * 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. + *

          + * 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. + *

          + * 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. + *

          + * 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 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 getDrops(ItemStack tool); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/BlockFace.java b/vspigot-api/src/main/java/org/bukkit/block/BlockFace.java new file mode 100644 index 0000000..58fb195 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/BlockFace.java @@ -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; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/BlockState.java b/vspigot-api/src/main/java/org/bukkit/block/BlockState.java new file mode 100644 index 0000000..ca57173 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/BlockState.java @@ -0,0 +1,194 @@ +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. + *

          + * 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. + *

          + * If the provided Location is null this method does nothing and returns + * null. + * + * @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. + *

          + * 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. + *

          + * 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. + *

          + * 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. + *

          + * 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. + *

          + * 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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/BrewingStand.java b/vspigot-api/src/main/java/org/bukkit/block/BrewingStand.java new file mode 100644 index 0000000..c66a51c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/BrewingStand.java @@ -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(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Chest.java b/vspigot-api/src/main/java/org/bukkit/block/Chest.java new file mode 100644 index 0000000..125d5e7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Chest.java @@ -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(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/CommandBlock.java b/vspigot-api/src/main/java/org/bukkit/block/CommandBlock.java new file mode 100644 index 0000000..85d5345 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/CommandBlock.java @@ -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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/ContainerBlock.java b/vspigot-api/src/main/java/org/bukkit/block/ContainerBlock.java new file mode 100644 index 0000000..c69ac9e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/ContainerBlock.java @@ -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 {} diff --git a/vspigot-api/src/main/java/org/bukkit/block/CreatureSpawner.java b/vspigot-api/src/main/java/org/bukkit/block/CreatureSpawner.java new file mode 100644 index 0000000..e54d997 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/CreatureSpawner.java @@ -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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Dispenser.java b/vspigot-api/src/main/java/org/bukkit/block/Dispenser.java new file mode 100644 index 0000000..bba753e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Dispenser.java @@ -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. + *

          + * 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. + *

          + * If the block is no longer a dispenser, this will return false. + * + * @return true if successful, otherwise false + */ + public boolean dispense(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/DoubleChest.java b/vspigot-api/src/main/java/org/bukkit/block/DoubleChest.java new file mode 100644 index 0000000..148099c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/DoubleChest.java @@ -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()); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Dropper.java b/vspigot-api/src/main/java/org/bukkit/block/Dropper.java new file mode 100644 index 0000000..21fbedc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Dropper.java @@ -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. + *

          + * Normal behavior of a Dropper is as follows: + *

          + * 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. + *

          + * 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(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Furnace.java b/vspigot-api/src/main/java/org/bukkit/block/Furnace.java new file mode 100644 index 0000000..94af85e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Furnace.java @@ -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(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Hopper.java b/vspigot-api/src/main/java/org/bukkit/block/Hopper.java new file mode 100644 index 0000000..e097157 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Hopper.java @@ -0,0 +1,10 @@ +package org.bukkit.block; + +import org.bukkit.inventory.InventoryHolder; + +/** + * Represents a hopper. + */ +public interface Hopper extends BlockState, InventoryHolder { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Jukebox.java b/vspigot-api/src/main/java/org/bukkit/block/Jukebox.java new file mode 100644 index 0000000..7b45b83 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Jukebox.java @@ -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(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/NoteBlock.java b/vspigot-api/src/main/java/org/bukkit/block/NoteBlock.java new file mode 100644 index 0000000..8380068 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/NoteBlock.java @@ -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 + *

          + * 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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/PistonMoveReaction.java b/vspigot-api/src/main/java/org/bukkit/block/PistonMoveReaction.java new file mode 100644 index 0000000..e5279f7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/PistonMoveReaction.java @@ -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 byId = new HashMap(); + 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); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Sign.java b/vspigot-api/src/main/java/org/bukkit/block/Sign.java new file mode 100644 index 0000000..5d7a633 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Sign.java @@ -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. + *

          + * 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. + *

          + * 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; +} diff --git a/vspigot-api/src/main/java/org/bukkit/block/Skull.java b/vspigot-api/src/main/java/org/bukkit/block/Skull.java new file mode 100644 index 0000000..4f4896f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/block/Skull.java @@ -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 + *

          + * 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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/BlockCommandSender.java b/vspigot-api/src/main/java/org/bukkit/command/BlockCommandSender.java new file mode 100644 index 0000000..ce229d2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/BlockCommandSender.java @@ -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(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/Command.java b/vspigot-api/src/main/java/org/bukkit/command/Command.java new file mode 100644 index 0000000..796ca24 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/Command.java @@ -0,0 +1,399 @@ +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.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 final String name; + private String nextLabel; + private String label; + private List aliases; + private List activeAliases; + private CommandMap commandMap = null; + protected String description = ""; + protected String usageMessage; + private String permission; + private String permissionMessage; + public org.spigotmc.CustomTimingsHandler timings; // Spigot + + protected Command(String name) { + this(name, "", "/" + name, new ArrayList()); + } + + protected Command(String name, String description, String usageMessage, List aliases) { + this.name = name; + this.nextLabel = name; + this.label = name; + this.description = description; + this.usageMessage = usageMessage; + this.aliases = aliases; + this.activeAliases = new ArrayList(aliases); + this.timings = new org.spigotmc.CustomTimingsHandler("** Command: " + name); // Spigot + } + + /** + * 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); + + /** + * @deprecated This method is not supported and returns null + */ + @Deprecated + public List 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 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 matchedPlayers = new ArrayList(); + for (Player player : sender.getServer().getOnlinePlayers()) { + String name = player.getDisguisedName(); // MineHQ - Disguises + if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) { + matchedPlayers.add(name); + } + } + + Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER); + return matchedPlayers; + } + + /** + * Returns the name of this command + * + * @return Name of this command + */ + public String getName() { + return name; + } + + /** + * 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. + *

          + * 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).split("\n")) { + target.sendMessage(line); + } + } + + return false; + } + + /** + * Tests the given {@link CommandSender} to see if they can perform this + * command. + *

          + * 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 current label for this command + * + * @return Label of this command or null if not registered + */ + public String getLabel() { + return label; + } + + /** + * Sets the label of this command. + *

          + * If the command is currently registered the label change will only take + * effect after its been re-registered e.g. after a /reload + * + * @param name The command's name + * @return returns true if the name change happened instantly or false if + * it was scheduled for re-registration + */ + public boolean setLabel(String name) { + this.nextLabel = name; + if (!isRegistered()) { + this.timings = new org.spigotmc.CustomTimingsHandler("** Command: " + name); // Spigot + 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(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 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 + * `aliases' node) is equivalent to this method. + * + * @param aliases aliases to register to this command + * @return this command object, for chaining + */ + public Command setAliases(List aliases) { + this.aliases = aliases; + if (!isRegistered()) { + this.activeAliases = new ArrayList(aliases); + } + return this; + } + + /** + * Sets a brief description of this command. Defining a description in the + * {@link PluginDescriptionFile#getCommands()} (under the + * `description' 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 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 + ')'; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/CommandException.java b/vspigot-api/src/main/java/org/bukkit/command/CommandException.java new file mode 100644 index 0000000..b63015f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/CommandException.java @@ -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 CommandException without detail + * message. + */ + public CommandException() {} + + /** + * Constructs an instance of CommandException 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); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/CommandExecutor.java b/vspigot-api/src/main/java/org/bukkit/command/CommandExecutor.java new file mode 100644 index 0000000..c75586f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/CommandExecutor.java @@ -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); +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/CommandMap.java b/vspigot-api/src/main/java/org/bukkit/command/CommandMap.java new file mode 100644 index 0000000..e7e20d8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/CommandMap.java @@ -0,0 +1,109 @@ +package org.bukkit.command; + +import java.util.List; + +public interface CommandMap { + + /** + * Registers all the commands belonging to a certain plugin. + *

          + * Caller can use:- + *

            + *
          • command.getName() to determine the label registered for this + * command + *
          • command.getAliases() to determine the aliases which where + * registered + *
          + * + * @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 commands); + + /** + * Registers a command. Returns true on success; false if name is already + * taken and fallback had to be used. + *

          + * Caller can use:- + *

            + *
          • command.getName() to determine the label registered for this + * command + *
          • command.getAliases() to determine the aliases which where + * registered + *
          + * + * @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. + *

          + * Caller can use:- + *

            + *
          • command.getName() to determine the label registered for this + * command + *
          • command.getAliases() to determine the aliases which where + * registered + *
          + * + * @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 tabComplete(CommandSender sender, String cmdLine) throws IllegalArgumentException; +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/CommandSender.java b/vspigot-api/src/main/java/org/bukkit/command/CommandSender.java new file mode 100644 index 0000000..148756b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/CommandSender.java @@ -0,0 +1,35 @@ +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(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/ConsoleCommandSender.java b/vspigot-api/src/main/java/org/bukkit/command/ConsoleCommandSender.java new file mode 100644 index 0000000..f309c2e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/ConsoleCommandSender.java @@ -0,0 +1,6 @@ +package org.bukkit.command; + +import org.bukkit.conversations.Conversable; + +public interface ConsoleCommandSender extends CommandSender, Conversable { +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/FormattedCommandAlias.java b/vspigot-api/src/main/java/org/bukkit/command/FormattedCommandAlias.java new file mode 100644 index 0000000..3f07d7f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/FormattedCommandAlias.java @@ -0,0 +1,124 @@ +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); + this.formatStrings = formatStrings; + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + boolean result = false; + ArrayList commands = new ArrayList(); + 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; + } + + private static boolean inRange(int i, int j, int k) { + return i >= j && i <= k; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java b/vspigot-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java new file mode 100644 index 0000000..a0a4129 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java @@ -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; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/PluginCommand.java b/vspigot-api/src/main/java/org/bukkit/command/PluginCommand.java new file mode 100644 index 0000000..3bfa31f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/PluginCommand.java @@ -0,0 +1,160 @@ +package org.bukkit.command; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.Plugin; + +/** + * 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("", 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. + *

          + * 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} + *

          + * Delegates to the tab completer if present. + *

          + * 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[])}. + *

          + * 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 tabComplete(CommandSender sender, String alias, String[] args) 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 completions = null; + try { + if (completer != null) { + completions = completer.onTabComplete(sender, this, alias, args); + } + if (completions == null && executor instanceof TabCompleter) { + completions = ((TabCompleter) executor).onTabComplete(sender, this, alias, args); + } + } 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; + } + + @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(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/PluginCommandYamlParser.java b/vspigot-api/src/main/java/org/bukkit/command/PluginCommandYamlParser.java new file mode 100644 index 0000000..5854583 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/PluginCommandYamlParser.java @@ -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 parse(Plugin plugin) { + List pluginCmds = new ArrayList(); + + Map> map = plugin.getDescription().getCommands(); + + if (map == null) { + return pluginCmds; + } + + for (Entry> 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 aliasList = new ArrayList(); + + 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; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/PluginIdentifiableCommand.java b/vspigot-api/src/main/java/org/bukkit/command/PluginIdentifiableCommand.java new file mode 100644 index 0000000..c5e0d2c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/PluginIdentifiableCommand.java @@ -0,0 +1,19 @@ +package org.bukkit.command; + +import org.bukkit.plugin.Plugin; + +/** + * This interface is used by the help system to group commands into + * sub-indexes based on the {@link Plugin} they are a part of. Custom command + * implementations will need to implement this interface to have a sub-index + * automatically generated on the plugin's behalf. + */ +public interface PluginIdentifiableCommand { + + /** + * Gets the owner of this PluginIdentifiableCommand. + * + * @return Plugin that owns this PluginIdentifiableCommand. + */ + public Plugin getPlugin(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/RemoteConsoleCommandSender.java b/vspigot-api/src/main/java/org/bukkit/command/RemoteConsoleCommandSender.java new file mode 100644 index 0000000..dc3bc1d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/RemoteConsoleCommandSender.java @@ -0,0 +1,4 @@ +package org.bukkit.command; + +public interface RemoteConsoleCommandSender extends CommandSender { +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/SimpleCommandMap.java b/vspigot-api/src/main/java/org/bukkit/command/SimpleCommandMap.java new file mode 100644 index 0000000..f15b95d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/SimpleCommandMap.java @@ -0,0 +1,305 @@ +package org.bukkit.command; + +import static org.bukkit.util.Java15Compat.Arrays_copyOfRange; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.commons.lang.Validate; +import org.bukkit.Server; +import org.bukkit.command.defaults.*; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +public class SimpleCommandMap implements CommandMap { + private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL); + protected final Map knownCommands = new HashMap(); + private final Server server; + + public SimpleCommandMap(final Server server) { + this.server = server; + setDefaultCommands(); + } + + private void setDefaultCommands() { + register("bukkit", new SaveCommand()); + register("bukkit", new SaveOnCommand()); + register("bukkit", new SaveOffCommand()); + register("bukkit", new StopCommand()); + register("bukkit", new VersionCommand("version")); + register("bukkit", new ReloadCommand("reload")); + register("bukkit", new PluginsCommand("plugins")); + register("bukkit", new TimingsCommand("timings")); + } + + public void setFallbackCommands() { + register("bukkit", new ListCommand()); + register("bukkit", new OpCommand()); + register("bukkit", new DeopCommand()); + register("bukkit", new BanIpCommand()); + register("bukkit", new PardonIpCommand()); + register("bukkit", new BanCommand()); + register("bukkit", new PardonCommand()); + register("bukkit", new KickCommand()); + register("bukkit", new TeleportCommand()); + register("bukkit", new GiveCommand()); + register("bukkit", new TimeCommand()); + register("bukkit", new SayCommand()); + register("bukkit", new WhitelistCommand()); + register("bukkit", new TellCommand()); + register("bukkit", new MeCommand()); + register("bukkit", new KillCommand()); + register("bukkit", new GameModeCommand()); + register("bukkit", new HelpCommand()); + register("bukkit", new ExpCommand()); + register("bukkit", new ToggleDownfallCommand()); + register("bukkit", new BanListCommand()); + register("bukkit", new DefaultGameModeCommand()); + register("bukkit", new SeedCommand()); + register("bukkit", new DifficultyCommand()); + register("bukkit", new WeatherCommand()); + register("bukkit", new SpawnpointCommand()); + register("bukkit", new ClearCommand()); + register("bukkit", new GameRuleCommand()); + register("bukkit", new EnchantCommand()); + register("bukkit", new TestForCommand()); + register("bukkit", new EffectCommand()); + register("bukkit", new ScoreboardCommand()); + register("bukkit", new PlaySoundCommand()); + register("bukkit", new SpreadPlayersCommand()); + register("bukkit", new SetWorldSpawnCommand()); + register("bukkit", new SetIdleTimeoutCommand()); + register("bukkit", new AchievementCommand()); + } + + /** + * {@inheritDoc} + */ + public void registerAll(String fallbackPrefix, List commands) { + if (commands != null) { + for (Command c : commands) { + register(fallbackPrefix, c); + } + } + } + + /** + * {@inheritDoc} + */ + public boolean register(String fallbackPrefix, Command command) { + return register(command.getName(), fallbackPrefix, command); + } + + /** + * {@inheritDoc} + */ + public boolean register(String label, String fallbackPrefix, Command command) { + label = label.toLowerCase().trim(); + fallbackPrefix = fallbackPrefix.toLowerCase().trim(); + boolean registered = register(label, command, false, fallbackPrefix); + + Iterator iterator = command.getAliases().iterator(); + while (iterator.hasNext()) { + if (!register(iterator.next(), command, true, fallbackPrefix)) { + iterator.remove(); + } + } + + // If we failed to register under the real name, we need to set the command label to the direct address + if (!registered) { + command.setLabel(fallbackPrefix + ":" + label); + } + + // Register to us so further updates of the commands label and aliases are postponed until its reregistered + command.register(this); + + return registered; + } + + /** + * Registers a command with the given name is possible. Also uses + * fallbackPrefix to create a unique name. + * + * @param label the name of the command, without the '/'-prefix. + * @param command the command to register + * @param isAlias whether the command is an alias + * @param fallbackPrefix a prefix which is prepended to the command for a + * unique address + * @return true if command was registered, false otherwise. + */ + private synchronized boolean register(String label, Command command, boolean isAlias, String fallbackPrefix) { + knownCommands.put(fallbackPrefix + ":" + label, command); + if ((command instanceof VanillaCommand || isAlias) && knownCommands.containsKey(label)) { + // Request is for an alias/fallback command and it conflicts with + // a existing command or previous alias ignore it + // Note: This will mean it gets removed from the commands list of active aliases + return false; + } + + boolean registered = true; + + // If the command exists but is an alias we overwrite it, otherwise we return + Command conflict = knownCommands.get(label); + if (conflict != null && conflict.getLabel().equals(label)) { + return false; + } + + if (!isAlias) { + command.setLabel(label); + } + knownCommands.put(label, command); + + return registered; + } + + /** + * {@inheritDoc} + */ + public boolean dispatch(CommandSender sender, String commandLine) throws CommandException { + String[] args = PATTERN_ON_SPACE.split(commandLine); + + if (args.length == 0) { + return false; + } + + String sentCommandLabel = args[0].toLowerCase(); + Command target = getCommand(sentCommandLabel); + + if (target == null) { + return false; + } + + try { + target.timings.startTiming(); // Spigot + // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) + target.execute(sender, sentCommandLabel, Arrays_copyOfRange(args, 1, args.length)); + target.timings.stopTiming(); // Spigot + } catch (CommandException ex) { + target.timings.stopTiming(); // Spigot + throw ex; + } catch (Throwable ex) { + target.timings.stopTiming(); // Spigot + throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex); + } + + // return true as command was handled + return true; + } + + public synchronized void clearCommands() { + for (Map.Entry entry : knownCommands.entrySet()) { + entry.getValue().unregister(this); + } + knownCommands.clear(); + setDefaultCommands(); + } + + public Command getCommand(String name) { + Command target = knownCommands.get(name.toLowerCase()); + return target; + } + + public List tabComplete(CommandSender sender, String cmdLine) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(cmdLine, "Command line cannot null"); + + int spaceIndex = cmdLine.indexOf(' '); + + if (spaceIndex == -1) { + ArrayList completions = new ArrayList(); + Map knownCommands = this.knownCommands; + + final String prefix = (sender instanceof Player ? "/" : ""); + + for (Map.Entry commandEntry : knownCommands.entrySet()) { + Command command = commandEntry.getValue(); + + if (!command.testPermissionSilent(sender)) { + continue; + } + + String name = commandEntry.getKey(); // Use the alias, not command name + + if (StringUtil.startsWithIgnoreCase(name, cmdLine)) { + completions.add(prefix + name); + } + } + + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); + return completions; + } + + String commandName = cmdLine.substring(0, spaceIndex); + Command target = getCommand(commandName); + + if (target == null) { + return null; + } + + if (!target.testPermissionSilent(sender)) { + return null; + } + + String argLine = cmdLine.substring(spaceIndex + 1, cmdLine.length()); + String[] args = PATTERN_ON_SPACE.split(argLine, -1); + + try { + return target.tabComplete(sender, commandName, args); + } catch (CommandException ex) { + throw ex; + } catch (Throwable ex) { + throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex); + } + } + + public Collection getCommands() { + return Collections.unmodifiableCollection(knownCommands.values()); + } + + public void registerServerAliases() { + Map values = server.getCommandAliases(); + + for (String alias : values.keySet()) { + if (alias.contains(":") || alias.contains(" ")) { + server.getLogger().warning("Could not register alias " + alias + " because it contains illegal characters"); + continue; + } + + String[] commandStrings = values.get(alias); + List targets = new ArrayList(); + StringBuilder bad = new StringBuilder(); + + for (String commandString : commandStrings) { + String[] commandArgs = commandString.split(" "); + Command command = getCommand(commandArgs[0]); + + if (command == null) { + if (bad.length() > 0) { + bad.append(", "); + } + bad.append(commandString); + } else { + targets.add(commandString); + } + } + + if (bad.length() > 0) { + server.getLogger().warning("Could not register alias " + alias + " because it contains commands that do not exist: " + bad); + continue; + } + + // We register these as commands so they have absolute priority. + if (targets.size() > 0) { + knownCommands.put(alias.toLowerCase(), new FormattedCommandAlias(alias.toLowerCase(), targets.toArray(new String[targets.size()]))); + } else { + knownCommands.remove(alias.toLowerCase()); + } + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/TabCommandExecutor.java b/vspigot-api/src/main/java/org/bukkit/command/TabCommandExecutor.java new file mode 100644 index 0000000..d24d795 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/TabCommandExecutor.java @@ -0,0 +1,16 @@ +package org.bukkit.command; + +import java.util.List; + +/** + * Represents a class which can handle command tab completion and commands + * + * @deprecated Remains for plugins that would have implemented it even without + * functionality + * @see TabExecutor + */ +@Deprecated +public interface TabCommandExecutor extends CommandExecutor { + public List onTabComplete(); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/TabCompleter.java b/vspigot-api/src/main/java/org/bukkit/command/TabCompleter.java new file mode 100644 index 0000000..6d61e3a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/TabCompleter.java @@ -0,0 +1,22 @@ +package org.bukkit.command; + +import java.util.List; + +/** + * Represents a class which can suggest tab completions for commands. + */ +public interface TabCompleter { + + /** + * Requests a list of possible completions for a command argument. + * + * @param sender Source of the command + * @param command Command which was executed + * @param alias The alias used + * @param args The arguments passed to the command, including final + * partial argument to be completed and command label + * @return A List of possible completions for the final argument, or null + * to default to the command executor + */ + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args); +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/TabExecutor.java b/vspigot-api/src/main/java/org/bukkit/command/TabExecutor.java new file mode 100644 index 0000000..6b8e3fb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/TabExecutor.java @@ -0,0 +1,8 @@ +package org.bukkit.command; + +/** + * This class is provided as a convenience to implement both TabCompleter and + * CommandExecutor. + */ +public interface TabExecutor extends TabCompleter, CommandExecutor { +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/AchievementCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/AchievementCommand.java new file mode 100644 index 0000000..d490732 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/AchievementCommand.java @@ -0,0 +1,187 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Achievement; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Statistic; +import org.bukkit.Material; +import org.bukkit.Statistic.Type; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerAchievementAwardedEvent; +import org.bukkit.event.player.PlayerStatisticIncrementEvent; + +import com.google.common.collect.ImmutableList; + +public class AchievementCommand extends VanillaCommand { + public AchievementCommand() { + super("achievement"); + this.description = "Gives the specified player an achievement or changes a statistic value. Use '*' to give all achievements."; + this.usageMessage = "/achievement give [player]"; + this.setPermission("bukkit.command.achievement"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + if (!args[0].equalsIgnoreCase("give")) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + String statisticString = args[1]; + Player player = null; + + if (args.length > 2) { + player = Bukkit.getPlayer(args[1]); + } else if (sender instanceof Player) { + player = (Player) sender; + } + + if (player == null) { + sender.sendMessage("You must specify which player you wish to perform this action on."); + return true; + } + + if (statisticString.equals("*")) { + for (Achievement achievement : Achievement.values()) { + if (player.hasAchievement(achievement)) { + continue; + } + PlayerAchievementAwardedEvent event = new PlayerAchievementAwardedEvent(player, achievement); + Bukkit.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + player.awardAchievement(achievement); + } + } + Command.broadcastCommandMessage(sender, String.format("Successfully given all achievements to %s", player.getName())); + return true; + } + + Achievement achievement = Bukkit.getUnsafe().getAchievementFromInternalName(statisticString); + Statistic statistic = Bukkit.getUnsafe().getStatisticFromInternalName(statisticString); + + if (achievement != null) { + if (player.hasAchievement(achievement)) { + sender.sendMessage(String.format("%s already has achievement %s", player.getName(), statisticString)); + return true; + } + + PlayerAchievementAwardedEvent event = new PlayerAchievementAwardedEvent(player, achievement); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + sender.sendMessage(String.format("Unable to award %s the achievement %s", player.getName(), statisticString)); + return true; + } + player.awardAchievement(achievement); + + Command.broadcastCommandMessage(sender, String.format("Successfully given %s the stat %s", player.getName(), statisticString)); + return true; + } + + if (statistic == null) { + sender.sendMessage(String.format("Unknown achievement or statistic '%s'", statisticString)); + return true; + } + + if (statistic.getType() == Type.UNTYPED) { + PlayerStatisticIncrementEvent event = new PlayerStatisticIncrementEvent(player, statistic, player.getStatistic(statistic), player.getStatistic(statistic) + 1); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + sender.sendMessage(String.format("Unable to increment %s for %s", statisticString, player.getName())); + return true; + } + player.incrementStatistic(statistic); + Command.broadcastCommandMessage(sender, String.format("Successfully given %s the stat %s", player.getName(), statisticString)); + return true; + } + + if (statistic.getType() == Type.ENTITY) { + EntityType entityType = EntityType.fromName(statisticString.substring(statisticString.lastIndexOf(".") + 1)); + + if (entityType == null) { + sender.sendMessage(String.format("Unknown achievement or statistic '%s'", statisticString)); + return true; + } + + PlayerStatisticIncrementEvent event = new PlayerStatisticIncrementEvent(player, statistic, player.getStatistic(statistic), player.getStatistic(statistic) + 1, entityType); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + sender.sendMessage(String.format("Unable to increment %s for %s", statisticString, player.getName())); + return true; + } + + try { + player.incrementStatistic(statistic, entityType); + } catch (IllegalArgumentException e) { + sender.sendMessage(String.format("Unknown achievement or statistic '%s'", statisticString)); + return true; + } + } else { + int id; + try { + id = getInteger(sender, statisticString.substring(statisticString.lastIndexOf(".") + 1), 0, Integer.MAX_VALUE, true); + } catch (NumberFormatException e) { + sender.sendMessage(e.getMessage()); + return true; + } + + Material material = Material.getMaterial(id); + + if (material == null) { + sender.sendMessage(String.format("Unknown achievement or statistic '%s'", statisticString)); + return true; + } + + PlayerStatisticIncrementEvent event = new PlayerStatisticIncrementEvent(player, statistic, player.getStatistic(statistic), player.getStatistic(statistic) + 1, material); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + sender.sendMessage(String.format("Unable to increment %s for %s", statisticString, player.getName())); + return true; + } + + try { + player.incrementStatistic(statistic, material); + } catch (IllegalArgumentException e) { + sender.sendMessage(String.format("Unknown achievement or statistic '%s'", statisticString)); + return true; + } + } + + Command.broadcastCommandMessage(sender, String.format("Successfully given %s the stat %s", player.getName(), statisticString)); + return true; + } + + @Override + public List 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 == 1) { + return Arrays.asList("give"); + } + + if (args.length == 2) { + return Bukkit.getUnsafe().tabCompleteInternalStatisticOrAchievementName(args[1], new ArrayList()); + } + + if (args.length == 3) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/BanCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/BanCommand.java new file mode 100644 index 0000000..df891b8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/BanCommand.java @@ -0,0 +1,55 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class BanCommand extends VanillaCommand { + public BanCommand() { + super("ban"); + this.description = "Prevents the specified player from using this server"; + this.usageMessage = "/ban [reason ...]"; + this.setPermission("bukkit.command.ban.player"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + String reason = args.length > 0 ? StringUtils.join(args, ' ', 1, args.length) : null; + Bukkit.getBanList(BanList.Type.NAME).addBan(args[0], reason, null, sender.getName()); + + Player player = Bukkit.getPlayer(args[0]); + if (player != null) { + player.kickPlayer("Banned by admin."); + } + + Command.broadcastCommandMessage(sender, "Banned player " + args[0]); + return true; + } + + @Override + public List 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 >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/BanIpCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/BanIpCommand.java new file mode 100644 index 0000000..afb3c6f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/BanIpCommand.java @@ -0,0 +1,77 @@ +package org.bukkit.command.defaults; + +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class BanIpCommand extends VanillaCommand { + public static final Pattern ipValidity = Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); + + public BanIpCommand() { + super("ban-ip"); + this.description = "Prevents the specified IP address from using this server"; + this.usageMessage = "/ban-ip [reason ...]"; + this.setPermission("bukkit.command.ban.ip"); + } + + @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 false; + } + + String reason = args.length > 0 ? StringUtils.join(args, ' ', 1, args.length) : null; + + if (ipValidity.matcher(args[0]).matches()) { + processIPBan(args[0], sender, reason); + } else { + Player player = Bukkit.getPlayer(args[0]); + + if (player == null) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + processIPBan(player.getAddress().getAddress().getHostAddress(), sender, reason); + } + + return true; + } + + private void processIPBan(String ip, CommandSender sender, String reason) { + Bukkit.getBanList(BanList.Type.IP).addBan(ip, reason, null, sender.getName()); + + // Find all matching players and kick + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.getAddress().getAddress().getHostAddress().equals(ip)) { + player.kickPlayer("You have been IP banned."); + } + } + + Command.broadcastCommandMessage(sender, "Banned IP Address 127.0.0.1"); + } + + @Override + public List 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 == 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/BanListCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/BanListCommand.java new file mode 100644 index 0000000..dfab7bc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/BanListCommand.java @@ -0,0 +1,73 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang.Validate; +import org.bukkit.BanEntry; +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class BanListCommand extends VanillaCommand { + private static final List BANLIST_TYPES = ImmutableList.of("ips", "players"); + + public BanListCommand() { + super("banlist"); + this.description = "View all players banned from this server"; + this.usageMessage = "/banlist [ips|players]"; + this.setPermission("bukkit.command.ban.list"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + BanList.Type banType = BanList.Type.NAME; + if (args.length > 0) { + if (args[0].equalsIgnoreCase("ips")) { + banType = BanList.Type.IP; + } else if (!args[0].equalsIgnoreCase("players")) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + } + + StringBuilder message = new StringBuilder(); + BanEntry[] banlist = Bukkit.getBanList(banType).getBanEntries().toArray(new BanEntry[0]); + + for (int x = 0; x < banlist.length; x++) { + if (x != 0) { + if (x == banlist.length - 1) { + message.append(" and "); + } else { + message.append(", "); + } + } + + message.append(banlist[x].getTarget()); + } + + sender.sendMessage("There are " + banlist.length + " total banned players:"); + sender.sendMessage(message.toString()); + return true; + } + + @Override + public List 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], BANLIST_TYPES, new ArrayList(BANLIST_TYPES.size())); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/BukkitCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/BukkitCommand.java new file mode 100644 index 0000000..23c8580 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/BukkitCommand.java @@ -0,0 +1,15 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.bukkit.command.Command; + +public abstract class BukkitCommand extends Command { + protected BukkitCommand(String name) { + super(name); + } + + protected BukkitCommand(String name, String description, String usageMessage, List aliases) { + super(name, description, usageMessage, aliases); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/ClearCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/ClearCommand.java new file mode 100644 index 0000000..a9df4f9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/ClearCommand.java @@ -0,0 +1,114 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ClearCommand extends VanillaCommand { + private static List materials; + static { + ArrayList materialList = new ArrayList(); + for (Material material : Material.values()) { + materialList.add(material.name()); + } + Collections.sort(materialList); + materials = ImmutableList.copyOf(materialList); + } + + public ClearCommand() { + super("clear"); + this.description = "Clears the player's inventory. Can specify item and data filters too."; + this.usageMessage = "/clear [item] [data]"; + this.setPermission("bukkit.command.clear"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + Player player = null; + if (args.length > 0) { + player = Bukkit.getPlayer(args[0]); + } else if (sender instanceof Player) { + player = (Player) sender; + } + + if (player != null) { + int id; + + if (args.length > 1 && !(args[1].equals("-1"))) { + Material material = Material.matchMaterial(args[1]); + if (material == null) { + sender.sendMessage(ChatColor.RED + "There's no item called " + args[1]); + return false; + } + + id = material.getId(); + } else { + id = -1; + } + + int data = args.length >= 3 ? getInteger(sender, args[2], 0) : -1; + int count = player.getInventory().clear(id, data); + + Command.broadcastCommandMessage(sender, "Cleared the inventory of " + player.getDisplayName() + ", removing " + count + " items"); + } else if (args.length == 0) { + sender.sendMessage(ChatColor.RED + "Please provide a player!"); + } else { + sender.sendMessage(ChatColor.RED + "Can't find player " + args[0]); + } + + return true; + } + + @Override + public List 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 == 1) { + return super.tabComplete(sender, alias, args); + } + if (args.length == 2) { + final String arg = args[1]; + final List materials = ClearCommand.materials; + List completion = null; + + final int size = materials.size(); + int i = Collections.binarySearch(materials, arg, String.CASE_INSENSITIVE_ORDER); + + if (i < 0) { + // Insertion (start) index + i = -1 - i; + } + + for ( ; i < size; i++) { + String material = materials.get(i); + if (StringUtil.startsWithIgnoreCase(material, arg)) { + if (completion == null) { + completion = new ArrayList(); + } + completion.add(material); + } else { + break; + } + } + + if (completion != null) { + return completion; + } + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java new file mode 100644 index 0000000..77ba678 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java @@ -0,0 +1,70 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class DefaultGameModeCommand extends VanillaCommand { + private static final List GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival"); + + public DefaultGameModeCommand() { + super("defaultgamemode"); + this.description = "Set the default gamemode"; + this.usageMessage = "/defaultgamemode "; + this.setPermission("bukkit.command.defaultgamemode"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + if (args.length == 0) { + sender.sendMessage("Usage: " + usageMessage); + return false; + } + + String modeArg = args[0]; + int value = -1; + + try { + value = Integer.parseInt(modeArg); + } catch (NumberFormatException ex) {} + + GameMode mode = GameMode.getByValue(value); + + if (mode == null) { + if (modeArg.equalsIgnoreCase("creative") || modeArg.equalsIgnoreCase("c")) { + mode = GameMode.CREATIVE; + } else if (modeArg.equalsIgnoreCase("adventure") || modeArg.equalsIgnoreCase("a")) { + mode = GameMode.ADVENTURE; + } else { + mode = GameMode.SURVIVAL; + } + } + + Bukkit.getServer().setDefaultGameMode(mode); + Command.broadcastCommandMessage(sender, "Default game mode set to " + mode.toString().toLowerCase()); + + return true; + } + + @Override + public List 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], GAMEMODE_NAMES, new ArrayList(GAMEMODE_NAMES.size())); + } + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/DeopCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/DeopCommand.java new file mode 100644 index 0000000..c46750a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/DeopCommand.java @@ -0,0 +1,62 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class DeopCommand extends VanillaCommand { + public DeopCommand() { + super("deop"); + this.description = "Takes the specified player's operator status"; + this.usageMessage = "/deop "; + this.setPermission("bukkit.command.op.take"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length != 1 || args[0].length() == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + OfflinePlayer player = Bukkit.getOfflinePlayer(args[0]); + player.setOp(false); + + if (player instanceof Player) { + ((Player) player).sendMessage(ChatColor.YELLOW + "You are no longer op!"); + } + + Command.broadcastCommandMessage(sender, "De-opped " + args[0]); + return true; + } + + @Override + public List 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 == 1) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getOperators()) { + String playerName = player.getName(); + if (StringUtil.startsWithIgnoreCase(playerName, args[0])) { + completions.add(playerName); + } + } + return completions; + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/DifficultyCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/DifficultyCommand.java new file mode 100644 index 0000000..ca98559 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/DifficultyCommand.java @@ -0,0 +1,81 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; +import org.bukkit.Difficulty; + +import java.util.ArrayList; +import java.util.List; + +public class DifficultyCommand extends VanillaCommand { + private static final List DIFFICULTY_NAMES = ImmutableList.of("peaceful", "easy", "normal", "hard"); + + public DifficultyCommand() { + super("difficulty"); + this.description = "Sets the game difficulty"; + this.usageMessage = "/difficulty "; + this.setPermission("bukkit.command.difficulty"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length != 1 || args[0].length() == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + Difficulty difficulty = Difficulty.getByValue(getDifficultyForString(sender, args[0])); + + if (Bukkit.isHardcore()) { + difficulty = Difficulty.HARD; + } + + Bukkit.getWorlds().get(0).setDifficulty(difficulty); + + int levelCount = 1; + if (Bukkit.getAllowNether()) { + Bukkit.getWorlds().get(levelCount).setDifficulty(difficulty); + levelCount++; + } + + if (Bukkit.getAllowEnd()) { + Bukkit.getWorlds().get(levelCount).setDifficulty(difficulty); + } + + Command.broadcastCommandMessage(sender, "Set difficulty to " + difficulty.toString()); + return true; + } + + protected int getDifficultyForString(CommandSender sender, String name) { + if (name.equalsIgnoreCase("peaceful") || name.equalsIgnoreCase("p")) { + return 0; + } else if (name.equalsIgnoreCase("easy") || name.equalsIgnoreCase("e")) { + return 1; + } else if (name.equalsIgnoreCase("normal") || name.equalsIgnoreCase("n")) { + return 2; + } else if (name.equalsIgnoreCase("hard") || name.equalsIgnoreCase("h")) { + return 3; + } else { + return getInteger(sender, name, 0, 3); + } + } + + @Override + public List 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], DIFFICULTY_NAMES, new ArrayList(DIFFICULTY_NAMES.size())); + } + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/EffectCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/EffectCommand.java new file mode 100644 index 0000000..7d0662e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/EffectCommand.java @@ -0,0 +1,119 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.List; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.StringUtil; + +public class EffectCommand extends VanillaCommand { + private static final List effects; + + public EffectCommand() { + super("effect"); + this.description = "Adds/Removes effects on players"; + this.usageMessage = "/effect [seconds] [amplifier]"; + this.setPermission("bukkit.command.effect"); + } + + static { + ImmutableList.Builder builder = ImmutableList.builder(); + + for (PotionEffectType type : PotionEffectType.values()) { + if (type != null) { + builder.add(type.getName()); + } + } + + effects = builder.build(); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) { + return true; + } + + if (args.length < 2) { + sender.sendMessage(getUsage()); + return true; + } + + final Player player = sender.getServer().getPlayer(args[0]); + + if (player == null) { + sender.sendMessage(ChatColor.RED + String.format("Player, %s, not found", args[0])); + return true; + } + + if ("clear".equalsIgnoreCase(args[1])) { + for (PotionEffect effect : player.getActivePotionEffects()) { + player.removePotionEffect(effect.getType()); + } + sender.sendMessage(String.format("Took all effects from %s", args[0])); + return true; + } + + PotionEffectType effect = PotionEffectType.getByName(args[1]); + + if (effect == null) { + effect = PotionEffectType.getById(getInteger(sender, args[1], 0)); + } + + if (effect == null) { + sender.sendMessage(ChatColor.RED + String.format("Effect, %s, not found", args[1])); + return true; + } + + int duration = 600; + int duration_temp = 30; + int amplification = 0; + + if (args.length >= 3) { + duration_temp = getInteger(sender, args[2], 0, 1000000); + if (effect.isInstant()) { + duration = duration_temp; + } else { + duration = duration_temp * 20; + } + } else if (effect.isInstant()) { + duration = 1; + } + + if (args.length >= 4) { + amplification = getInteger(sender, args[3], 0, 255); + } + + if (duration_temp == 0) { + if (!player.hasPotionEffect(effect)) { + sender.sendMessage(String.format("Couldn't take %s from %s as they do not have the effect", effect.getName(), args[0])); + return true; + } + + player.removePotionEffect(effect); + broadcastCommandMessage(sender, String.format("Took %s from %s", effect.getName(), args[0])); + } else { + final PotionEffect applyEffect = new PotionEffect(effect, duration, amplification); + + player.addPotionEffect(applyEffect, true); + broadcastCommandMessage(sender, String.format("Given %s (ID %d) * %d to %s for %d seconds", effect.getName(), effect.getId(), amplification, args[0], duration_temp)); + } + + return true; + } + + @Override + public List tabComplete(CommandSender sender, String commandLabel, String[] args) { + if (args.length == 1) { + return super.tabComplete(sender, commandLabel, args); + } else if (args.length == 2) { + return StringUtil.copyPartialMatches(args[1], effects, new ArrayList(effects.size())); + } + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/EnchantCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/EnchantCommand.java new file mode 100644 index 0000000..2976649 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/EnchantCommand.java @@ -0,0 +1,169 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.WordUtils; +import com.google.common.collect.ImmutableList; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.StringUtil; + +public class EnchantCommand extends VanillaCommand { + private static final List ENCHANTMENT_NAMES = new ArrayList(); + + public EnchantCommand() { + super("enchant"); + this.description = "Adds enchantments to the item the player is currently holding. Specify 0 for the level to remove an enchantment. Specify force to ignore normal enchantment restrictions"; + this.usageMessage = "/enchant [level|max|0] [force]"; + this.setPermission("bukkit.command.enchant"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + boolean force = false; + if (args.length > 2) { + force = args[args.length > 3 ? 3 : 2].equalsIgnoreCase("force"); + } + + Player player = Bukkit.getPlayerExact(args[0]); + if (player == null) { + sender.sendMessage("Can't find player " + args[0]); + } else { + ItemStack item = player.getItemInHand(); + if (item.getType() == Material.AIR) { + sender.sendMessage("The player isn't holding an item"); + } else { + String itemName = item.getType().toString().replaceAll("_", " "); + itemName = WordUtils.capitalizeFully(itemName); + + Enchantment enchantment = getEnchantment(args[1].toUpperCase()); + if (enchantment == null) { + sender.sendMessage(String.format("Enchantment does not exist: %s", args[1])); + } else { + String enchantmentName = enchantment.getName().replaceAll("_", " "); + enchantmentName = WordUtils.capitalizeFully(enchantmentName); + + if (!force && !enchantment.canEnchantItem(item)) { + sender.sendMessage(String.format("%s cannot be applied to %s", enchantmentName, itemName)); + } else { + int level = 1; + if (args.length > 2) { + Integer integer = getInteger(args[2]); + int minLevel = enchantment.getStartLevel(); + int maxLevel = force ? Short.MAX_VALUE : enchantment.getMaxLevel(); + + if (integer != null) { + if (integer == 0) { + item.removeEnchantment(enchantment); + Command.broadcastCommandMessage(sender, String.format("Removed %s on %s's %s", enchantmentName, player.getName(), itemName)); + return true; + } + + if (integer < minLevel || integer > maxLevel) { + sender.sendMessage(String.format("Level for enchantment %s must be between %d and %d", enchantmentName, minLevel, maxLevel)); + sender.sendMessage("Specify 0 for level to remove an enchantment"); + return true; + } + + level = integer; + } + + if ("max".equals(args[2])) { + level = maxLevel; + } + } + + Map enchantments = item.getEnchantments(); + boolean conflicts = false; + + if (!force && !enchantments.isEmpty()) { // TODO: Improve this to use a "hasEnchantments" call + for (Map.Entry entry : enchantments.entrySet()) { + Enchantment enchant = entry.getKey(); + + if (enchant.equals(enchantment)) continue; + if (enchant.conflictsWith(enchantment)) { + sender.sendMessage(String.format("Can't apply the enchantment %s on an item with the enchantment %s", enchantmentName, WordUtils.capitalizeFully(enchant.getName().replaceAll("_", " ")))); + conflicts = true; + break; + } + } + } + + if (!conflicts) { + item.addUnsafeEnchantment(enchantment, level); + + Command.broadcastCommandMessage(sender, String.format("Applied %s (Lvl %d) on %s's %s", enchantmentName, level, player.getName(), itemName), false); + sender.sendMessage(String.format("Enchanting succeeded, applied %s (Lvl %d) onto your %s", enchantmentName, level, itemName)); + } + } + } + } + } + return true; + } + + @Override + public List 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 == 1) { + return super.tabComplete(sender, alias, args); + } + + if (args.length == 2) { + return StringUtil.copyPartialMatches(args[1], ENCHANTMENT_NAMES, new ArrayList(ENCHANTMENT_NAMES.size())); + } + + if (args.length == 3 || args.length == 4) { + if (!args[args.length - 2].equalsIgnoreCase("force")) { + return ImmutableList.of("force"); + } + } + + return ImmutableList.of(); + } + + private Enchantment getEnchantment(String lookup) { + Enchantment enchantment = Enchantment.getByName(lookup); + + if (enchantment == null) { + Integer id = getInteger(lookup); + if (id != null) { + enchantment = Enchantment.getById(id); + } + } + + return enchantment; + } + + public static void buildEnchantments() { + if (!ENCHANTMENT_NAMES.isEmpty()) { + throw new IllegalStateException("Enchantments have already been built!"); + } + + for (Enchantment enchantment : Enchantment.values()) { + ENCHANTMENT_NAMES.add(enchantment.getName()); + } + + Collections.sort(ENCHANTMENT_NAMES); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/ExpCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/ExpCommand.java new file mode 100644 index 0000000..2749760 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/ExpCommand.java @@ -0,0 +1,89 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class ExpCommand extends VanillaCommand { + public ExpCommand() { + super("xp"); + this.description = "Gives the specified player a certain amount of experience. Specify L to give levels instead, with a negative amount resulting in taking levels."; + this.usageMessage = "/xp [player] OR /xp L [player]"; + this.setPermission("bukkit.command.xp"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length > 0) { + String inputAmount = args[0]; + Player player = null; + + boolean isLevel = inputAmount.endsWith("l") || inputAmount.endsWith("L"); + if (isLevel && inputAmount.length() > 1) { + inputAmount = inputAmount.substring(0, inputAmount.length() - 1); + } + + int amount = getInteger(sender, inputAmount, Integer.MIN_VALUE, Integer.MAX_VALUE); + boolean isTaking = amount < 0; + + if (isTaking) { + amount *= -1; + } + + if (args.length > 1) { + player = Bukkit.getPlayer(args[1]); + } else if (sender instanceof Player) { + player = (Player) sender; + } + + if (player != null) { + if (isLevel) { + if (isTaking) { + player.giveExpLevels(-amount); + Command.broadcastCommandMessage(sender, "Taken " + amount + " level(s) from " + player.getName()); + } else { + player.giveExpLevels(amount); + Command.broadcastCommandMessage(sender, "Given " + amount + " level(s) to " + player.getName()); + } + } else { + if (isTaking) { + sender.sendMessage(ChatColor.RED + "Taking experience can only be done by levels, cannot give players negative experience points"); + return false; + } else { + player.giveExp(amount); + Command.broadcastCommandMessage(sender, "Given " + amount + " experience to " + player.getName()); + } + } + } else { + sender.sendMessage("Can't find player, was one provided?\n" + ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + return true; + } + + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + @Override + public List 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 == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/GameModeCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/GameModeCommand.java new file mode 100644 index 0000000..c58f906 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/GameModeCommand.java @@ -0,0 +1,98 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class GameModeCommand extends VanillaCommand { + private static final List GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival"); + + public GameModeCommand() { + super("gamemode"); + this.description = "Changes the player to a specific game mode"; + this.usageMessage = "/gamemode [player]"; + this.setPermission("bukkit.command.gamemode"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + String modeArg = args[0]; + String playerArg = sender.getName(); + + if (args.length == 2) { + playerArg = args[1]; + } + + Player player = Bukkit.getPlayerExact(playerArg); + + if (player != null) { + int value = -1; + + try { + value = Integer.parseInt(modeArg); + } catch (NumberFormatException ex) {} + + GameMode mode = GameMode.getByValue(value); + + if (mode == null) { + if (modeArg.equalsIgnoreCase("creative") || modeArg.equalsIgnoreCase("c")) { + mode = GameMode.CREATIVE; + } else if (modeArg.equalsIgnoreCase("adventure") || modeArg.equalsIgnoreCase("a")) { + mode = GameMode.ADVENTURE; + } else { + mode = GameMode.SURVIVAL; + } + } + + if (mode != player.getGameMode()) { + player.setGameMode(mode); + + if (mode != player.getGameMode()) { + sender.sendMessage("Game mode change for " + player.getName() + " failed!"); + } else { + if (player == sender) { + Command.broadcastCommandMessage(sender, "Set own game mode to " + mode.toString() + " mode"); + } else { + Command.broadcastCommandMessage(sender, "Set " + player.getName() + "'s game mode to " + mode.toString() + " mode"); + } + } + } else { + sender.sendMessage(player.getName() + " already has game mode " + mode.getValue()); + } + } else { + sender.sendMessage("Can't find player " + playerArg); + } + + return true; + } + + @Override + public List 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], GAMEMODE_NAMES, new ArrayList(GAMEMODE_NAMES.size())); + } else if (args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/GameRuleCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/GameRuleCommand.java new file mode 100644 index 0000000..40c531b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/GameRuleCommand.java @@ -0,0 +1,88 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.Validate; +import org.bukkit.ChatColor; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.HumanEntity; + +public class GameRuleCommand extends VanillaCommand { + private static final List GAMERULE_STATES = ImmutableList.of("true", "false"); + + public GameRuleCommand() { + super("gamerule"); + this.description = "Sets a server's game rules"; + this.usageMessage = "/gamerule OR /gamerule "; + this.setPermission("bukkit.command.gamerule"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length > 0) { + String rule = args[0]; + World world = getGameWorld(sender); + + if (world.isGameRule(rule)) { + if (args.length > 1) { + String value = args[1]; + + world.setGameRuleValue(rule, value); + Command.broadcastCommandMessage(sender, "Game rule " + rule + " has been set to: " + value); + } else { + String value = world.getGameRuleValue(rule); + sender.sendMessage(rule + " = " + value); + } + } else { + sender.sendMessage(ChatColor.RED + "No game rule called " + rule + " is available"); + } + + return true; + } else { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + sender.sendMessage("Rules: " + this.createString(getGameWorld(sender).getGameRules(), 0, ", ")); + + return true; + } + } + + private World getGameWorld(CommandSender sender) { + if (sender instanceof HumanEntity) { + World world = ((HumanEntity) sender).getWorld(); + if (world != null) { + return world; + } + } else if (sender instanceof BlockCommandSender) { + return ((BlockCommandSender) sender).getBlock().getWorld(); + } + + return Bukkit.getWorlds().get(0); + } + + @Override + public List 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 == 1) { + return StringUtil.copyPartialMatches(args[0], Arrays.asList(getGameWorld(sender).getGameRules()), new ArrayList()); + } + + if (args.length == 2) { + return StringUtil.copyPartialMatches(args[1], GAMERULE_STATES, new ArrayList(GAMERULE_STATES.size())); + } + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/GiveCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/GiveCommand.java new file mode 100644 index 0000000..5eb071f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/GiveCommand.java @@ -0,0 +1,140 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.StringUtil; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; + +public class GiveCommand extends VanillaCommand { + private static List materials; + static { + ArrayList materialList = new ArrayList(); + for (Material material : Material.values()) { + materialList.add(material.name()); + } + Collections.sort(materialList); + materials = ImmutableList.copyOf(materialList); + } + + public GiveCommand() { + super("give"); + this.description = "Gives the specified player a certain amount of items"; + this.usageMessage = "/give [amount [data]]"; + this.setPermission("bukkit.command.give"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if ((args.length < 2)) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + Player player = Bukkit.getPlayerExact(args[0]); + + if (player != null) { + Material material = Material.matchMaterial(args[1]); + + if (material == null) { + material = Bukkit.getUnsafe().getMaterialFromInternalName(args[1]); + } + + if (material != null) { + int amount = 1; + short data = 0; + + if (args.length >= 3) { + amount = this.getInteger(sender, args[2], 1, 64); + + if (args.length >= 4) { + try { + data = Short.parseShort(args[3]); + } catch (NumberFormatException ex) {} + } + } + + ItemStack stack = new ItemStack(material, amount, data); + + if (args.length >= 5) { + try { + stack = Bukkit.getUnsafe().modifyItemStack(stack, Joiner.on(' ').join(Arrays.asList(args).subList(4, args.length))); + } catch (Throwable t) { + player.sendMessage("Not a valid tag"); + return true; + } + } + + for (int i = 0; i < player.getInventory().getSize(); i++) { + ItemStack item = player.getInventory().getItem(i); + if (item == null || item.getType() == Material.AIR) { + player.getInventory().setItem(i, stack); + break; + } else if (item.isSimilar(stack)) { + player.getInventory().addItem(stack); + break; + } + } + + if (!(sender instanceof ConsoleCommandSender)) + Command.broadcastCommandMessage(sender, "Gave " + player.getName() + " some " + material.getId() + " (" + material + ")"); + } else { + sender.sendMessage("There's no item called " + args[1]); + } + } else { + sender.sendMessage("Can't find player " + args[0]); + } + + return true; + } + + @Override + public List 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 == 1) { + return super.tabComplete(sender, alias, args); + } + if (args.length == 2) { + final String arg = args[1]; + final List materials = GiveCommand.materials; + List completion = new ArrayList(); + + final int size = materials.size(); + int i = Collections.binarySearch(materials, arg, String.CASE_INSENSITIVE_ORDER); + + if (i < 0) { + // Insertion (start) index + i = -1 - i; + } + + for ( ; i < size; i++) { + String material = materials.get(i); + if (StringUtil.startsWithIgnoreCase(material, arg)) { + completion.add(material); + } else { + break; + } + } + + return Bukkit.getUnsafe().tabCompleteInternalMaterialName(arg, completion); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/HelpCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/HelpCommand.java new file mode 100644 index 0000000..ff73b26 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/HelpCommand.java @@ -0,0 +1,228 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.help.HelpMap; +import org.bukkit.help.HelpTopic; +import org.bukkit.help.HelpTopicComparator; +import org.bukkit.help.IndexHelpTopic; +import org.bukkit.util.ChatPaginator; + +import com.google.common.collect.ImmutableList; + +public class HelpCommand extends VanillaCommand { + public HelpCommand() { + super("help"); + this.description = "Shows the help menu"; + this.usageMessage = "/help \n/help \n/help "; + this.setAliases(Arrays.asList(new String[] { "?" })); + this.setPermission("bukkit.command.help"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + String command; + int pageNumber; + int pageHeight; + int pageWidth; + + if (args.length == 0) { + command = ""; + pageNumber = 1; + } else if (NumberUtils.isDigits(args[args.length - 1])) { + command = StringUtils.join(ArrayUtils.subarray(args, 0, args.length - 1), " "); + try { + pageNumber = NumberUtils.createInteger(args[args.length - 1]); + } catch (NumberFormatException exception) { + pageNumber = 1; + } + if (pageNumber <= 0) { + pageNumber = 1; + } + } else { + command = StringUtils.join(args, " "); + pageNumber = 1; + } + + if (sender instanceof ConsoleCommandSender) { + pageHeight = ChatPaginator.UNBOUNDED_PAGE_HEIGHT; + pageWidth = ChatPaginator.UNBOUNDED_PAGE_WIDTH; + } else { + pageHeight = ChatPaginator.CLOSED_CHAT_PAGE_HEIGHT - 1; + pageWidth = ChatPaginator.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH; + } + + HelpMap helpMap = Bukkit.getServer().getHelpMap(); + HelpTopic topic = helpMap.getHelpTopic(command); + + if (topic == null) { + topic = helpMap.getHelpTopic("/" + command); + } + + if (topic == null) { + topic = findPossibleMatches(command); + } + + if (topic == null || !topic.canSee(sender)) { + sender.sendMessage(ChatColor.RED + "No help for " + command); + return true; + } + + ChatPaginator.ChatPage page = ChatPaginator.paginate(topic.getFullText(sender), pageNumber, pageWidth, pageHeight); + + StringBuilder header = new StringBuilder(); + header.append(ChatColor.YELLOW); + header.append("--------- "); + header.append(ChatColor.WHITE); + header.append("Help: "); + header.append(topic.getName()); + header.append(" "); + if (page.getTotalPages() > 1) { + header.append("("); + header.append(page.getPageNumber()); + header.append("/"); + header.append(page.getTotalPages()); + header.append(") "); + } + header.append(ChatColor.YELLOW); + for (int i = header.length(); i < ChatPaginator.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH; i++) { + header.append("-"); + } + sender.sendMessage(header.toString()); + + sender.sendMessage(page.getLines()); + + return true; + } + + @Override + public List 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) { + List matchedTopics = new ArrayList(); + String searchString = args[0]; + for (HelpTopic topic : Bukkit.getServer().getHelpMap().getHelpTopics()) { + String trimmedTopic = topic.getName().startsWith("/") ? topic.getName().substring(1) : topic.getName(); + + if (trimmedTopic.startsWith(searchString)) { + matchedTopics.add(trimmedTopic); + } + } + return matchedTopics; + } + return ImmutableList.of(); + } + + protected HelpTopic findPossibleMatches(String searchString) { + int maxDistance = (searchString.length() / 5) + 3; + Set possibleMatches = new TreeSet(HelpTopicComparator.helpTopicComparatorInstance()); + + if (searchString.startsWith("/")) { + searchString = searchString.substring(1); + } + + for (HelpTopic topic : Bukkit.getServer().getHelpMap().getHelpTopics()) { + String trimmedTopic = topic.getName().startsWith("/") ? topic.getName().substring(1) : topic.getName(); + + if (trimmedTopic.length() < searchString.length()) { + continue; + } + + if (Character.toLowerCase(trimmedTopic.charAt(0)) != Character.toLowerCase(searchString.charAt(0))) { + continue; + } + + if (damerauLevenshteinDistance(searchString, trimmedTopic.substring(0, searchString.length())) < maxDistance) { + possibleMatches.add(topic); + } + } + + if (possibleMatches.size() > 0) { + return new IndexHelpTopic("Search", null, null, possibleMatches, "Search for: " + searchString); + } else { + return null; + } + } + + /** + * Computes the Dameraur-Levenshtein Distance between two strings. Adapted + * from the algorithm at Wikipedia: Damerau–Levenshtein distance + * + * @param s1 The first string being compared. + * @param s2 The second string being compared. + * @return The number of substitutions, deletions, insertions, and + * transpositions required to get from s1 to s2. + */ + protected static int damerauLevenshteinDistance(String s1, String s2) { + if (s1 == null && s2 == null) { + return 0; + } + if (s1 != null && s2 == null) { + return s1.length(); + } + if (s1 == null && s2 != null) { + return s2.length(); + } + + int s1Len = s1.length(); + int s2Len = s2.length(); + int[][] H = new int[s1Len + 2][s2Len + 2]; + + int INF = s1Len + s2Len; + H[0][0] = INF; + for (int i = 0; i <= s1Len; i++) { + H[i + 1][1] = i; + H[i + 1][0] = INF; + } + for (int j = 0; j <= s2Len; j++) { + H[1][j + 1] = j; + H[0][j + 1] = INF; + } + + Map sd = new HashMap(); + for (char Letter : (s1 + s2).toCharArray()) { + if (!sd.containsKey(Letter)) { + sd.put(Letter, 0); + } + } + + for (int i = 1; i <= s1Len; i++) { + int DB = 0; + for (int j = 1; j <= s2Len; j++) { + int i1 = sd.get(s2.charAt(j - 1)); + int j1 = DB; + + if (s1.charAt(i - 1) == s2.charAt(j - 1)) { + H[i + 1][j + 1] = H[i][j]; + DB = j; + } else { + H[i + 1][j + 1] = Math.min(H[i][j], Math.min(H[i + 1][j], H[i][j + 1])) + 1; + } + + H[i + 1][j + 1] = Math.min(H[i + 1][j + 1], H[i1][j1] + (i - i1 - 1) + 1 + (j - j1 - 1)); + } + sd.put(s1.charAt(i - 1), i); + } + + return H[s1Len + 1][s2Len + 1]; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/KickCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/KickCommand.java new file mode 100644 index 0000000..b6c3f79 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/KickCommand.java @@ -0,0 +1,59 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class KickCommand extends VanillaCommand { + public KickCommand() { + super("kick"); + this.description = "Removes the specified player from the server"; + this.usageMessage = "/kick [reason ...]"; + this.setPermission("bukkit.command.kick"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length < 1 || args[0].length() == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + Player player = Bukkit.getPlayerExact(args[0]); + + if (player != null) { + String reason = "Kicked by an operator."; + + if (args.length > 1) { + reason = createString(args, 1); + } + + player.kickPlayer(reason); + Command.broadcastCommandMessage(sender, "Kicked player " + player.getName() + ". With reason:\n" + reason); + } else { + sender.sendMessage( args[0] + " not found."); + } + + return true; + } + + @Override + public List 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 >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/KillCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/KillCommand.java new file mode 100644 index 0000000..3270db5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/KillCommand.java @@ -0,0 +1,50 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageEvent; + +import com.google.common.collect.ImmutableList; + +public class KillCommand extends VanillaCommand { + public KillCommand() { + super("kill"); + this.description = "Commits suicide, only usable as a player"; + this.usageMessage = "/kill"; + this.setPermission("bukkit.command.kill"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + if (sender instanceof Player) { + Player player = (Player) sender; + + EntityDamageEvent ede = new EntityDamageEvent(player, EntityDamageEvent.DamageCause.SUICIDE, 1000); + Bukkit.getPluginManager().callEvent(ede); + if (ede.isCancelled()) return true; + + ede.getEntity().setLastDamageCause(ede); + player.setHealth(0); + sender.sendMessage("Ouch. That look like it hurt."); + } else { + sender.sendMessage("You can only perform this command as a player"); + } + + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/ListCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/ListCommand.java new file mode 100644 index 0000000..eb8a6a9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/ListCommand.java @@ -0,0 +1,54 @@ +package org.bukkit.command.defaults; + +import java.util.Collection; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class ListCommand extends VanillaCommand { + public ListCommand() { + super("list"); + this.description = "Lists all online players"; + this.usageMessage = "/list"; + this.setPermission("bukkit.command.list"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + StringBuilder online = new StringBuilder(); + + final Collection players = Bukkit.getOnlinePlayers(); + + for (Player player : players) { + // If a player is hidden from the sender don't show them in the list + if (sender instanceof Player && !((Player) sender).canSee(player)) + continue; + + if (online.length() > 0) { + online.append(", "); + } + + online.append(player.getDisplayName()); + } + + sender.sendMessage("There are " + players.size() + "/" + Bukkit.getMaxPlayers() + " players online:\n" + online.toString()); + + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/MeCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/MeCommand.java new file mode 100644 index 0000000..ddcd9ad --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/MeCommand.java @@ -0,0 +1,35 @@ +package org.bukkit.command.defaults; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +public class MeCommand extends VanillaCommand { + public MeCommand() { + super("me"); + this.description = "Performs the specified action in chat"; + this.usageMessage = "/me "; + this.setPermission("bukkit.command.me"); + } + + @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 false; + } + + StringBuilder message = new StringBuilder(); + message.append(sender.getName()); + + for (String arg : args) { + message.append(" "); + message.append(arg); + } + + Bukkit.broadcastMessage("* " + message.toString()); + + return true; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/OpCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/OpCommand.java new file mode 100644 index 0000000..e1f8452 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/OpCommand.java @@ -0,0 +1,75 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class OpCommand extends VanillaCommand { + public OpCommand() { + super("op"); + this.description = "Gives the specified player operator status"; + this.usageMessage = "/op "; + this.setPermission("bukkit.command.op.give"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length != 1 || args[0].length() == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + OfflinePlayer player = Bukkit.getOfflinePlayer(args[0]); + player.setOp(true); + + Command.broadcastCommandMessage(sender, "Opped " + args[0]); + return true; + } + + @Override + public List 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 == 1) { + if (!(sender instanceof Player)) { + return ImmutableList.of(); + } + + String lastWord = args[0]; + if (lastWord.length() == 0) { + return ImmutableList.of(); + } + + Player senderPlayer = (Player) sender; + + ArrayList matchedPlayers = new ArrayList(); + for (Player player : sender.getServer().getOnlinePlayers()) { + String name = player.getName(); + if (!senderPlayer.canSee(player) || player.isOp()) { + continue; + } + if (StringUtil.startsWithIgnoreCase(name, lastWord)) { + matchedPlayers.add(name); + } + } + + Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER); + return matchedPlayers; + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/PardonCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/PardonCommand.java new file mode 100644 index 0000000..82c7a95 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/PardonCommand.java @@ -0,0 +1,56 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class PardonCommand extends VanillaCommand { + public PardonCommand() { + super("pardon"); + this.description = "Allows the specified player to use this server"; + this.usageMessage = "/pardon "; + this.setPermission("bukkit.command.unban.player"); + } + + @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 false; + } + + Bukkit.getBanList(BanList.Type.NAME).pardon(args[0]); + Command.broadcastCommandMessage(sender, "Pardoned " + args[0]); + return true; + } + + @Override + public List 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 == 1) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getBannedPlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[0])) { + completions.add(name); + } + } + return completions; + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java new file mode 100644 index 0000000..689a0bd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java @@ -0,0 +1,52 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class PardonIpCommand extends VanillaCommand { + public PardonIpCommand() { + super("pardon-ip"); + this.description = "Allows the specified IP address to use this server"; + this.usageMessage = "/pardon-ip

          "; + this.setPermission("bukkit.command.unban.ip"); + } + + @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 false; + } + + if (BanIpCommand.ipValidity.matcher(args[0]).matches()) { + Bukkit.unbanIP(args[0]); + Command.broadcastCommandMessage(sender, "Pardoned ip " + args[0]); + } else { + sender.sendMessage("Invalid ip"); + } + + return true; + } + + @Override + public List 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 == 1) { + return StringUtil.copyPartialMatches(args[0], Bukkit.getIPBans(), new ArrayList()); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/PlaySoundCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/PlaySoundCommand.java new file mode 100644 index 0000000..0cb5ca2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/PlaySoundCommand.java @@ -0,0 +1,87 @@ +package org.bukkit.command.defaults; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class PlaySoundCommand extends VanillaCommand { + public PlaySoundCommand() { + super("playsound"); + this.description = "Plays a sound to a given player"; + this.usageMessage = "/playsound [x] [y] [z] [volume] [pitch] [minimumVolume]"; + this.setPermission("bukkit.command.playsound"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) { + return true; + } + + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + final String soundArg = args[0]; + final String playerArg = args[1]; + + final Player player = Bukkit.getPlayerExact(playerArg); + if (player == null) { + sender.sendMessage(ChatColor.RED + "Can't find player " + playerArg); + return false; + } + + final Location location = player.getLocation(); + + double x = Math.floor(location.getX()); + double y = Math.floor(location.getY() + 0.5D); + double z = Math.floor(location.getZ()); + double volume = 1.0D; + double pitch = 1.0D; + double minimumVolume = 0.0D; + + switch (args.length) { + default: + case 8: + minimumVolume = getDouble(sender, args[7], 0.0D, 1.0D); + case 7: + pitch = getDouble(sender, args[6], 0.0D, 2.0D); + case 6: + volume = getDouble(sender, args[5], 0.0D, Float.MAX_VALUE); + case 5: + z = getRelativeDouble(z, sender, args[4]); + case 4: + y = getRelativeDouble(y, sender, args[3]); + case 3: + x = getRelativeDouble(x, sender, args[2]); + case 2: + // Noop + } + + final double fixedVolume = volume > 1.0D ? volume * 16.0D : 16.0D; + final Location soundLocation = new Location(player.getWorld(), x, y, z); + if (location.distanceSquared(soundLocation) > fixedVolume * fixedVolume) { + if (minimumVolume <= 0.0D) { + sender.sendMessage(ChatColor.RED + playerArg + " is too far away to hear the sound"); + return false; + } + + final double deltaX = x - location.getX(); + final double deltaY = y - location.getY(); + final double deltaZ = z - location.getZ(); + final double delta = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ) / 2.0D; + + if (delta > 0.0D) { + location.add(deltaX / delta, deltaY / delta, deltaZ / delta); + } + + player.playSound(location, soundArg, (float) minimumVolume, (float) pitch); + } else { + player.playSound(soundLocation, soundArg, (float) volume, (float) pitch); + } + sender.sendMessage(String.format("Played '%s' to %s", soundArg, playerArg)); + return true; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java new file mode 100644 index 0000000..e21d167 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java @@ -0,0 +1,51 @@ +package org.bukkit.command.defaults; + +import java.util.Arrays; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + +public class PluginsCommand extends BukkitCommand { + public PluginsCommand(String name) { + super(name); + this.description = "Gets a list of plugins running on the server"; + this.usageMessage = "/plugins"; + this.setPermission("bukkit.command.plugins"); + this.setAliases(Arrays.asList("pl")); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + sender.sendMessage("Plugins " + getPluginList()); + return true; + } + + private String getPluginList() { + StringBuilder pluginList = new StringBuilder(); + Plugin[] plugins = Bukkit.getPluginManager().getPlugins(); + + for (Plugin plugin : plugins) { + if (pluginList.length() > 0) { + pluginList.append(ChatColor.WHITE); + pluginList.append(", "); + } + + pluginList.append(plugin.isEnabled() ? ChatColor.GREEN : ChatColor.RED); + pluginList.append(plugin.getDescription().getName()); + } + + return "(" + plugins.length + "): " + pluginList.toString(); + } + + // Spigot Start + @Override + public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException + { + return java.util.Collections.emptyList(); + } + // Spigot End +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/ReloadCommand.java new file mode 100644 index 0000000..a08ae80 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/ReloadCommand.java @@ -0,0 +1,36 @@ +package org.bukkit.command.defaults; + +import java.util.Arrays; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public class ReloadCommand extends BukkitCommand { + public ReloadCommand(String name) { + super(name); + this.description = "Reloads the server configuration and plugins"; + this.usageMessage = "/reload"; + this.setPermission("bukkit.command.reload"); + this.setAliases(Arrays.asList("rl")); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + Bukkit.reload(); + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); + + return true; + } + + // Spigot Start + @Override + public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException + { + return java.util.Collections.emptyList(); + } + // Spigot End +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveCommand.java new file mode 100644 index 0000000..8a44f9c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveCommand.java @@ -0,0 +1,46 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import com.google.common.collect.ImmutableList; + +public class SaveCommand extends VanillaCommand { + public SaveCommand() { + super("save-all"); + this.description = "Saves the server to disk"; + this.usageMessage = "/save-all"; + this.setPermission("bukkit.command.save.perform"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + Command.broadcastCommandMessage(sender, "Forcing save.."); + + Bukkit.savePlayers(); + + for (World world : Bukkit.getWorlds()) { + world.save(); + } + + Command.broadcastCommandMessage(sender, "Save complete."); + + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java new file mode 100644 index 0000000..1fbb3f0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java @@ -0,0 +1,41 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import com.google.common.collect.ImmutableList; + +public class SaveOffCommand extends VanillaCommand { + public SaveOffCommand() { + super("save-off"); + this.description = "Disables server autosaving"; + this.usageMessage = "/save-off"; + this.setPermission("bukkit.command.save.disable"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + for (World world : Bukkit.getWorlds()) { + world.setAutoSave(false); + } + + Command.broadcastCommandMessage(sender, "Disabled level saving.."); + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java new file mode 100644 index 0000000..9774f67 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java @@ -0,0 +1,41 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import com.google.common.collect.ImmutableList; + +public class SaveOnCommand extends VanillaCommand { + public SaveOnCommand() { + super("save-on"); + this.description = "Enables server autosaving"; + this.usageMessage = "/save-on"; + this.setPermission("bukkit.command.save.enable"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + for (World world : Bukkit.getWorlds()) { + world.setAutoSave(true); + } + + Command.broadcastCommandMessage(sender, "Enabled level saving.."); + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SayCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SayCommand.java new file mode 100644 index 0000000..658fe21 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SayCommand.java @@ -0,0 +1,62 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class SayCommand extends VanillaCommand { + public SayCommand() { + super("say"); + this.description = "Broadcasts the given message as the sender"; + this.usageMessage = "/say "; + this.setPermission("bukkit.command.say"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + StringBuilder message = new StringBuilder(); + message.append(ChatColor.LIGHT_PURPLE).append("["); + if (sender instanceof ConsoleCommandSender) { + message.append("Server"); + } else if (sender instanceof Player) { + message.append(((Player) sender).getDisplayName()); + } else { + message.append(sender.getName()); + } + message.append(ChatColor.LIGHT_PURPLE).append("] "); + + if (args.length > 0) { + message.append(args[0]); + for (int i = 1; i < args.length; i++) { + message.append(" ").append(args[i]); + } + } + + Bukkit.broadcastMessage(message.toString()); + return true; + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + + if (args.length >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/ScoreboardCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/ScoreboardCommand.java new file mode 100644 index 0000000..f0490cf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/ScoreboardCommand.java @@ -0,0 +1,617 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Score; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class ScoreboardCommand extends VanillaCommand { + + private static final List MAIN_CHOICES = ImmutableList.of("objectives", "players", "teams"); + private static final List OBJECTIVES_CHOICES = ImmutableList.of("list", "add", "remove", "setdisplay"); + private static final List OBJECTIVES_CRITERIA = ImmutableList.of("health", "playerKillCount", "totalKillCount", "deathCount", "dummy"); + private static final List PLAYERS_CHOICES = ImmutableList.of("set", "add", "remove", "reset", "list"); + private static final List TEAMS_CHOICES = ImmutableList.of("add", "remove", "join", "leave", "empty", "list", "option"); + private static final List TEAMS_OPTION_CHOICES = ImmutableList.of("color", "friendlyfire", "seeFriendlyInvisibles"); + private static final Map OBJECTIVES_DISPLAYSLOT = ImmutableMap.of("belowName", DisplaySlot.BELOW_NAME, "list", DisplaySlot.PLAYER_LIST, "sidebar", DisplaySlot.SIDEBAR); + private static final Map TEAMS_OPTION_COLOR = ImmutableMap.builder() + .put("aqua", ChatColor.AQUA) + .put("black", ChatColor.BLACK) + .put("blue", ChatColor.BLUE) + .put("bold", ChatColor.BOLD) + .put("dark_aqua", ChatColor.DARK_AQUA) + .put("dark_blue", ChatColor.DARK_BLUE) + .put("dark_gray", ChatColor.DARK_GRAY) + .put("dark_green", ChatColor.DARK_GREEN) + .put("dark_purple", ChatColor.DARK_PURPLE) + .put("dark_red", ChatColor.DARK_RED) + .put("gold", ChatColor.GOLD) + .put("gray", ChatColor.GRAY) + .put("green", ChatColor.GREEN) + .put("italic", ChatColor.ITALIC) + .put("light_purple", ChatColor.LIGHT_PURPLE) + .put("obfuscated", ChatColor.MAGIC) // This is the important line + .put("red", ChatColor.RED) + .put("reset", ChatColor.RESET) + .put("strikethrough", ChatColor.STRIKETHROUGH) + .put("underline", ChatColor.UNDERLINE) + .put("white", ChatColor.WHITE) + .put("yellow", ChatColor.YELLOW) + .build(); + private static final List BOOLEAN = ImmutableList.of("true", "false"); + + public ScoreboardCommand() { + super("scoreboard"); + this.description = "Scoreboard control"; + this.usageMessage = "/scoreboard"; + this.setPermission("bukkit.command.scoreboard"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) + return true; + if (args.length < 1 || args[0].length() == 0) { + sender.sendMessage(ChatColor.RED + "Usage: /scoreboard "); + return false; + } + + final Scoreboard mainScoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + + if (args[0].equalsIgnoreCase("objectives")) { + if (args.length == 1) { + sender.sendMessage(ChatColor.RED + "Usage: /scoreboard objectives "); + return false; + } + if (args[1].equalsIgnoreCase("list")) { + Set objectives = mainScoreboard.getObjectives(); + if (objectives.isEmpty()) { + sender.sendMessage(ChatColor.RED + "There are no objectives on the scoreboard"); + return false; + } + sender.sendMessage(ChatColor.DARK_GREEN + "Showing " + objectives.size() + " objective(s) on scoreboard"); + for (Objective objective : objectives) { + sender.sendMessage("- " + objective.getName() + ": displays as '" + objective.getDisplayName() + "' and is type '" + objective.getCriteria() + "'"); + } + } else if (args[1].equalsIgnoreCase("add")) { + if (args.length < 4) { + sender.sendMessage(ChatColor.RED + "/scoreboard objectives add [display name ...]"); + return false; + } + String name = args[2]; + String criteria = args[3]; + + if (criteria == null) { + sender.sendMessage(ChatColor.RED + "Invalid objective criteria type. Valid types are: " + stringCollectionToString(OBJECTIVES_CRITERIA)); + } else if (name.length() > 16) { + sender.sendMessage(ChatColor.RED + "The name '" + name + "' is too long for an objective, it can be at most 16 characters long"); + } else if (mainScoreboard.getObjective(name) != null) { + sender.sendMessage(ChatColor.RED + "An objective with the name '" + name + "' already exists"); + } else { + String displayName = null; + if (args.length > 4) { + displayName = StringUtils.join(ArrayUtils.subarray(args, 4, args.length), ' '); + if (displayName.length() > 32) { + sender.sendMessage(ChatColor.RED + "The name '" + displayName + "' is too long for an objective, it can be at most 32 characters long"); + return false; + } + } + Objective objective = mainScoreboard.registerNewObjective(name, criteria); + if (displayName != null && displayName.length() > 0) { + objective.setDisplayName(displayName); + } + sender.sendMessage("Added new objective '" + name + "' successfully"); + } + } else if (args[1].equalsIgnoreCase("remove")) { + if (args.length != 3) { + sender.sendMessage(ChatColor.RED + "/scoreboard objectives remove "); + return false; + } + String name = args[2]; + Objective objective = mainScoreboard.getObjective(name); + if (objective == null) { + sender.sendMessage(ChatColor.RED + "No objective was found by the name '" + name + "'"); + } else { + objective.unregister(); + sender.sendMessage("Removed objective '" + name + "' successfully"); + } + } else if (args[1].equalsIgnoreCase("setdisplay")) { + if (args.length != 3 && args.length != 4) { + sender.sendMessage(ChatColor.RED + "/scoreboard objectives setdisplay [objective]"); + return false; + } + String slotName = args[2]; + DisplaySlot slot = OBJECTIVES_DISPLAYSLOT.get(slotName); + if (slot == null) { + sender.sendMessage(ChatColor.RED + "No such display slot '" + slotName + "'"); + } else { + if (args.length == 4) { + String objectiveName = args[3]; + Objective objective = mainScoreboard.getObjective(objectiveName); + if (objective == null) { + sender.sendMessage(ChatColor.RED + "No objective was found by the name '" + objectiveName + "'"); + return false; + } + + objective.setDisplaySlot(slot); + sender.sendMessage("Set the display objective in slot '" + slotName + "' to '" + objective.getName() + "'"); + } else { + Objective objective = mainScoreboard.getObjective(slot); + if (objective != null) { + objective.setDisplaySlot(null); + } + sender.sendMessage("Cleared objective display slot '" + slotName + "'"); + } + } + } + } else if (args[0].equalsIgnoreCase("players")) { + if (args.length == 1) { + sender.sendMessage(ChatColor.RED + "/scoreboard players "); + return false; + } + if (args[1].equalsIgnoreCase("set") || args[1].equalsIgnoreCase("add") || args[1].equalsIgnoreCase("remove")) { + if (args.length != 5) { + if (args[1].equalsIgnoreCase("set")) { + sender.sendMessage(ChatColor.RED + "/scoreboard players set "); + } else if (args[1].equalsIgnoreCase("add")) { + sender.sendMessage(ChatColor.RED + "/scoreboard players add "); + } else { + sender.sendMessage(ChatColor.RED + "/scoreboard players remove "); + } + return false; + } + String objectiveName = args[3]; + Objective objective = mainScoreboard.getObjective(objectiveName); + if (objective == null) { + sender.sendMessage(ChatColor.RED + "No objective was found by the name '" + objectiveName + "'"); + return false; + } else if (!objective.isModifiable()) { + sender.sendMessage(ChatColor.RED + "The objective '" + objectiveName + "' is read-only and cannot be set"); + return false; + } + + String valueString = args[4]; + int value; + try { + value = Integer.parseInt(valueString); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.RED + "'" + valueString + "' is not a valid number"); + return false; + } + if (value < 1 && !args[1].equalsIgnoreCase("set")) { + sender.sendMessage(ChatColor.RED + "The number you have entered (" + value + ") is too small, it must be at least 1"); + return false; + } + + String playerName = args[2]; + if (playerName.length() > 16) { + sender.sendMessage(ChatColor.RED + "'" + playerName + "' is too long for a player name"); + return false; + } + Score score = objective.getScore(playerName); + int newScore; + if (args[1].equalsIgnoreCase("set")) { + newScore = value; + } else if (args[1].equalsIgnoreCase("add")) { + newScore = score.getScore() + value; + } else { + newScore = score.getScore() - value; + } + score.setScore(newScore); + sender.sendMessage("Set score of " + objectiveName + " for player " + playerName + " to " + newScore); + } else if (args[1].equalsIgnoreCase("reset")) { + if (args.length != 3) { + sender.sendMessage(ChatColor.RED + "/scoreboard players reset "); + return false; + } + String playerName = args[2]; + if (playerName.length() > 16) { + sender.sendMessage(ChatColor.RED + "'" + playerName + "' is too long for a player name"); + return false; + } + mainScoreboard.resetScores(playerName); + sender.sendMessage("Reset all scores of player " + playerName); + } else if (args[1].equalsIgnoreCase("list")) { + if (args.length > 3) { + sender.sendMessage(ChatColor.RED + "/scoreboard players list "); + return false; + } + if (args.length == 2) { + Set entries = mainScoreboard.getEntries(); + if (entries.isEmpty()) { + sender.sendMessage(ChatColor.RED + "There are no tracked players on the scoreboard"); + } else { + sender.sendMessage(ChatColor.DARK_GREEN + "Showing " + entries.size() + " tracked players on the scoreboard"); + sender.sendMessage(stringCollectionToString(entries)); + } + } else { + String playerName = args[2]; + if (playerName.length() > 16) { + sender.sendMessage(ChatColor.RED + "'" + playerName + "' is too long for a player name"); + return false; + } + Set scores = mainScoreboard.getScores(playerName); + if (scores.isEmpty()) { + sender.sendMessage(ChatColor.RED + "Player " + playerName + " has no scores recorded"); + } else { + sender.sendMessage(ChatColor.DARK_GREEN + "Showing " + scores.size() + " tracked objective(s) for " + playerName); + for (Score score : scores) { + sender.sendMessage("- " + score.getObjective().getDisplayName() + ": " + score.getScore() + " (" + score.getObjective().getName() + ")"); + } + } + } + } + } else if (args[0].equalsIgnoreCase("teams")) { + if (args.length == 1) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams "); + return false; + } + if (args[1].equalsIgnoreCase("list")) { + if (args.length == 2) { + Set teams = mainScoreboard.getTeams(); + if (teams.isEmpty()) { + sender.sendMessage(ChatColor.RED + "There are no teams registered on the scoreboard"); + } else { + sender.sendMessage(ChatColor.DARK_GREEN + "Showing " + teams.size() + " teams on the scoreboard"); + for (Team team : teams) { + sender.sendMessage("- " + team.getName() + ": '" + team.getDisplayName() + "' has " + team.getSize() + " players"); + } + } + } else if (args.length == 3) { + String teamName = args[2]; + Team team = mainScoreboard.getTeam(teamName); + if (team == null) { + sender.sendMessage(ChatColor.RED + "No team was found by the name '" + teamName + "'"); + } else { + Set players = team.getPlayers(); + if (players.isEmpty()) { + sender.sendMessage(ChatColor.RED + "Team " + team.getName() + " has no players"); + } else { + sender.sendMessage(ChatColor.DARK_GREEN + "Showing " + players.size() + " player(s) in team " + team.getName()); + sender.sendMessage(offlinePlayerSetToString(players)); + } + } + } else { + sender.sendMessage(ChatColor.RED + "/scoreboard teams list [name]"); + return false; + } + } else if (args[1].equalsIgnoreCase("add")) { + if (args.length < 3) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams add [display name ...]"); + return false; + } + String name = args[2]; + if (name.length() > 16) { + sender.sendMessage(ChatColor.RED + "The name '" + name + "' is too long for a team, it can be at most 16 characters long"); + } else if (mainScoreboard.getTeam(name) != null) { + sender.sendMessage(ChatColor.RED + "A team with the name '" + name + "' already exists"); + } else { + String displayName = null; + if (args.length > 3) { + displayName = StringUtils.join(ArrayUtils.subarray(args, 3, args.length), ' '); + if (displayName.length() > 32) { + sender.sendMessage(ChatColor.RED + "The display name '" + displayName + "' is too long for a team, it can be at most 32 characters long"); + return false; + } + } + Team team = mainScoreboard.registerNewTeam(name); + if (displayName != null && displayName.length() > 0) { + team.setDisplayName(displayName); + } + sender.sendMessage("Added new team '" + team.getName() + "' successfully"); + } + } else if (args[1].equalsIgnoreCase("remove")) { + if (args.length != 3) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams remove "); + return false; + } + String name = args[2]; + Team team = mainScoreboard.getTeam(name); + if (team == null) { + sender.sendMessage(ChatColor.RED + "No team was found by the name '" + name + "'"); + } else { + team.unregister(); + sender.sendMessage("Removed team " + name); + } + } else if (args[1].equalsIgnoreCase("empty")) { + if (args.length != 3) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams clear "); + return false; + } + String name = args[2]; + Team team = mainScoreboard.getTeam(name); + if (team == null) { + sender.sendMessage(ChatColor.RED + "No team was found by the name '" + name + "'"); + } else { + Set players = team.getPlayers(); + if (players.isEmpty()) { + sender.sendMessage(ChatColor.RED + "Team " + team.getName() + " is already empty, cannot remove nonexistant players"); + } else { + for (OfflinePlayer player : players) { + team.removePlayer(player); + } + sender.sendMessage("Removed all " + players.size() + " player(s) from team " + team.getName()); + } + } + } else if (args[1].equalsIgnoreCase("join")) { + if ((sender instanceof Player) ? args.length < 3 : args.length < 4) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams join [player...]"); + return false; + } + String teamName = args[2]; + Team team = mainScoreboard.getTeam(teamName); + if (team == null) { + sender.sendMessage(ChatColor.RED + "No team was found by the name '" + teamName + "'"); + } else { + Set addedPlayers = new HashSet(); + if ((sender instanceof Player) && args.length == 3) { + team.addPlayer((Player) sender); + addedPlayers.add(sender.getName()); + } else { + for (int i = 3; i < args.length; i++) { + String playerName = args[i]; + OfflinePlayer offlinePlayer; + Player player = Bukkit.getPlayerExact(playerName); + if (player != null) { + offlinePlayer = player; + } else { + offlinePlayer = Bukkit.getOfflinePlayer(playerName); + } + team.addPlayer(offlinePlayer); + addedPlayers.add(offlinePlayer.getName()); + } + } + sender.sendMessage("Added " + addedPlayers.size() + " player(s) to team " + team.getName() + ": " + stringCollectionToString(addedPlayers)); + } + } else if (args[1].equalsIgnoreCase("leave")) { + if (!(sender instanceof Player) && args.length < 3) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams leave [player...]"); + return false; + } + Set left = new HashSet(); + Set noTeam = new HashSet(); + if ((sender instanceof Player) && args.length == 2) { + Team team = mainScoreboard.getPlayerTeam((Player) sender); + if (team != null) { + team.removePlayer((Player) sender); + left.add(sender.getName()); + } else { + noTeam.add(sender.getName()); + } + } else { + for (int i = 2; i < args.length; i++) { + String playerName = args[i]; + OfflinePlayer offlinePlayer; + Player player = Bukkit.getPlayerExact(playerName); + if (player != null) { + offlinePlayer = player; + } else { + offlinePlayer = Bukkit.getOfflinePlayer(playerName); + } + Team team = mainScoreboard.getPlayerTeam(offlinePlayer); + if (team != null) { + team.removePlayer(offlinePlayer); + left.add(offlinePlayer.getName()); + } else { + noTeam.add(offlinePlayer.getName()); + } + } + } + if (!left.isEmpty()) { + sender.sendMessage("Removed " + left.size() + " player(s) from their teams: " + stringCollectionToString(left)); + } + if (!noTeam.isEmpty()) { + sender.sendMessage("Could not remove " + noTeam.size() + " player(s) from their teams: " + stringCollectionToString(noTeam)); + } + } else if (args[1].equalsIgnoreCase("option")) { + if (args.length != 4 && args.length != 5) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams option "); + return false; + } + String teamName = args[2]; + Team team = mainScoreboard.getTeam(teamName); + if (team == null) { + sender.sendMessage(ChatColor.RED + "No team was found by the name '" + teamName + "'"); + return false; + } + String option = args[3].toLowerCase(); + if (!option.equals("friendlyfire") && !option.equals("color") && !option.equals("seefriendlyinvisibles")) { + sender.sendMessage(ChatColor.RED + "/scoreboard teams option "); + return false; + } + if (args.length == 4) { + if (option.equals("color")) { + sender.sendMessage(ChatColor.RED + "Valid values for option color are: " + stringCollectionToString(TEAMS_OPTION_COLOR.keySet())); + } else { + sender.sendMessage(ChatColor.RED + "Valid values for option " + option + " are: true and false"); + } + } else { + String value = args[4].toLowerCase(); + if (option.equals("color")) { + ChatColor color = TEAMS_OPTION_COLOR.get(value); + if (color == null) { + sender.sendMessage(ChatColor.RED + "Valid values for option color are: " + stringCollectionToString(TEAMS_OPTION_COLOR.keySet())); + return false; + } + team.setPrefix(color.toString()); + team.setSuffix(ChatColor.RESET.toString()); + } else { + if (!value.equals("true") && !value.equals("false")) { + sender.sendMessage(ChatColor.RED + "Valid values for option " + option + " are: true and false"); + return false; + } + if (option.equals("friendlyfire")) { + team.setAllowFriendlyFire(value.equals("true")); + } else { + team.setCanSeeFriendlyInvisibles(value.equals("true")); + } + } + sender.sendMessage("Set option " + option + " for team " + team.getName() + " to " + value); + } + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /scoreboard "); + return false; + } + return true; + } + + @Override + public List 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 == 1) { + return StringUtil.copyPartialMatches(args[0], MAIN_CHOICES, new ArrayList()); + } + if (args.length > 1) { + if (args[0].equalsIgnoreCase("objectives")) { + if (args.length == 2) { + return StringUtil.copyPartialMatches(args[1], OBJECTIVES_CHOICES, new ArrayList()); + } + if (args[1].equalsIgnoreCase("add")) { + if (args.length == 4) { + return StringUtil.copyPartialMatches(args[3], OBJECTIVES_CRITERIA, new ArrayList()); + } + } else if (args[1].equalsIgnoreCase("remove")) { + if (args.length == 3) { + return StringUtil.copyPartialMatches(args[2], this.getCurrentObjectives(), new ArrayList()); + } + } else if (args[1].equalsIgnoreCase("setdisplay")) { + if (args.length == 3) { + return StringUtil.copyPartialMatches(args[2], OBJECTIVES_DISPLAYSLOT.keySet(), new ArrayList()); + } + if (args.length == 4) { + return StringUtil.copyPartialMatches(args[3], this.getCurrentObjectives(), new ArrayList()); + } + } + } else if (args[0].equalsIgnoreCase("players")) { + if (args.length == 2) { + return StringUtil.copyPartialMatches(args[1], PLAYERS_CHOICES, new ArrayList()); + } + if (args[1].equalsIgnoreCase("set") || args[1].equalsIgnoreCase("add") || args[1].equalsIgnoreCase("remove")) { + if (args.length == 3) { + return super.tabComplete(sender, alias, args); + } + if (args.length == 4) { + return StringUtil.copyPartialMatches(args[3], this.getCurrentObjectives(), new ArrayList()); + } + } else { + if (args.length == 3) { + return StringUtil.copyPartialMatches(args[2], this.getCurrentEntries(), new ArrayList()); + } + } + } else if (args[0].equalsIgnoreCase("teams")) { + if (args.length == 2) { + return StringUtil.copyPartialMatches(args[1], TEAMS_CHOICES, new ArrayList()); + } + if (args[1].equalsIgnoreCase("join")) { + if (args.length == 3) { + return StringUtil.copyPartialMatches(args[2], this.getCurrentTeams(), new ArrayList()); + } + if (args.length >= 4) { + return super.tabComplete(sender, alias, args); + } + } else if (args[1].equalsIgnoreCase("leave")) { + return super.tabComplete(sender, alias, args); + } else if (args[1].equalsIgnoreCase("option")) { + if (args.length == 3) { + return StringUtil.copyPartialMatches(args[2], this.getCurrentTeams(), new ArrayList()); + } + if (args.length == 4) { + return StringUtil.copyPartialMatches(args[3], TEAMS_OPTION_CHOICES, new ArrayList()); + } + if (args.length == 5) { + if (args[3].equalsIgnoreCase("color")) { + return StringUtil.copyPartialMatches(args[4], TEAMS_OPTION_COLOR.keySet(), new ArrayList()); + } else { + return StringUtil.copyPartialMatches(args[4], BOOLEAN, new ArrayList()); + } + } + } else { + if (args.length == 3) { + return StringUtil.copyPartialMatches(args[2], this.getCurrentTeams(), new ArrayList()); + } + } + } + } + + return ImmutableList.of(); + } + + private static String offlinePlayerSetToString(Set set) { + StringBuilder string = new StringBuilder(); + String lastValue = null; + for (OfflinePlayer value : set) { + string.append(lastValue = value.getName()).append(", "); + } + string.delete(string.length() - 2, Integer.MAX_VALUE); + if (string.length() != lastValue.length()) { + string.insert(string.length() - lastValue.length(), "and "); + } + return string.toString(); + + } + + private static String stringCollectionToString(Collection set) { + StringBuilder string = new StringBuilder(); + String lastValue = null; + for (String value : set) { + string.append(lastValue = value).append(", "); + } + string.delete(string.length() - 2, Integer.MAX_VALUE); + if (string.length() != lastValue.length()) { + string.insert(string.length() - lastValue.length(), "and "); + } + return string.toString(); + } + + private List getCurrentObjectives() { + List list = new ArrayList(); + for (Objective objective : Bukkit.getScoreboardManager().getMainScoreboard().getObjectives()) { + list.add(objective.getName()); + } + Collections.sort(list, String.CASE_INSENSITIVE_ORDER); + return list; + } + + private List getCurrentEntries() { + List list = new ArrayList(); + for (String entry : Bukkit.getScoreboardManager().getMainScoreboard().getEntries()) { + list.add(entry); + } + Collections.sort(list, String.CASE_INSENSITIVE_ORDER); + return list; + } + + private List getCurrentTeams() { + List list = new ArrayList(); + for (Team team : Bukkit.getScoreboardManager().getMainScoreboard().getTeams()) { + list.add(team.getName()); + } + Collections.sort(list, String.CASE_INSENSITIVE_ORDER); + return list; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SeedCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SeedCommand.java new file mode 100644 index 0000000..b64fd40 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SeedCommand.java @@ -0,0 +1,41 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class SeedCommand extends VanillaCommand { + public SeedCommand() { + super("seed"); + this.description = "Shows the world seed"; + this.usageMessage = "/seed"; + this.setPermission("bukkit.command.seed"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + long seed; + if (sender instanceof Player) { + seed = ((Player) sender).getWorld().getSeed(); + } else { + seed = Bukkit.getWorlds().get(0).getSeed(); + } + sender.sendMessage("Seed: " + seed); + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SetIdleTimeoutCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SetIdleTimeoutCommand.java new file mode 100644 index 0000000..6b8bb2d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SetIdleTimeoutCommand.java @@ -0,0 +1,53 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public class SetIdleTimeoutCommand extends VanillaCommand { + + public SetIdleTimeoutCommand() { + super("setidletimeout"); + this.description = "Sets the server's idle timeout"; + this.usageMessage = "/setidletimeout "; + this.setPermission("bukkit.command.setidletimeout"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length == 1) { + int minutes; + + try { + minutes = getInteger(sender, args[0], 0, Integer.MAX_VALUE, true); + } catch (NumberFormatException ex) { + sender.sendMessage(ex.getMessage()); + return true; + } + + Bukkit.getServer().setIdleTimeout(minutes); + + Command.broadcastCommandMessage(sender, "Successfully set the idle timeout to " + minutes + " minutes."); + return true; + } + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SetWorldSpawnCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SetWorldSpawnCommand.java new file mode 100644 index 0000000..18f1e2c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SetWorldSpawnCommand.java @@ -0,0 +1,91 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class SetWorldSpawnCommand extends VanillaCommand { + + public SetWorldSpawnCommand() { + super("setworldspawn"); + this.description = "Sets a worlds's spawn point. If no coordinates are specified, the player's coordinates will be used."; + this.usageMessage = "/setworldspawn OR /setworldspawn "; + this.setPermission("bukkit.command.setworldspawn"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + Player player = null; + World world; + if (sender instanceof Player) { + player = (Player) sender; + world = player.getWorld(); + } else { + world = Bukkit.getWorlds().get(0); + } + + final int x, y, z; + Float yaw = null, pitch = null; // Poweruser + + if (args.length == 0) { + if (player == null) { + sender.sendMessage("You can only perform this command as a player"); + return true; + } + + Location location = player.getLocation(); + + x = location.getBlockX(); + y = location.getBlockY(); + z = location.getBlockZ(); + // Poweruser start + yaw = new Float(location.getYaw()); + pitch = new Float(location.getPitch()); + // Poweruser end + } else if (args.length == 3) { + try { + x = getInteger(sender, args[0], MIN_COORD, MAX_COORD, true); + y = getInteger(sender, args[1], 0, world.getMaxHeight(), true); + z = getInteger(sender, args[2], MIN_COORD, MAX_COORD, true); + } catch (NumberFormatException ex) { + sender.sendMessage(ex.getMessage()); + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + // Poweruser start + if(yaw != null && pitch != null) { + world.setSpawnLocation(x, y, z, yaw.floatValue(), pitch.floatValue()); + Command.broadcastCommandMessage(sender, "Set world " + world.getName() + "'s spawnpoint to (" + x + ", " + y + ", " + z + ", yaw=" + yaw.floatValue() + ", pitch=" + pitch.floatValue()); + } else { + world.setSpawnLocation(x, y, z); + Command.broadcastCommandMessage(sender, "Set world " + world.getName() + "'s spawnpoint to (" + x + ", " + y + ", " + z + ")"); + } + // Poweruser end + + return true; + + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SpawnpointCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SpawnpointCommand.java new file mode 100644 index 0000000..4214bee --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SpawnpointCommand.java @@ -0,0 +1,87 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class SpawnpointCommand extends VanillaCommand { + + public SpawnpointCommand() { + super("spawnpoint"); + this.description = "Sets a player's spawn point"; + this.usageMessage = "/spawnpoint OR /spawnpoint OR /spawnpoint "; + this.setPermission("bukkit.command.spawnpoint"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + Player player; + + if (args.length == 0) { + if (sender instanceof Player) { + player = (Player) sender; + } else { + sender.sendMessage("Please provide a player!"); + return true; + } + } else { + player = Bukkit.getPlayerExact(args[0]); + if (player == null) { + sender.sendMessage("Can't find player " + args[0]); + return true; + } + } + + World world = player.getWorld(); + + if (args.length == 4) { + if (world != null) { + int pos = 1; + final int x, y, z; + try { + x = getInteger(sender, args[pos++], MIN_COORD, MAX_COORD, true); + y = getInteger(sender, args[pos++], 0, world.getMaxHeight()); + z = getInteger(sender, args[pos], MIN_COORD, MAX_COORD, true); + } catch(NumberFormatException ex) { + sender.sendMessage(ex.getMessage()); + return true; + } + + player.setBedSpawnLocation(new Location(world, x, y, z), true); + Command.broadcastCommandMessage(sender, "Set " + player.getDisplayName() + "'s spawnpoint to " + x + ", " + y + ", " + z); + } + } else if (args.length <= 1) { + Location location = player.getLocation(); + player.setBedSpawnLocation(location, true); + Command.broadcastCommandMessage(sender, "Set " + player.getDisplayName() + "'s spawnpoint to " + location.getX() + ", " + location.getY() + ", " + location.getZ()); + } else { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + return true; + } + + @Override + public List 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 super.tabComplete(sender, alias, args); + } + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/SpreadPlayersCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/SpreadPlayersCommand.java new file mode 100644 index 0000000..5297cf7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/SpreadPlayersCommand.java @@ -0,0 +1,265 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Team; + +public class SpreadPlayersCommand extends VanillaCommand { + private static final Random random = new Random(); + + public SpreadPlayersCommand() { + super("spreadplayers"); + this.description = "Spreads players around a point"; + this.usageMessage = "/spreadplayers "; + this.setPermission("bukkit.command.spreadplayers"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) { + return true; + } + + if (args.length < 6) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + final double x = getDouble(sender, args[0], -30000000, 30000000); + final double z = getDouble(sender, args[1], -30000000, 30000000); + final double distance = getDouble(sender, args[2]); + final double range = getDouble(sender, args[3]); + + if (distance < 0.0D) { + sender.sendMessage(ChatColor.RED + "Distance is too small."); + return false; + } + + if (range < distance + 1.0D) { + sender.sendMessage(ChatColor.RED + "Max range is too small."); + return false; + } + + final String respectTeams = args[4]; + boolean teams = false; + + if (respectTeams.equalsIgnoreCase("true")) { + teams = true; + } else if (!respectTeams.equalsIgnoreCase("false")) { + sender.sendMessage(String.format(ChatColor.RED + "'%s' is not true or false", args[4])); + return false; + } + + List players = Lists.newArrayList(); + World world = null; + + for (int i = 5; i < args.length; i++) { + Player player = Bukkit.getPlayerExact(args[i]); + if (player == null) { + continue; + } + + if (world == null) { + world = player.getWorld(); + } + players.add(player); + } + + if (world == null) { + return true; + } + + final double xRangeMin = x - range; + final double zRangeMin = z - range; + final double xRangeMax = x + range; + final double zRangeMax = z + range; + + final int spreadSize = teams ? getTeams(players) : players.size(); + + final Location[] locations = getSpreadLocations(world, spreadSize, xRangeMin, zRangeMin, xRangeMax, zRangeMax); + final int rangeSpread = range(world, distance, xRangeMin, zRangeMin, xRangeMax, zRangeMax, locations); + + if (rangeSpread == -1) { + sender.sendMessage(String.format("Could not spread %d %s around %s,%s (too many players for space - try using spread of at most %s)", spreadSize, teams ? "teams" : "players", x, z)); + return false; + } + + final double distanceSpread = spread(world, players, locations, teams); + + sender.sendMessage(String.format("Succesfully spread %d %s around %s,%s", locations.length, teams ? "teams" : "players", x, z)); + if (locations.length > 1) { + sender.sendMessage(String.format("(Average distance between %s is %s blocks apart after %s iterations)", teams ? "teams" : "players", String.format("%.2f", distanceSpread), rangeSpread)); + } + return true; + } + + private int range(World world, double distance, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax, Location[] locations) { + boolean flag = true; + double max; + + int i; + + for (i = 0; i < 10000 && flag; ++i) { + flag = false; + max = Float.MAX_VALUE; + + Location loc1; + int j; + + for (int k = 0; k < locations.length; ++k) { + Location loc2 = locations[k]; + + j = 0; + loc1 = new Location(world, 0, 0, 0); + + for (int l = 0; l < locations.length; ++l) { + if (k != l) { + Location loc3 = locations[l]; + double dis = loc2.distanceSquared(loc3); + + max = Math.min(dis, max); + if (dis < distance) { + ++j; + loc1.add(loc3.getX() - loc2.getX(), 0, 0); + loc1.add(loc3.getZ() - loc2.getZ(), 0, 0); + } + } + } + + if (j > 0) { + loc2.setX(loc2.getX() / j); + loc2.setZ(loc2.getZ() / j); + double d7 = Math.sqrt(loc1.getX() * loc1.getX() + loc1.getZ() * loc1.getZ()); + + if (d7 > 0.0D) { + loc1.setX(loc1.getX() / d7); + loc2.add(-loc1.getX(), 0, -loc1.getZ()); + } else { + double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; + double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; + loc2.setX(x); + loc2.setZ(z); + } + + flag = true; + } + + boolean swap = false; + + if (loc2.getX() < xRangeMin) { + loc2.setX(xRangeMin); + swap = true; + } else if (loc2.getX() > xRangeMax) { + loc2.setX(xRangeMax); + swap = true; + } + + if (loc2.getZ() < zRangeMin) { + loc2.setZ(zRangeMin); + swap = true; + } else if (loc2.getZ() > zRangeMax) { + loc2.setZ(zRangeMax); + swap = true; + } + if (swap) { + flag = true; + } + } + + if (!flag) { + Location[] locs = locations; + int i1 = locations.length; + + for (j = 0; j < i1; ++j) { + loc1 = locs[j]; + if (world.getHighestBlockYAt(loc1) == 0) { + double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; + double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; + locations[i] = (new Location(world, x, 0, z)); + loc1.setX(x); + loc1.setZ(z); + flag = true; + } + } + } + } + + if (i >= 10000) { + return -1; + } else { + return i; + } + } + + private double spread(World world, List list, Location[] locations, boolean teams) { + double distance = 0.0D; + int i = 0; + Map hashmap = Maps.newHashMap(); + + for (int j = 0; j < list.size(); ++j) { + Player player = list.get(j); + Location location; + + if (teams) { + Team team = player.getScoreboard().getPlayerTeam(player); + + if (!hashmap.containsKey(team)) { + hashmap.put(team, locations[i++]); + } + + location = hashmap.get(team); + } else { + location = locations[i++]; + } + + player.teleport(new Location(world, Math.floor(location.getX()) + 0.5D, world.getHighestBlockYAt((int) location.getX(), (int) location.getZ()), Math.floor(location.getZ()) + 0.5D)); + double value = Double.MAX_VALUE; + + for (int k = 0; k < locations.length; ++k) { + if (location != locations[k]) { + double d = location.distanceSquared(locations[k]); + value = Math.min(d, value); + } + } + + distance += value; + } + + distance /= list.size(); + return distance; + } + + private int getTeams(List players) { + Set teams = Sets.newHashSet(); + + for (Player player : players) { + teams.add(player.getScoreboard().getPlayerTeam(player)); + } + + return teams.size(); + } + + private Location[] getSpreadLocations(World world, int size, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax) { + Location[] locations = new Location[size]; + + for (int i = 0; i < size; ++i) { + double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin; + double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin; + locations[i] = (new Location(world, x, 0, z)); + } + + return locations; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/StopCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/StopCommand.java new file mode 100644 index 0000000..7d7dfe4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/StopCommand.java @@ -0,0 +1,49 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class StopCommand extends VanillaCommand { + public StopCommand() { + super("stop"); + this.description = "Stops the server with optional reason"; + this.usageMessage = "/stop [reason]"; + this.setPermission("bukkit.command.stop"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + Command.broadcastCommandMessage(sender, "Stopping the server.."); + + Bukkit.shutdown(); + final String reason = this.createString(args, 0); + if (StringUtils.isNotEmpty(reason)) { + for (final Player player2 : Bukkit.getOnlinePlayers()) { + player2.kickPlayer(reason); + } + } + + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/TeleportCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/TeleportCommand.java new file mode 100644 index 0000000..fd1b9bd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/TeleportCommand.java @@ -0,0 +1,124 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import com.google.common.collect.ImmutableList; + +public class TeleportCommand extends VanillaCommand { + + public TeleportCommand() { + super("tp"); + this.description = "Teleports the given player (or yourself) to another player or coordinates"; + this.usageMessage = "/tp [player] and/or "; + this.setPermission("bukkit.command.teleport"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length < 1 || args.length > 4) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + Player player; + + if (args.length == 1 || args.length == 3) { + if (sender instanceof Player) { + player = (Player) sender; + } else { + sender.sendMessage("Please provide a player!"); + return true; + } + } else { + player = Bukkit.getPlayerExact(args[0]); + } + + if (player == null) { + sender.sendMessage("Player not found: " + args[0]); + return true; + } + + if (args.length < 3) { + Player target = Bukkit.getPlayerExact(args[args.length - 1]); + if (target == null) { + sender.sendMessage("Can't find player " + args[args.length - 1] + ". No tp."); + return true; + } + player.teleport(target, TeleportCause.COMMAND); + Command.broadcastCommandMessage(sender, "Teleported " + player.getDisplayName() + " to " + target.getDisplayName()); + } else if (player.getWorld() != null) { + Location playerLocation = player.getLocation(); + double x = getCoordinate(sender, playerLocation.getX(), args[args.length - 3]); + double y = getCoordinate(sender, playerLocation.getY(), args[args.length - 2], 0, 0); + double z = getCoordinate(sender, playerLocation.getZ(), args[args.length - 1]); + + if (x == MIN_COORD_MINUS_ONE || y == MIN_COORD_MINUS_ONE || z == MIN_COORD_MINUS_ONE) { + sender.sendMessage("Please provide a valid location!"); + return true; + } + + playerLocation.setX(x); + playerLocation.setY(y); + playerLocation.setZ(z); + + player.teleport(playerLocation, TeleportCause.COMMAND); + Command.broadcastCommandMessage(sender, String.format("Teleported %s to %.2f, %.2f, %.2f", player.getDisplayName(), x, y, z)); + } + return true; + } + + private double getCoordinate(CommandSender sender, double current, String input) { + return getCoordinate(sender, current, input, MIN_COORD, MAX_COORD); + } + + private double getCoordinate(CommandSender sender, double current, String input, int min, int max) { + boolean relative = input.startsWith("~"); + double result = relative ? current : 0; + + if (!relative || input.length() > 1) { + boolean exact = input.contains("."); + if (relative) input = input.substring(1); + + double testResult = getDouble(sender, input); + if (testResult == MIN_COORD_MINUS_ONE) { + return MIN_COORD_MINUS_ONE; + } + result += testResult; + + if (!exact && !relative) result += 0.5f; + } + if (min != 0 || max != 0) { + if (result < min) { + result = MIN_COORD_MINUS_ONE; + } + + if (result > max) { + result = MIN_COORD_MINUS_ONE; + } + } + + return result; + } + + @Override + public List 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 == 1 || args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/TellCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/TellCommand.java new file mode 100644 index 0000000..fc49207 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/TellCommand.java @@ -0,0 +1,60 @@ +package org.bukkit.command.defaults; + +import java.util.Arrays; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class TellCommand extends VanillaCommand { + public TellCommand() { + super("tell"); + this.description = "Sends a private message to the given player"; + this.usageMessage = "/tell "; + this.setAliases(Arrays.asList(new String[] { "w", "msg" })); + this.setPermission("bukkit.command.tell"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + Player player = Bukkit.getPlayerExact(args[0]); + + // If a player is hidden from the sender pretend they are offline + if (player == null || (sender instanceof Player && !((Player) sender).canSee(player))) { + sender.sendMessage("There's no player by that name online."); + } else { + StringBuilder message = new StringBuilder(); + + for (int i = 1; i < args.length; i++) { + if (i > 1) message.append(" "); + message.append(args[i]); + } + + String result = ChatColor.GRAY + sender.getName() + " whispers " + message; + + sender.sendMessage("[" + sender.getName() + "->" + player.getName() + "] " + message); + player.sendMessage(result); + } + + return true; + } + + // Spigot Start + @Override + public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException + { + if ( args.length == 0 ) + { + return super.tabComplete( sender, alias, args ); + } + return java.util.Collections.emptyList(); + } + // Spigot End +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/TestForCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/TestForCommand.java new file mode 100644 index 0000000..a687fef --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/TestForCommand.java @@ -0,0 +1,38 @@ +package org.bukkit.command.defaults; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +public class TestForCommand extends VanillaCommand { + public TestForCommand() { + super("testfor"); + this.description = "Tests whether a specifed player is online"; + this.usageMessage = "/testfor "; + this.setPermission("bukkit.command.testfor"); + } + + @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 false; + } + + sender.sendMessage(ChatColor.RED + "/testfor is only usable by commandblocks with analog output."); + return true; + } + + // Spigot Start + @Override + public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException + { + if ( args.length == 0 ) + { + return super.tabComplete( sender, alias, args ); + } + return java.util.Collections.emptyList(); + } + // Spigot End +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/TimeCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/TimeCommand.java new file mode 100644 index 0000000..86083b4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/TimeCommand.java @@ -0,0 +1,88 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class TimeCommand extends VanillaCommand { + private static final List TABCOMPLETE_ADD_SET = ImmutableList.of("add", "set"); + private static final List TABCOMPLETE_DAY_NIGHT = ImmutableList.of("day", "night"); + + public TimeCommand() { + super("time"); + this.description = "Changes the time on each world"; + this.usageMessage = "/time set \n/time add "; + this.setPermission("bukkit.command.time.add;bukkit.command.time.set"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Incorrect usage. Correct usage:\n" + usageMessage); + return false; + } + + int value; + + if (args[0].equals("set")) { + if (!sender.hasPermission("bukkit.command.time.set")) { + sender.sendMessage(ChatColor.RED + "You don't have permission to set the time"); + return true; + } + + if (args[1].equals("day")) { + value = 0; + } else if (args[1].equals("night")) { + value = 12500; + } else { + value = getInteger(sender, args[1], 0); + } + + for (World world : Bukkit.getWorlds()) { + world.setTime(value); + } + + Command.broadcastCommandMessage(sender, "Set time to " + value); + } else if (args[0].equals("add")) { + if (!sender.hasPermission("bukkit.command.time.add")) { + sender.sendMessage(ChatColor.RED + "You don't have permission to set the time"); + return true; + } + + value = getInteger(sender, args[1], 0); + + for (World world : Bukkit.getWorlds()) { + world.setFullTime(world.getFullTime() + value); + } + + Command.broadcastCommandMessage(sender, "Added " + value + " to time"); + } else { + sender.sendMessage("Unknown method. Usage: " + usageMessage); + } + + return true; + } + + @Override + public List 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], TABCOMPLETE_ADD_SET, new ArrayList(TABCOMPLETE_ADD_SET.size())); + } else if (args.length == 2 && args[0].equalsIgnoreCase("set")) { + return StringUtil.copyPartialMatches(args[1], TABCOMPLETE_DAY_NIGHT, new ArrayList(TABCOMPLETE_DAY_NIGHT.size())); + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/TimingsCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/TimingsCommand.java new file mode 100644 index 0000000..37168f3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/TimingsCommand.java @@ -0,0 +1,253 @@ +package org.bukkit.command.defaults; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; +import org.bukkit.plugin.TimedRegisteredListener; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +// Spigot start +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.logging.Level; + +import javax.net.ssl.HttpsURLConnection; + +import org.bukkit.command.RemoteConsoleCommandSender; +import org.bukkit.plugin.SimplePluginManager; +import org.spigotmc.CustomTimingsHandler; +// Spigot end + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +public class TimingsCommand extends BukkitCommand { + private static final List TIMINGS_SUBCOMMANDS = ImmutableList.of("report", "reset", "on", "off", "paste"); // Spigot + public static long timingStart = 0; // Spigot + + public TimingsCommand(String name) { + super(name); + this.description = "Manages Spigot Timings data to see performance of the server."; // Spigot + this.usageMessage = "/timings "; // Spigot + this.setPermission("bukkit.command.timings"); + } + + // Spigot start - redesigned Timings Command + public void executeSpigotTimings(CommandSender sender, String[] args) { + if ( "on".equals( args[0] ) ) + { + ( (SimplePluginManager) Bukkit.getPluginManager() ).useTimings( true ); + CustomTimingsHandler.reload(); + sender.sendMessage( "Enabled Timings & Reset" ); + return; + } else if ( "off".equals( args[0] ) ) + { + ( (SimplePluginManager) Bukkit.getPluginManager() ).useTimings( false ); + sender.sendMessage( "Disabled Timings" ); + return; + } + + if ( !Bukkit.getPluginManager().useTimings() ) + { + sender.sendMessage( "Please enable timings by typing /timings on" ); + return; + } + + boolean paste = "paste".equals( args[0] ); + if ("reset".equals(args[0])) { + CustomTimingsHandler.reload(); + sender.sendMessage("Timings reset"); + } else if ("merged".equals(args[0]) || "report".equals(args[0]) || paste) { + long sampleTime = System.nanoTime() - timingStart; + int index = 0; + File timingFolder = new File("timings"); + timingFolder.mkdirs(); + File timings = new File(timingFolder, "timings.txt"); + ByteArrayOutputStream bout = ( paste ) ? new ByteArrayOutputStream() : null; + while (timings.exists()) timings = new File(timingFolder, "timings" + (++index) + ".txt"); + PrintStream fileTimings = null; + try { + fileTimings = ( paste ) ? new PrintStream( bout ) : new PrintStream( timings ); + + CustomTimingsHandler.printTimings(fileTimings); + fileTimings.println( "Sample time " + sampleTime + " (" + sampleTime / 1E9 + "s)" ); + + fileTimings.println( "" ); + fileTimings.println( Bukkit.spigot().getConfig().saveToString() ); + fileTimings.println( "" ); + + if ( paste ) + { + new PasteThread( sender, bout ).start(); + return; + } + + sender.sendMessage("Timings written to " + timings.getPath()); + sender.sendMessage( "Paste contents of file into form at http://aikar.co/timings.php to read results." ); + + } catch (IOException e) { + } finally { + if (fileTimings != null) { + fileTimings.close(); + } + } + } + } + // Spigot end + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length < 1) { // Spigot + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + if (true) { executeSpigotTimings(sender, args); return true; } // Spigot + if (!sender.getServer().getPluginManager().useTimings()) { + sender.sendMessage("Please enable timings by setting \"settings.plugin-profiling\" to true in bukkit.yml"); + return true; + } + + boolean separate = "separate".equals(args[0]); + if ("reset".equals(args[0])) { + for (HandlerList handlerList : HandlerList.getHandlerLists()) { + for (RegisteredListener listener : handlerList.getRegisteredListeners()) { + if (listener instanceof TimedRegisteredListener) { + ((TimedRegisteredListener)listener).reset(); + } + } + } + sender.sendMessage("Timings reset"); + } else if ("merged".equals(args[0]) || separate) { + + int index = 0; + int pluginIdx = 0; + File timingFolder = new File("timings"); + timingFolder.mkdirs(); + File timings = new File(timingFolder, "timings.txt"); + File names = null; + while (timings.exists()) timings = new File(timingFolder, "timings" + (++index) + ".txt"); + PrintStream fileTimings = null; + PrintStream fileNames = null; + try { + fileTimings = new PrintStream(timings); + if (separate) { + names = new File(timingFolder, "names" + index + ".txt"); + fileNames = new PrintStream(names); + } + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + pluginIdx++; + long totalTime = 0; + if (separate) { + fileNames.println(pluginIdx + " " + plugin.getDescription().getFullName()); + fileTimings.println("Plugin " + pluginIdx); + } + else fileTimings.println(plugin.getDescription().getFullName()); + for (RegisteredListener listener : HandlerList.getRegisteredListeners(plugin)) { + if (listener instanceof TimedRegisteredListener) { + TimedRegisteredListener trl = (TimedRegisteredListener) listener; + long time = trl.getTotalTime(); + int count = trl.getCount(); + if (count == 0) continue; + long avg = time / count; + totalTime += time; + Class eventClass = trl.getEventClass(); + if (count > 0 && eventClass != null) { + fileTimings.println(" " + eventClass.getSimpleName() + (trl.hasMultiple() ? " (and sub-classes)" : "") + " Time: " + time + " Count: " + count + " Avg: " + avg); + } + } + } + fileTimings.println(" Total time " + totalTime + " (" + totalTime / 1000000000 + "s)"); + } + sender.sendMessage("Timings written to " + timings.getPath()); + if (separate) sender.sendMessage("Names written to " + names.getPath()); + } catch (IOException e) { + } finally { + if (fileTimings != null) { + fileTimings.close(); + } + if (fileNames != null) { + fileNames.close(); + } + } + } + return true; + } + + @Override + public List 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(TIMINGS_SUBCOMMANDS.size())); + } + return ImmutableList.of(); + } + + // Spigot start + private static class PasteThread extends Thread + { + + private final CommandSender sender; + private final ByteArrayOutputStream bout; + + public PasteThread(CommandSender sender, ByteArrayOutputStream bout) + { + super( "Timings paste thread" ); + this.sender = sender; + this.bout = bout; + } + + @Override + public synchronized void start() { + if (sender instanceof RemoteConsoleCommandSender) { + run(); + } else { + super.start(); + } + } + + + @Override + public void run() { + try { + final HttpURLConnection con = (HttpURLConnection)new URL("https://timings.spigotmc.org/paste").openConnection(); + con.setDoOutput(true); + con.setRequestMethod("POST"); + con.setInstanceFollowRedirects(false); + try (final OutputStream out = con.getOutputStream()) { + out.write(this.bout.toByteArray()); + } + final JsonObject location = new Gson().fromJson(new InputStreamReader(con.getInputStream()), JsonObject.class); + con.getInputStream().close(); + final String pasteID = location.get("key").getAsString(); + this.sender.sendMessage(ChatColor.GREEN + "Timings results can be viewed at https://www.spigotmc.org/go/timings?url=" + pasteID); + } + catch (IOException ex) { + this.sender.sendMessage(ChatColor.RED + "Error pasting timings, check your console for more information"); + Bukkit.getServer().getLogger().log(Level.WARNING, "Could not paste timings", ex); + } + } + } + // Spigot end +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java new file mode 100644 index 0000000..ac78bfb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java @@ -0,0 +1,56 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.ImmutableList; + +public class ToggleDownfallCommand extends VanillaCommand { + public ToggleDownfallCommand() { + super("toggledownfall"); + this.description = "Toggles rain on/off on a given world"; + this.usageMessage = "/toggledownfall"; + this.setPermission("bukkit.command.toggledownfall"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + World world = null; + + if (args.length == 1) { + world = Bukkit.getWorld(args[0]); + + if (world == null) { + sender.sendMessage(ChatColor.RED + "No world exists with the name '" + args[0] + "'"); + return true; + } + } else if (sender instanceof Player) { + world = ((Player) sender).getWorld(); + } else { + world = Bukkit.getWorlds().get(0); + } + + Command.broadcastCommandMessage(sender, "Toggling downfall " + (world.hasStorm() ? "off" : "on") + " for world '" + world.getName() + "'"); + world.setStorm(!world.hasStorm()); + + return true; + } + + @Override + public List 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"); + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/VanillaCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/VanillaCommand.java new file mode 100644 index 0000000..9b13ac6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/VanillaCommand.java @@ -0,0 +1,110 @@ +package org.bukkit.command.defaults; + +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public abstract class VanillaCommand extends Command { + static final int MAX_COORD = 30000000; + static final int MIN_COORD_MINUS_ONE = -30000001; + static final int MIN_COORD = -30000000; + + protected VanillaCommand(String name) { + super(name); + } + + protected VanillaCommand(String name, String description, String usageMessage, List aliases) { + super(name, description, usageMessage, aliases); + } + + public boolean matches(String input) { + return input.equalsIgnoreCase(this.getName()); + } + + protected int getInteger(CommandSender sender, String value, int min) { + return getInteger(sender, value, min, Integer.MAX_VALUE); + } + + int getInteger(CommandSender sender, String value, int min, int max) { + return getInteger(sender, value, min, max, false); + } + + int getInteger(CommandSender sender, String value, int min, int max, boolean Throws) { + int i = min; + + try { + i = Integer.valueOf(value); + } catch (NumberFormatException ex) { + if (Throws) { + throw new NumberFormatException(String.format("%s is not a valid number", value)); + } + } + + if (i < min) { + i = min; + } else if (i > max) { + i = max; + } + + return i; + } + + Integer getInteger(String value) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException ex) { + return null; + } + } + + public static double getRelativeDouble(double original, CommandSender sender, String input) { + if (input.startsWith("~")) { + double value = getDouble(sender, input.substring(1)); + if (value == MIN_COORD_MINUS_ONE) { + return MIN_COORD_MINUS_ONE; + } + return original + value; + } else { + return getDouble(sender, input); + } + } + + public static double getDouble(CommandSender sender, String input) { + try { + return Double.parseDouble(input); + } catch (NumberFormatException ex) { + return MIN_COORD_MINUS_ONE; + } + } + + public static double getDouble(CommandSender sender, String input, double min, double max) { + double result = getDouble(sender, input); + + // TODO: This should throw an exception instead. + if (result < min) { + result = min; + } else if (result > max) { + result = max; + } + + return result; + } + + String createString(String[] args, int start) { + return createString(args, start, " "); + } + + String createString(String[] args, int start, String glue) { + StringBuilder string = new StringBuilder(); + + for (int x = start; x < args.length; x++) { + string.append(args[x]); + if (x != args.length - 1) { + string.append(glue); + } + } + + return string.toString(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/VersionCommand.java new file mode 100644 index 0000000..902f9d1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/VersionCommand.java @@ -0,0 +1,129 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class VersionCommand extends BukkitCommand { + public VersionCommand(String name) { + super(name); + + this.description = "Gets the version of this server including any plugins in use"; + this.usageMessage = "/version [plugin name]"; + this.setPermission("bukkit.command.version"); + this.setAliases(Arrays.asList("ver", "about")); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length == 0) { + sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); + } else { + StringBuilder name = new StringBuilder(); + + for (String arg : args) { + if (name.length() > 0) { + name.append(' '); + } + + name.append(arg); + } + + String pluginName = name.toString(); + Plugin exactPlugin = Bukkit.getPluginManager().getPlugin(pluginName); + if (exactPlugin != null) { + describeToSender(exactPlugin, sender); + return true; + } + + boolean found = false; + pluginName = pluginName.toLowerCase(); + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + if (plugin.getName().toLowerCase().contains(pluginName)) { + describeToSender(plugin, sender); + found = true; + } + } + + if (!found) { + sender.sendMessage("This server is not running any plugin by that name."); + sender.sendMessage("Use /plugins to get a list of plugins."); + } + } + return true; + } + + private void describeToSender(Plugin plugin, CommandSender sender) { + PluginDescriptionFile desc = plugin.getDescription(); + sender.sendMessage(ChatColor.GREEN + desc.getName() + ChatColor.WHITE + " version " + ChatColor.GREEN + desc.getVersion()); + + if (desc.getDescription() != null) { + sender.sendMessage(desc.getDescription()); + } + + if (desc.getWebsite() != null) { + sender.sendMessage("Website: " + ChatColor.GREEN + desc.getWebsite()); + } + + if (!desc.getAuthors().isEmpty()) { + if (desc.getAuthors().size() == 1) { + sender.sendMessage("Author: " + getAuthors(desc)); + } else { + sender.sendMessage("Authors: " + getAuthors(desc)); + } + } + } + + private String getAuthors(final PluginDescriptionFile desc) { + StringBuilder result = new StringBuilder(); + List authors = desc.getAuthors(); + + for (int i = 0; i < authors.size(); i++) { + if (result.length() > 0) { + result.append(ChatColor.WHITE); + + if (i < authors.size() - 1) { + result.append(", "); + } else { + result.append(" and "); + } + } + + result.append(ChatColor.GREEN); + result.append(authors.get(i)); + } + + return result.toString(); + } + + @Override + public List 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) { + List completions = new ArrayList(); + String toComplete = args[0].toLowerCase(); + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + if (StringUtil.startsWithIgnoreCase(plugin.getName(), toComplete)) { + completions.add(plugin.getName()); + } + } + return completions; + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/WeatherCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/WeatherCommand.java new file mode 100644 index 0000000..a39c1b0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/WeatherCommand.java @@ -0,0 +1,73 @@ +package org.bukkit.command.defaults; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class WeatherCommand extends VanillaCommand { + private static final List WEATHER_TYPES = ImmutableList.of("clear", "rain", "thunder"); + + public WeatherCommand() { + super("weather"); + this.description = "Changes the weather"; + this.usageMessage = "/weather [duration in seconds]"; + this.setPermission("bukkit.command.weather"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + if (args.length == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + int duration = (300 + new Random().nextInt(600)) * 20; + if (args.length >= 2) { + duration = getInteger(sender, args[1], 1, 1000000) * 20; + } + + World world = Bukkit.getWorlds().get(0); + + world.setWeatherDuration(duration); + world.setThunderDuration(duration); + + if ("clear".equalsIgnoreCase(args[0])) { + world.setStorm(false); + world.setThundering(false); + Command.broadcastCommandMessage(sender, "Changed weather to clear for " + (duration / 20) + " seconds."); + } else if ("rain".equalsIgnoreCase(args[0])) { + world.setStorm(true); + world.setThundering(false); + Command.broadcastCommandMessage(sender, "Changed weather to rainy for " + (duration / 20) + " seconds."); + } else if ("thunder".equalsIgnoreCase(args[0])) { + world.setStorm(true); + world.setThundering(true); + Command.broadcastCommandMessage(sender, "Changed weather to thundering " + (duration / 20) + " seconds."); + } + + return true; + } + + @Override + public List 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], WEATHER_TYPES, new ArrayList(WEATHER_TYPES.size())); + } + + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java b/vspigot-api/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java new file mode 100644 index 0000000..6559b33 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java @@ -0,0 +1,127 @@ +package org.bukkit.command.defaults; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; + +public class WhitelistCommand extends VanillaCommand { + private static final List WHITELIST_SUBCOMMANDS = ImmutableList.of("add", "remove", "on", "off", "list", "reload"); + + public WhitelistCommand() { + super("whitelist"); + this.description = "Manages the list of players allowed to use this server"; + this.usageMessage = "/whitelist (add|remove) \n/whitelist (on|off|list|reload)"; + this.setPermission("bukkit.command.whitelist.reload;bukkit.command.whitelist.enable;bukkit.command.whitelist.disable;bukkit.command.whitelist.list;bukkit.command.whitelist.add;bukkit.command.whitelist.remove"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length == 1) { + if (args[0].equalsIgnoreCase("reload")) { + if (badPerm(sender, "reload")) return true; + + Bukkit.reloadWhitelist(); + Command.broadcastCommandMessage(sender, "Reloaded white-list from file"); + return true; + } else if (args[0].equalsIgnoreCase("on")) { + if (badPerm(sender, "enable")) return true; + + Bukkit.setWhitelist(true); + Command.broadcastCommandMessage(sender, "Turned on white-listing"); + return true; + } else if (args[0].equalsIgnoreCase("off")) { + if (badPerm(sender, "disable")) return true; + + Bukkit.setWhitelist(false); + Command.broadcastCommandMessage(sender, "Turned off white-listing"); + return true; + } else if (args[0].equalsIgnoreCase("list")) { + if (badPerm(sender, "list")) return true; + + StringBuilder result = new StringBuilder(); + + for (OfflinePlayer player : Bukkit.getWhitelistedPlayers()) { + if (result.length() > 0) { + result.append(", "); + } + + result.append(player.getName()); + } + + sender.sendMessage("White-listed players: " + result.toString()); + return true; + } + } else if (args.length == 2) { + if (args[0].equalsIgnoreCase("add")) { + if (badPerm(sender, "add")) return true; + + Bukkit.getOfflinePlayer(args[1]).setWhitelisted(true); + + Command.broadcastCommandMessage(sender, "Added " + args[1] + " to white-list"); + return true; + } else if (args[0].equalsIgnoreCase("remove")) { + if (badPerm(sender, "remove")) return true; + + Bukkit.getOfflinePlayer(args[1]).setWhitelisted(false); + + Command.broadcastCommandMessage(sender, "Removed " + args[1] + " from white-list"); + return true; + } + } + + sender.sendMessage(ChatColor.RED + "Correct command usage:\n" + usageMessage); + return false; + } + + private boolean badPerm(CommandSender sender, String perm) { + if (!sender.hasPermission("bukkit.command.whitelist." + perm)) { + sender.sendMessage(ChatColor.RED + "You do not have permission to perform this action."); + return true; + } + + return false; + } + + @Override + public List 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], WHITELIST_SUBCOMMANDS, new ArrayList(WHITELIST_SUBCOMMANDS.size())); + } else if (args.length == 2) { + if (args[0].equalsIgnoreCase("add")) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getOnlinePlayers()) { // Spigot - well maybe sometimes you haven't turned the whitelist on just yet. + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[1]) && !player.isWhitelisted()) { + completions.add(name); + } + } + return completions; + } else if (args[0].equalsIgnoreCase("remove")) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getWhitelistedPlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[1])) { + completions.add(name); + } + } + return completions; + } + } + return ImmutableList.of(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/Configuration.java b/vspigot-api/src/main/java/org/bukkit/configuration/Configuration.java new file mode 100644 index 0000000..9289c21 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/Configuration.java @@ -0,0 +1,84 @@ +package org.bukkit.configuration; + +import java.util.Map; + +/** + * Represents a source of configurable options and settings + */ +public interface Configuration extends ConfigurationSection { + /** + * Sets the default value of the given path as provided. + *

          + * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default value. + *

          + * If value is null, the value will be removed from the default + * Configuration source. + * + * @param path Path of the value to set. + * @param value Value to set the default to. + * @throws IllegalArgumentException Thrown if path is null. + */ + public void addDefault(String path, Object value); + + /** + * Sets the default values of the given paths as provided. + *

          + * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default values. + * + * @param defaults A map of Path->Values to add to defaults. + * @throws IllegalArgumentException Thrown if defaults is null. + */ + public void addDefaults(Map defaults); + + /** + * Sets the default values of the given paths as provided. + *

          + * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default value. + *

          + * This method will not hold a reference to the specified Configuration, + * nor will it automatically update if that Configuration ever changes. If + * you require this, you should set the default source with {@link + * #setDefaults(org.bukkit.configuration.Configuration)}. + * + * @param defaults A configuration holding a list of defaults to copy. + * @throws IllegalArgumentException Thrown if defaults is null or this. + */ + public void addDefaults(Configuration defaults); + + /** + * Sets the source of all default values for this {@link Configuration}. + *

          + * If a previous source was set, or previous default values were defined, + * then they will not be copied to the new source. + * + * @param defaults New source of default values for this configuration. + * @throws IllegalArgumentException Thrown if defaults is null or this. + */ + public void setDefaults(Configuration defaults); + + /** + * Gets the source {@link Configuration} for this configuration. + *

          + * If no configuration source was set, but default values were added, then + * a {@link MemoryConfiguration} will be returned. If no source was set + * and no defaults were set, then this method will return null. + * + * @return Configuration source for default values, or null if none exist. + */ + public Configuration getDefaults(); + + /** + * Gets the {@link ConfigurationOptions} for this {@link Configuration}. + *

          + * All setters through this method are chainable. + * + * @return Options for this configuration + */ + public ConfigurationOptions options(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/ConfigurationOptions.java b/vspigot-api/src/main/java/org/bukkit/configuration/ConfigurationOptions.java new file mode 100644 index 0000000..2f59382 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/ConfigurationOptions.java @@ -0,0 +1,90 @@ +package org.bukkit.configuration; + +/** + * Various settings for controlling the input and output of a {@link + * Configuration} + */ +public class ConfigurationOptions { + private char pathSeparator = '.'; + private boolean copyDefaults = false; + private final Configuration configuration; + + protected ConfigurationOptions(Configuration configuration) { + this.configuration = configuration; + } + + /** + * Returns the {@link Configuration} that this object is responsible for. + * + * @return Parent configuration + */ + public Configuration configuration() { + return configuration; + } + + /** + * Gets the char that will be used to separate {@link + * ConfigurationSection}s + *

          + * This value does not affect how the {@link Configuration} is stored, + * only in how you access the data. The default value is '.'. + * + * @return Path separator + */ + public char pathSeparator() { + return pathSeparator; + } + + /** + * Sets the char that will be used to separate {@link + * ConfigurationSection}s + *

          + * This value does not affect how the {@link Configuration} is stored, + * only in how you access the data. The default value is '.'. + * + * @param value Path separator + * @return This object, for chaining + */ + public ConfigurationOptions pathSeparator(char value) { + this.pathSeparator = value; + return this; + } + + /** + * Checks if the {@link Configuration} should copy values from its default + * {@link Configuration} directly. + *

          + * If this is true, all values in the default Configuration will be + * directly copied, making it impossible to distinguish between values + * that were set and values that are provided by default. As a result, + * {@link ConfigurationSection#contains(java.lang.String)} will always + * return the same value as {@link + * ConfigurationSection#isSet(java.lang.String)}. The default value is + * false. + * + * @return Whether or not defaults are directly copied + */ + public boolean copyDefaults() { + return copyDefaults; + } + + /** + * Sets if the {@link Configuration} should copy values from its default + * {@link Configuration} directly. + *

          + * If this is true, all values in the default Configuration will be + * directly copied, making it impossible to distinguish between values + * that were set and values that are provided by default. As a result, + * {@link ConfigurationSection#contains(java.lang.String)} will always + * return the same value as {@link + * ConfigurationSection#isSet(java.lang.String)}. The default value is + * false. + * + * @param value Whether or not defaults are directly copied + * @return This object, for chaining + */ + public ConfigurationOptions copyDefaults(boolean value) { + this.copyDefaults = value; + return this; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/ConfigurationSection.java b/vspigot-api/src/main/java/org/bukkit/configuration/ConfigurationSection.java new file mode 100644 index 0000000..9afc1dc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/ConfigurationSection.java @@ -0,0 +1,851 @@ +package org.bukkit.configuration; + +import java.util.Map; +import java.util.Set; +import java.util.List; + +import org.bukkit.Color; +import org.bukkit.OfflinePlayer; +import org.bukkit.util.Vector; +import org.bukkit.inventory.ItemStack; + +/** + * Represents a section of a {@link Configuration} + */ +public interface ConfigurationSection { + /** + * Gets a set containing all keys in this section. + *

          + * If deep is set to true, then this will contain all the keys within any + * child {@link ConfigurationSection}s (and their children, etc). These + * will be in a valid path notation for you to use. + *

          + * If deep is set to false, then this will contain only the keys of any + * direct children, and not their own children. + * + * @param deep Whether or not to get a deep list, as opposed to a shallow + * list. + * @return Set of keys contained within this ConfigurationSection. + */ + public Set getKeys(boolean deep); + + /** + * Gets a Map containing all keys and their values for this section. + *

          + * If deep is set to true, then this will contain all the keys and values + * within any child {@link ConfigurationSection}s (and their children, + * etc). These keys will be in a valid path notation for you to use. + *

          + * If deep is set to false, then this will contain only the keys and + * values of any direct children, and not their own children. + * + * @param deep Whether or not to get a deep list, as opposed to a shallow + * list. + * @return Map of keys and values of this section. + */ + public Map getValues(boolean deep); + + /** + * Checks if this {@link ConfigurationSection} contains the given path. + *

          + * If the value for the requested path does not exist but a default value + * has been specified, this will return true. + * + * @param path Path to check for existence. + * @return True if this section contains the requested path, either via + * default or being set. + * @throws IllegalArgumentException Thrown when path is null. + */ + public boolean contains(String path); + + /** + * Checks if this {@link ConfigurationSection} has a value set for the + * given path. + *

          + * If the value for the requested path does not exist but a default value + * has been specified, this will still return false. + * + * @param path Path to check for existence. + * @return True if this section contains the requested path, regardless of + * having a default. + * @throws IllegalArgumentException Thrown when path is null. + */ + public boolean isSet(String path); + + /** + * Gets the path of this {@link ConfigurationSection} from its root {@link + * Configuration} + *

          + * For any {@link Configuration} themselves, this will return an empty + * string. + *

          + * If the section is no longer contained within its root for any reason, + * such as being replaced with a different value, this may return null. + *

          + * To retrieve the single name of this section, that is, the final part of + * the path returned by this method, you may use {@link #getName()}. + * + * @return Path of this section relative to its root + */ + public String getCurrentPath(); + + /** + * Gets the name of this individual {@link ConfigurationSection}, in the + * path. + *

          + * This will always be the final part of {@link #getCurrentPath()}, unless + * the section is orphaned. + * + * @return Name of this section + */ + public String getName(); + + /** + * Gets the root {@link Configuration} that contains this {@link + * ConfigurationSection} + *

          + * For any {@link Configuration} themselves, this will return its own + * object. + *

          + * If the section is no longer contained within its root for any reason, + * such as being replaced with a different value, this may return null. + * + * @return Root configuration containing this section. + */ + public Configuration getRoot(); + + /** + * Gets the parent {@link ConfigurationSection} that directly contains + * this {@link ConfigurationSection}. + *

          + * For any {@link Configuration} themselves, this will return null. + *

          + * If the section is no longer contained within its parent for any reason, + * such as being replaced with a different value, this may return null. + * + * @return Parent section containing this section. + */ + public ConfigurationSection getParent(); + + /** + * Gets the requested Object by path. + *

          + * If the Object does not exist but a default value has been specified, + * this will return the default value. If the Object does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the Object to get. + * @return Requested Object. + */ + public Object get(String path); + + /** + * Gets the requested Object by path, returning a default value if not + * found. + *

          + * If the Object does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the Object to get. + * @param def The default value to return if the path is not found. + * @return Requested Object. + */ + public Object get(String path, Object def); + + /** + * Sets the specified path to the given value. + *

          + * If value is null, the entry will be removed. Any existing entry will be + * replaced, regardless of what the new value is. + *

          + * Some implementations may have limitations on what you may store. See + * their individual javadocs for details. No implementations should allow + * you to store {@link Configuration}s or {@link ConfigurationSection}s, + * please use {@link #createSection(java.lang.String)} for that. + * + * @param path Path of the object to set. + * @param value New value to set the path to. + */ + public void set(String path, Object value); + + /** + * Creates an empty {@link ConfigurationSection} at the specified path. + *

          + * Any value that was previously set at this path will be overwritten. If + * the previous value was itself a {@link ConfigurationSection}, it will + * be orphaned. + * + * @param path Path to create the section at. + * @return Newly created section + */ + public ConfigurationSection createSection(String path); + + /** + * Creates a {@link ConfigurationSection} at the specified path, with + * specified values. + *

          + * Any value that was previously set at this path will be overwritten. If + * the previous value was itself a {@link ConfigurationSection}, it will + * be orphaned. + * + * @param path Path to create the section at. + * @param map The values to used. + * @return Newly created section + */ + public ConfigurationSection createSection(String path, Map map); + + // Primitives + /** + * Gets the requested String by path. + *

          + * If the String does not exist but a default value has been specified, + * this will return the default value. If the String does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the String to get. + * @return Requested String. + */ + public String getString(String path); + + /** + * Gets the requested String by path, returning a default value if not + * found. + *

          + * If the String does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the String to get. + * @param def The default value to return if the path is not found or is + * not a String. + * @return Requested String. + */ + public String getString(String path, String def); + + /** + * Checks if the specified path is a String. + *

          + * If the path exists but is not a String, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a String and return appropriately. + * + * @param path Path of the String to check. + * @return Whether or not the specified path is a String. + */ + public boolean isString(String path); + + /** + * Gets the requested int by path. + *

          + * If the int does not exist but a default value has been specified, this + * will return the default value. If the int does not exist and no default + * value was specified, this will return 0. + * + * @param path Path of the int to get. + * @return Requested int. + */ + public int getInt(String path); + + /** + * Gets the requested int by path, returning a default value if not found. + *

          + * If the int does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the int to get. + * @param def The default value to return if the path is not found or is + * not an int. + * @return Requested int. + */ + public int getInt(String path, int def); + + /** + * Checks if the specified path is an int. + *

          + * If the path exists but is not a int, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a int and return appropriately. + * + * @param path Path of the int to check. + * @return Whether or not the specified path is an int. + */ + public boolean isInt(String path); + + /** + * Gets the requested boolean by path. + *

          + * If the boolean does not exist but a default value has been specified, + * this will return the default value. If the boolean does not exist and + * no default value was specified, this will return false. + * + * @param path Path of the boolean to get. + * @return Requested boolean. + */ + public boolean getBoolean(String path); + + /** + * Gets the requested boolean by path, returning a default value if not + * found. + *

          + * If the boolean does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the boolean to get. + * @param def The default value to return if the path is not found or is + * not a boolean. + * @return Requested boolean. + */ + public boolean getBoolean(String path, boolean def); + + /** + * Checks if the specified path is a boolean. + *

          + * If the path exists but is not a boolean, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a boolean and return appropriately. + * + * @param path Path of the boolean to check. + * @return Whether or not the specified path is a boolean. + */ + public boolean isBoolean(String path); + + /** + * Gets the requested double by path. + *

          + * If the double does not exist but a default value has been specified, + * this will return the default value. If the double does not exist and no + * default value was specified, this will return 0. + * + * @param path Path of the double to get. + * @return Requested double. + */ + public double getDouble(String path); + + /** + * Gets the requested double by path, returning a default value if not + * found. + *

          + * If the double does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the double to get. + * @param def The default value to return if the path is not found or is + * not a double. + * @return Requested double. + */ + public double getDouble(String path, double def); + + /** + * Checks if the specified path is a double. + *

          + * If the path exists but is not a double, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a double and return appropriately. + * + * @param path Path of the double to check. + * @return Whether or not the specified path is a double. + */ + public boolean isDouble(String path); + + // PaperSpigot start - Add getFloat + /** + * Gets the requested float by path. + *

          + * If the float does not exist but a default value has been specified, + * this will return the default value. If the float does not exist and no + * default value was specified, this will return 0. + * + * @param path Path of the float to get. + * @return Requested float. + */ + public float getFloat(String path); + + /** + * Gets the requested float by path, returning a default value if not + * found. + *

          + * If the float does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the float to get. + * @param def The default value to return if the path is not found or is + * not a float. + * @return Requested float. + */ + public float getFloat(String path, float def); + + /** + * Checks if the specified path is a float. + *

          + * If the path exists but is not a float, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a gloat and return appropriately. + * + * @param path Path of the float to check. + * @return Whether or not the specified path is a float. + */ + public boolean isFloat(String path); + // PaperSpigot end + + /** + * Gets the requested long by path. + *

          + * If the long does not exist but a default value has been specified, this + * will return the default value. If the long does not exist and no + * default value was specified, this will return 0. + * + * @param path Path of the long to get. + * @return Requested long. + */ + public long getLong(String path); + + /** + * Gets the requested long by path, returning a default value if not + * found. + *

          + * If the long does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the long to get. + * @param def The default value to return if the path is not found or is + * not a long. + * @return Requested long. + */ + public long getLong(String path, long def); + + /** + * Checks if the specified path is a long. + *

          + * If the path exists but is not a long, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a long and return appropriately. + * + * @param path Path of the long to check. + * @return Whether or not the specified path is a long. + */ + public boolean isLong(String path); + + // Java + /** + * Gets the requested List by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the List to get. + * @return Requested List. + */ + public List getList(String path); + + /** + * Gets the requested List by path, returning a default value if not + * found. + *

          + * If the List does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the List to get. + * @param def The default value to return if the path is not found or is + * not a List. + * @return Requested List. + */ + public List getList(String path, List def); + + /** + * Checks if the specified path is a List. + *

          + * If the path exists but is not a List, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a List and return appropriately. + * + * @param path Path of the List to check. + * @return Whether or not the specified path is a List. + */ + public boolean isList(String path); + + /** + * Gets the requested List of String by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a String if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of String. + */ + public List getStringList(String path); + + /** + * Gets the requested List of Integer by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Integer if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Integer. + */ + public List getIntegerList(String path); + + /** + * Gets the requested List of Boolean by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Boolean if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Boolean. + */ + public List getBooleanList(String path); + + /** + * Gets the requested List of Double by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Double if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Double. + */ + public List getDoubleList(String path); + + /** + * Gets the requested List of Float by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Float if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Float. + */ + public List getFloatList(String path); + + /** + * Gets the requested List of Long by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Long if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Long. + */ + public List getLongList(String path); + + /** + * Gets the requested List of Byte by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Byte if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Byte. + */ + public List getByteList(String path); + + /** + * Gets the requested List of Character by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Character if + * possible, but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Character. + */ + public List getCharacterList(String path); + + /** + * Gets the requested List of Short by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Short if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Short. + */ + public List getShortList(String path); + + /** + * Gets the requested List of Maps by path. + *

          + * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

          + * This method will attempt to cast any values into a Map if possible, but + * may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Maps. + */ + public List> getMapList(String path); + + // Bukkit + /** + * Gets the requested Vector by path. + *

          + * If the Vector does not exist but a default value has been specified, + * this will return the default value. If the Vector does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the Vector to get. + * @return Requested Vector. + */ + public Vector getVector(String path); + + /** + * Gets the requested {@link Vector} by path, returning a default value if + * not found. + *

          + * If the Vector does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the Vector to get. + * @param def The default value to return if the path is not found or is + * not a Vector. + * @return Requested Vector. + */ + public Vector getVector(String path, Vector def); + + /** + * Checks if the specified path is a Vector. + *

          + * If the path exists but is not a Vector, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a Vector and return appropriately. + * + * @param path Path of the Vector to check. + * @return Whether or not the specified path is a Vector. + */ + public boolean isVector(String path); + + /** + * Gets the requested OfflinePlayer by path. + *

          + * If the OfflinePlayer does not exist but a default value has been + * specified, this will return the default value. If the OfflinePlayer + * does not exist and no default value was specified, this will return + * null. + * + * @param path Path of the OfflinePlayer to get. + * @return Requested OfflinePlayer. + */ + public OfflinePlayer getOfflinePlayer(String path); + + /** + * Gets the requested {@link OfflinePlayer} by path, returning a default + * value if not found. + *

          + * If the OfflinePlayer does not exist then the specified default value + * will returned regardless of if a default has been identified in the + * root {@link Configuration}. + * + * @param path Path of the OfflinePlayer to get. + * @param def The default value to return if the path is not found or is + * not an OfflinePlayer. + * @return Requested OfflinePlayer. + */ + public OfflinePlayer getOfflinePlayer(String path, OfflinePlayer def); + + /** + * Checks if the specified path is an OfflinePlayer. + *

          + * If the path exists but is not a OfflinePlayer, this will return false. + * If the path does not exist, this will return false. If the path does + * not exist but a default value has been specified, this will check if + * that default value is a OfflinePlayer and return appropriately. + * + * @param path Path of the OfflinePlayer to check. + * @return Whether or not the specified path is an OfflinePlayer. + */ + public boolean isOfflinePlayer(String path); + + /** + * Gets the requested ItemStack by path. + *

          + * If the ItemStack does not exist but a default value has been specified, + * this will return the default value. If the ItemStack does not exist and + * no default value was specified, this will return null. + * + * @param path Path of the ItemStack to get. + * @return Requested ItemStack. + */ + public ItemStack getItemStack(String path); + + /** + * Gets the requested {@link ItemStack} by path, returning a default value + * if not found. + *

          + * If the ItemStack does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the ItemStack to get. + * @param def The default value to return if the path is not found or is + * not an ItemStack. + * @return Requested ItemStack. + */ + public ItemStack getItemStack(String path, ItemStack def); + + /** + * Checks if the specified path is an ItemStack. + *

          + * If the path exists but is not a ItemStack, this will return false. If + * the path does not exist, this will return false. If the path does not + * exist but a default value has been specified, this will check if that + * default value is a ItemStack and return appropriately. + * + * @param path Path of the ItemStack to check. + * @return Whether or not the specified path is an ItemStack. + */ + public boolean isItemStack(String path); + + /** + * Gets the requested Color by path. + *

          + * If the Color does not exist but a default value has been specified, + * this will return the default value. If the Color does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the Color to get. + * @return Requested Color. + */ + public Color getColor(String path); + + /** + * Gets the requested {@link Color} by path, returning a default value if + * not found. + *

          + * If the Color does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link Configuration}. + * + * @param path Path of the Color to get. + * @param def The default value to return if the path is not found or is + * not a Color. + * @return Requested Color. + */ + public Color getColor(String path, Color def); + + /** + * Checks if the specified path is a Color. + *

          + * If the path exists but is not a Color, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a Color and return appropriately. + * + * @param path Path of the Color to check. + * @return Whether or not the specified path is a Color. + */ + public boolean isColor(String path); + + /** + * Gets the requested ConfigurationSection by path. + *

          + * If the ConfigurationSection does not exist but a default value has been + * specified, this will return the default value. If the + * ConfigurationSection does not exist and no default value was specified, + * this will return null. + * + * @param path Path of the ConfigurationSection to get. + * @return Requested ConfigurationSection. + */ + public ConfigurationSection getConfigurationSection(String path); + + /** + * Checks if the specified path is a ConfigurationSection. + *

          + * If the path exists but is not a ConfigurationSection, this will return + * false. If the path does not exist, this will return false. If the path + * does not exist but a default value has been specified, this will check + * if that default value is a ConfigurationSection and return + * appropriately. + * + * @param path Path of the ConfigurationSection to check. + * @return Whether or not the specified path is a ConfigurationSection. + */ + public boolean isConfigurationSection(String path); + + /** + * Gets the equivalent {@link ConfigurationSection} from the default + * {@link Configuration} defined in {@link #getRoot()}. + *

          + * If the root contains no defaults, or the defaults doesn't contain a + * value for this path, or the value at this path is not a {@link + * ConfigurationSection} then this will return null. + * + * @return Equivalent section in root configuration + */ + public ConfigurationSection getDefaultSection(); + + /** + * Sets the default value in the root at the given path as provided. + *

          + * If no source {@link Configuration} was provided as a default + * collection, then a new {@link MemoryConfiguration} will be created to + * hold the new default value. + *

          + * If value is null, the value will be removed from the default + * Configuration source. + *

          + * If the value as returned by {@link #getDefaultSection()} is null, then + * this will create a new section at the path, replacing anything that may + * have existed there previously. + * + * @param path Path of the value to set. + * @param value Value to set the default to. + * @throws IllegalArgumentException Thrown if path is null. + */ + public void addDefault(String path, Object value); +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/InvalidConfigurationException.java b/vspigot-api/src/main/java/org/bukkit/configuration/InvalidConfigurationException.java new file mode 100644 index 0000000..d23480e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/InvalidConfigurationException.java @@ -0,0 +1,45 @@ +package org.bukkit.configuration; + +/** + * Exception thrown when attempting to load an invalid {@link Configuration} + */ +@SuppressWarnings("serial") +public class InvalidConfigurationException extends Exception { + + /** + * Creates a new instance of InvalidConfigurationException without a + * message or cause. + */ + public InvalidConfigurationException() {} + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified message. + * + * @param msg The details of the exception. + */ + public InvalidConfigurationException(String msg) { + super(msg); + } + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified cause. + * + * @param cause The cause of the exception. + */ + public InvalidConfigurationException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified message and cause. + * + * @param cause The cause of the exception. + * @param msg The details of the exception. + */ + public InvalidConfigurationException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/MemoryConfiguration.java b/vspigot-api/src/main/java/org/bukkit/configuration/MemoryConfiguration.java new file mode 100644 index 0000000..19c27a1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/MemoryConfiguration.java @@ -0,0 +1,79 @@ +package org.bukkit.configuration; + +import java.util.Map; + +import org.apache.commons.lang.Validate; + +/** + * This is a {@link Configuration} implementation that does not save or load + * from any source, and stores all values in memory only. + * This is useful for temporary Configurations for providing defaults. + */ +public class MemoryConfiguration extends MemorySection implements Configuration { + protected Configuration defaults; + protected MemoryConfigurationOptions options; + + /** + * Creates an empty {@link MemoryConfiguration} with no default values. + */ + public MemoryConfiguration() {} + + /** + * Creates an empty {@link MemoryConfiguration} using the specified {@link + * Configuration} as a source for all default values. + * + * @param defaults Default value provider + * @throws IllegalArgumentException Thrown if defaults is null + */ + public MemoryConfiguration(Configuration defaults) { + this.defaults = defaults; + } + + @Override + public void addDefault(String path, Object value) { + Validate.notNull(path, "Path may not be null"); + + if (defaults == null) { + defaults = new MemoryConfiguration(); + } + + defaults.set(path, value); + } + + public void addDefaults(Map defaults) { + Validate.notNull(defaults, "Defaults may not be null"); + + for (Map.Entry entry : defaults.entrySet()) { + addDefault(entry.getKey(), entry.getValue()); + } + } + + public void addDefaults(Configuration defaults) { + Validate.notNull(defaults, "Defaults may not be null"); + + addDefaults(defaults.getValues(true)); + } + + public void setDefaults(Configuration defaults) { + Validate.notNull(defaults, "Defaults may not be null"); + + this.defaults = defaults; + } + + public Configuration getDefaults() { + return defaults; + } + + @Override + public ConfigurationSection getParent() { + return null; + } + + public MemoryConfigurationOptions options() { + if (options == null) { + options = new MemoryConfigurationOptions(this); + } + + return options; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/MemoryConfigurationOptions.java b/vspigot-api/src/main/java/org/bukkit/configuration/MemoryConfigurationOptions.java new file mode 100644 index 0000000..44c046c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/MemoryConfigurationOptions.java @@ -0,0 +1,28 @@ +package org.bukkit.configuration; + +/** + * Various settings for controlling the input and output of a {@link + * MemoryConfiguration} + */ +public class MemoryConfigurationOptions extends ConfigurationOptions { + protected MemoryConfigurationOptions(MemoryConfiguration configuration) { + super(configuration); + } + + @Override + public MemoryConfiguration configuration() { + return (MemoryConfiguration) super.configuration(); + } + + @Override + public MemoryConfigurationOptions copyDefaults(boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public MemoryConfigurationOptions pathSeparator(char value) { + super.pathSeparator(value); + return this; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/MemorySection.java b/vspigot-api/src/main/java/org/bukkit/configuration/MemorySection.java new file mode 100644 index 0000000..0e2b26a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/MemorySection.java @@ -0,0 +1,837 @@ +package org.bukkit.configuration; + +import static org.bukkit.util.NumberConversions.*; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.OfflinePlayer; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +/** + * A type of {@link ConfigurationSection} that is stored in memory. + */ +public class MemorySection implements ConfigurationSection { + protected final Map map = new LinkedHashMap(); + private final Configuration root; + private final ConfigurationSection parent; + private final String path; + private final String fullPath; + + /** + * Creates an empty MemorySection for use as a root {@link Configuration} + * section. + *

          + * Note that calling this without being yourself a {@link Configuration} + * will throw an exception! + * + * @throws IllegalStateException Thrown if this is not a {@link + * Configuration} root. + */ + protected MemorySection() { + if (!(this instanceof Configuration)) { + throw new IllegalStateException("Cannot construct a root MemorySection when not a Configuration"); + } + + this.path = ""; + this.fullPath = ""; + this.parent = null; + this.root = (Configuration) this; + } + + /** + * Creates an empty MemorySection with the specified parent and path. + * + * @param parent Parent section that contains this own section. + * @param path Path that you may access this section from via the root + * {@link Configuration}. + * @throws IllegalArgumentException Thrown is parent or path is null, or + * if parent contains no root Configuration. + */ + protected MemorySection(ConfigurationSection parent, String path) { + Validate.notNull(parent, "Parent cannot be null"); + Validate.notNull(path, "Path cannot be null"); + + this.path = path; + this.parent = parent; + this.root = parent.getRoot(); + + Validate.notNull(root, "Path cannot be orphaned"); + + this.fullPath = createPath(parent, path); + } + + public Set getKeys(boolean deep) { + Set result = new LinkedHashSet(); + + Configuration root = getRoot(); + if (root != null && root.options().copyDefaults()) { + ConfigurationSection defaults = getDefaultSection(); + + if (defaults != null) { + result.addAll(defaults.getKeys(deep)); + } + } + + mapChildrenKeys(result, this, deep); + + return result; + } + + public Map getValues(boolean deep) { + Map result = new LinkedHashMap(); + + Configuration root = getRoot(); + if (root != null && root.options().copyDefaults()) { + ConfigurationSection defaults = getDefaultSection(); + + if (defaults != null) { + result.putAll(defaults.getValues(deep)); + } + } + + mapChildrenValues(result, this, deep); + + return result; + } + + public boolean contains(String path) { + return get(path) != null; + } + + public boolean isSet(String path) { + Configuration root = getRoot(); + if (root == null) { + return false; + } + if (root.options().copyDefaults()) { + return contains(path); + } + return get(path, null) != null; + } + + public String getCurrentPath() { + return fullPath; + } + + public String getName() { + return path; + } + + public Configuration getRoot() { + return root; + } + + public ConfigurationSection getParent() { + return parent; + } + + public void addDefault(String path, Object value) { + Validate.notNull(path, "Path cannot be null"); + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot add default without root"); + } + if (root == this) { + throw new UnsupportedOperationException("Unsupported addDefault(String, Object) implementation"); + } + root.addDefault(createPath(this, path), value); + } + + public ConfigurationSection getDefaultSection() { + Configuration root = getRoot(); + Configuration defaults = root == null ? null : root.getDefaults(); + + if (defaults != null) { + if (defaults.isConfigurationSection(getCurrentPath())) { + return defaults.getConfigurationSection(getCurrentPath()); + } + } + + return null; + } + + public void set(String path, Object value) { + Validate.notEmpty(path, "Cannot set to an empty path"); + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot use section without a root"); + } + + final char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + String node = path.substring(i2, i1); + ConfigurationSection subSection = section.getConfigurationSection(node); + if (subSection == null) { + section = section.createSection(node); + } else { + section = subSection; + } + } + + String key = path.substring(i2); + if (section == this) { + if (value == null) { + map.remove(key); + } else { + map.put(key, value); + } + } else { + section.set(key, value); + } + } + + public Object get(String path) { + return get(path, getDefault(path)); + } + + public Object get(String path, Object def) { + Validate.notNull(path, "Path cannot be null"); + + if (path.length() == 0) { + return this; + } + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot access section without a root"); + } + + final char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + section = section.getConfigurationSection(path.substring(i2, i1)); + if (section == null) { + return def; + } + } + + String key = path.substring(i2); + if (section == this) { + Object result = map.get(key); + return (result == null) ? def : result; + } + return section.get(key, def); + } + + public ConfigurationSection createSection(String path) { + Validate.notEmpty(path, "Cannot create section at empty path"); + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot create section without a root"); + } + + final char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + String node = path.substring(i2, i1); + ConfigurationSection subSection = section.getConfigurationSection(node); + if (subSection == null) { + section = section.createSection(node); + } else { + section = subSection; + } + } + + String key = path.substring(i2); + if (section == this) { + ConfigurationSection result = new MemorySection(this, key); + map.put(key, result); + return result; + } + return section.createSection(key); + } + + public ConfigurationSection createSection(String path, Map map) { + ConfigurationSection section = createSection(path); + + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() instanceof Map) { + section.createSection(entry.getKey().toString(), (Map) entry.getValue()); + } else { + section.set(entry.getKey().toString(), entry.getValue()); + } + } + + return section; + } + + // Primitives + public String getString(String path) { + Object def = getDefault(path); + return getString(path, def != null ? def.toString() : null); + } + + public String getString(String path, String def) { + Object val = get(path, def); + return (val != null) ? val.toString() : def; + } + + public boolean isString(String path) { + Object val = get(path); + return val instanceof String; + } + + public int getInt(String path) { + Object def = getDefault(path); + return getInt(path, (def instanceof Number) ? toInt(def) : 0); + } + + public int getInt(String path, int def) { + Object val = get(path, def); + return (val instanceof Number) ? toInt(val) : def; + } + + public boolean isInt(String path) { + Object val = get(path); + return val instanceof Integer; + } + + public boolean getBoolean(String path) { + Object def = getDefault(path); + return getBoolean(path, (def instanceof Boolean) ? (Boolean) def : false); + } + + public boolean getBoolean(String path, boolean def) { + Object val = get(path, def); + return (val instanceof Boolean) ? (Boolean) val : def; + } + + public boolean isBoolean(String path) { + Object val = get(path); + return val instanceof Boolean; + } + + public double getDouble(String path) { + Object def = getDefault(path); + return getDouble(path, (def instanceof Number) ? toDouble(def) : 0); + } + + public double getDouble(String path, double def) { + Object val = get(path, def); + return (val instanceof Number) ? toDouble(val) : def; + } + + public boolean isDouble(String path) { + Object val = get(path); + return val instanceof Double; + } + + // PaperSpigot start - Add getFloat + public float getFloat(String path) { + Object def = getDefault(path); + return getFloat(path, (def instanceof Float) ? toFloat(def) : 0); + } + + public float getFloat(String path, float def) { + Object val = get(path, def); + return (val instanceof Float) ? toFloat(val) : def; + } + + public boolean isFloat(String path) { + Object val = get(path); + return val instanceof Float; + } + // PaperSpigot end + + public long getLong(String path) { + Object def = getDefault(path); + return getLong(path, (def instanceof Number) ? toLong(def) : 0); + } + + public long getLong(String path, long def) { + Object val = get(path, def); + return (val instanceof Number) ? toLong(val) : def; + } + + public boolean isLong(String path) { + Object val = get(path); + return val instanceof Long; + } + + // Java + public List getList(String path) { + Object def = getDefault(path); + return getList(path, (def instanceof List) ? (List) def : null); + } + + public List getList(String path, List def) { + Object val = get(path, def); + return (List) ((val instanceof List) ? val : def); + } + + public boolean isList(String path) { + Object val = get(path); + return val instanceof List; + } + + public List getStringList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if ((object instanceof String) || (isPrimitiveWrapper(object))) { + result.add(String.valueOf(object)); + } + } + + return result; + } + + public List getIntegerList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Integer) { + result.add((Integer) object); + } else if (object instanceof String) { + try { + result.add(Integer.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((int) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).intValue()); + } + } + + return result; + } + + public List getBooleanList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Boolean) { + result.add((Boolean) object); + } else if (object instanceof String) { + if (Boolean.TRUE.toString().equals(object)) { + result.add(true); + } else if (Boolean.FALSE.toString().equals(object)) { + result.add(false); + } + } + } + + return result; + } + + public List getDoubleList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Double) { + result.add((Double) object); + } else if (object instanceof String) { + try { + result.add(Double.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((double) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).doubleValue()); + } + } + + return result; + } + + public List getFloatList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Float) { + result.add((Float) object); + } else if (object instanceof String) { + try { + result.add(Float.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((float) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).floatValue()); + } + } + + return result; + } + + public List getLongList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Long) { + result.add((Long) object); + } else if (object instanceof String) { + try { + result.add(Long.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((long) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).longValue()); + } + } + + return result; + } + + public List getByteList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Byte) { + result.add((Byte) object); + } else if (object instanceof String) { + try { + result.add(Byte.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((byte) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).byteValue()); + } + } + + return result; + } + + public List getCharacterList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Character) { + result.add((Character) object); + } else if (object instanceof String) { + String str = (String) object; + + if (str.length() == 1) { + result.add(str.charAt(0)); + } + } else if (object instanceof Number) { + result.add((char) ((Number) object).intValue()); + } + } + + return result; + } + + public List getShortList(String path) { + List list = getList(path); + + if (list == null) { + return new ArrayList(0); + } + + List result = new ArrayList(); + + for (Object object : list) { + if (object instanceof Short) { + result.add((Short) object); + } else if (object instanceof String) { + try { + result.add(Short.valueOf((String) object)); + } catch (Exception ex) { + } + } else if (object instanceof Character) { + result.add((short) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).shortValue()); + } + } + + return result; + } + + public List> getMapList(String path) { + List list = getList(path); + List> result = new ArrayList>(); + + if (list == null) { + return result; + } + + for (Object object : list) { + if (object instanceof Map) { + result.add((Map) object); + } + } + + return result; + } + + // Bukkit + public Vector getVector(String path) { + Object def = getDefault(path); + return getVector(path, (def instanceof Vector) ? (Vector) def : null); + } + + public Vector getVector(String path, Vector def) { + Object val = get(path, def); + return (val instanceof Vector) ? (Vector) val : def; + } + + public boolean isVector(String path) { + Object val = get(path); + return val instanceof Vector; + } + + public OfflinePlayer getOfflinePlayer(String path) { + Object def = getDefault(path); + return getOfflinePlayer(path, (def instanceof OfflinePlayer) ? (OfflinePlayer) def : null); + } + + public OfflinePlayer getOfflinePlayer(String path, OfflinePlayer def) { + Object val = get(path, def); + return (val instanceof OfflinePlayer) ? (OfflinePlayer) val : def; + } + + public boolean isOfflinePlayer(String path) { + Object val = get(path); + return val instanceof OfflinePlayer; + } + + public ItemStack getItemStack(String path) { + Object def = getDefault(path); + return getItemStack(path, (def instanceof ItemStack) ? (ItemStack) def : null); + } + + public ItemStack getItemStack(String path, ItemStack def) { + Object val = get(path, def); + return (val instanceof ItemStack) ? (ItemStack) val : def; + } + + public boolean isItemStack(String path) { + Object val = get(path); + return val instanceof ItemStack; + } + + public Color getColor(String path) { + Object def = getDefault(path); + return getColor(path, (def instanceof Color) ? (Color) def : null); + } + + public Color getColor(String path, Color def) { + Object val = get(path, def); + return (val instanceof Color) ? (Color) val : def; + } + + public boolean isColor(String path) { + Object val = get(path); + return val instanceof Color; + } + + public ConfigurationSection getConfigurationSection(String path) { + Object val = get(path, null); + if (val != null) { + return (val instanceof ConfigurationSection) ? (ConfigurationSection) val : null; + } + + val = get(path, getDefault(path)); + return (val instanceof ConfigurationSection) ? createSection(path) : null; + } + + public boolean isConfigurationSection(String path) { + Object val = get(path); + return val instanceof ConfigurationSection; + } + + protected boolean isPrimitiveWrapper(Object input) { + return input instanceof Integer || input instanceof Boolean || + input instanceof Character || input instanceof Byte || + input instanceof Short || input instanceof Double || + input instanceof Long || input instanceof Float; + } + + protected Object getDefault(String path) { + Validate.notNull(path, "Path cannot be null"); + + Configuration root = getRoot(); + Configuration defaults = root == null ? null : root.getDefaults(); + return (defaults == null) ? null : defaults.get(createPath(this, path)); + } + + protected void mapChildrenKeys(Set output, ConfigurationSection section, boolean deep) { + if (section instanceof MemorySection) { + MemorySection sec = (MemorySection) section; + + for (Map.Entry entry : sec.map.entrySet()) { + output.add(createPath(section, entry.getKey(), this)); + + if ((deep) && (entry.getValue() instanceof ConfigurationSection)) { + ConfigurationSection subsection = (ConfigurationSection) entry.getValue(); + mapChildrenKeys(output, subsection, deep); + } + } + } else { + Set keys = section.getKeys(deep); + + for (String key : keys) { + output.add(createPath(section, key, this)); + } + } + } + + protected void mapChildrenValues(Map output, ConfigurationSection section, boolean deep) { + if (section instanceof MemorySection) { + MemorySection sec = (MemorySection) section; + + for (Map.Entry entry : sec.map.entrySet()) { + output.put(createPath(section, entry.getKey(), this), entry.getValue()); + + if (entry.getValue() instanceof ConfigurationSection) { + if (deep) { + mapChildrenValues(output, (ConfigurationSection) entry.getValue(), deep); + } + } + } + } else { + Map values = section.getValues(deep); + + for (Map.Entry entry : values.entrySet()) { + output.put(createPath(section, entry.getKey(), this), entry.getValue()); + } + } + } + + /** + * Creates a full path to the given {@link ConfigurationSection} from its + * root {@link Configuration}. + *

          + * You may use this method for any given {@link ConfigurationSection}, not + * only {@link MemorySection}. + * + * @param section Section to create a path for. + * @param key Name of the specified section. + * @return Full path of the section from its root. + */ + public static String createPath(ConfigurationSection section, String key) { + return createPath(section, key, (section == null) ? null : section.getRoot()); + } + + /** + * Creates a relative path to the given {@link ConfigurationSection} from + * the given relative section. + *

          + * You may use this method for any given {@link ConfigurationSection}, not + * only {@link MemorySection}. + * + * @param section Section to create a path for. + * @param key Name of the specified section. + * @param relativeTo Section to create the path relative to. + * @return Full path of the section from its root. + */ + public static String createPath(ConfigurationSection section, String key, ConfigurationSection relativeTo) { + Validate.notNull(section, "Cannot create path without a section"); + Configuration root = section.getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot create path without a root"); + } + char separator = root.options().pathSeparator(); + + StringBuilder builder = new StringBuilder(); + if (section != null) { + for (ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) { + if (builder.length() > 0) { + builder.insert(0, separator); + } + + builder.insert(0, parent.getName()); + } + } + + if ((key != null) && (key.length() > 0)) { + if (builder.length() > 0) { + builder.append(separator); + } + + builder.append(key); + } + + return builder.toString(); + } + + @Override + public String toString() { + Configuration root = getRoot(); + return new StringBuilder() + .append(getClass().getSimpleName()) + .append("[path='") + .append(getCurrentPath()) + .append("', root='") + .append(root == null ? null : root.getClass().getSimpleName()) + .append("']") + .toString(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java b/vspigot-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java new file mode 100644 index 0000000..9d6d1c6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java @@ -0,0 +1,290 @@ +package org.bukkit.configuration.file; + +import com.google.common.base.Charsets; +import com.google.common.io.Files; + +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.InvalidConfigurationException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; + +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.MemoryConfiguration; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; + +/** + * This is a base class for all File based implementations of {@link + * Configuration} + */ +public abstract class FileConfiguration extends MemoryConfiguration { + /** + * This value specified that the system default encoding should be + * completely ignored, as it cannot handle the ASCII character set, or it + * is a strict-subset of UTF8 already (plain ASCII). + * + * @deprecated temporary compatibility measure + */ + @Deprecated + public static final boolean UTF8_OVERRIDE; + /** + * This value specifies if the system default encoding is unicode, but + * cannot parse standard ASCII. + * + * @deprecated temporary compatibility measure + */ + @Deprecated + public static final boolean UTF_BIG; + /** + * This value specifies if the system supports unicode. + * + * @deprecated temporary compatibility measure + */ + @Deprecated + public static final boolean SYSTEM_UTF; + static { + final byte[] testBytes = Base64Coder.decode("ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX4NCg=="); + final String testString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n"; + final Charset defaultCharset = Charset.defaultCharset(); + final String resultString = new String(testBytes, defaultCharset); + final boolean trueUTF = defaultCharset.name().contains("UTF"); + UTF8_OVERRIDE = !testString.equals(resultString) || defaultCharset.equals(Charset.forName("US-ASCII")); + SYSTEM_UTF = trueUTF || UTF8_OVERRIDE; + UTF_BIG = trueUTF && UTF8_OVERRIDE; + } + + /** + * Creates an empty {@link FileConfiguration} with no default values. + */ + public FileConfiguration() { + super(); + } + + /** + * Creates an empty {@link FileConfiguration} using the specified {@link + * Configuration} as a source for all default values. + * + * @param defaults Default value provider + */ + public FileConfiguration(Configuration defaults) { + super(defaults); + } + + /** + * Saves this {@link FileConfiguration} to the specified location. + *

          + * If the file does not exist, it will be created. If already exists, it + * will be overwritten. If it cannot be overwritten or created, an + * exception will be thrown. + *

          + * This method will save using the system default encoding, or possibly + * using UTF8. + * + * @param file File to save to. + * @throws IOException Thrown when the given file cannot be written to for + * any reason. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void save(File file) throws IOException { + Validate.notNull(file, "File cannot be null"); + + Files.createParentDirs(file); + + String data = saveToString(); + + Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset()); + + try { + writer.write(data); + } finally { + writer.close(); + } + } + + /** + * Saves this {@link FileConfiguration} to the specified location. + *

          + * If the file does not exist, it will be created. If already exists, it + * will be overwritten. If it cannot be overwritten or created, an + * exception will be thrown. + *

          + * This method will save using the system default encoding, or possibly + * using UTF8. + * + * @param file File to save to. + * @throws IOException Thrown when the given file cannot be written to for + * any reason. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void save(String file) throws IOException { + Validate.notNull(file, "File cannot be null"); + + save(new File(file)); + } + + /** + * Saves this {@link FileConfiguration} to a string, and returns it. + * + * @return String containing this configuration. + */ + public abstract String saveToString(); + + /** + * Loads this {@link FileConfiguration} from the specified location. + *

          + * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given file. + *

          + * If the file cannot be loaded for any reason, an exception will be + * thrown. + *

          + * This will attempt to use the {@link Charset#defaultCharset()} for + * files, unless {@link #UTF8_OVERRIDE} but not {@link #UTF_BIG} is + * specified. + * + * @param file File to load from. + * @throws FileNotFoundException Thrown when the given file cannot be + * opened. + * @throws IOException Thrown when the given file cannot be read. + * @throws InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void load(File file) throws FileNotFoundException, IOException, InvalidConfigurationException { + Validate.notNull(file, "File cannot be null"); + + final FileInputStream stream = new FileInputStream(file); + + load(new InputStreamReader(stream, UTF8_OVERRIDE && !UTF_BIG ? Charsets.UTF_8 : Charset.defaultCharset())); + } + + /** + * Loads this {@link FileConfiguration} from the specified stream. + *

          + * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given stream. + *

          + * This will attempt to use the {@link Charset#defaultCharset()}, unless + * {@link #UTF8_OVERRIDE} or {@link #UTF_BIG} is specified. + * + * @param stream Stream to load from + * @throws IOException Thrown when the given file cannot be read. + * @throws InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when stream is null. + * @deprecated This does not consider encoding + * @see #load(Reader) + */ + @Deprecated + public void load(InputStream stream) throws IOException, InvalidConfigurationException { + Validate.notNull(stream, "Stream cannot be null"); + + load(new InputStreamReader(stream, UTF8_OVERRIDE ? Charsets.UTF_8 : Charset.defaultCharset())); + } + + /** + * Loads this {@link FileConfiguration} from the specified reader. + *

          + * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given stream. + * + * @param reader the reader to load from + * @throws IOException thrown when underlying reader throws an IOException + * @throws InvalidConfigurationException thrown when the reader does not + * represent a valid Configuration + * @throws IllegalArgumentException thrown when reader is null + */ + public void load(Reader reader) throws IOException, InvalidConfigurationException { + BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + + StringBuilder builder = new StringBuilder(); + + try { + String line; + + while ((line = input.readLine()) != null) { + builder.append(line); + builder.append('\n'); + } + } finally { + input.close(); + } + + loadFromString(builder.toString()); + } + + /** + * Loads this {@link FileConfiguration} from the specified location. + *

          + * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given file. + *

          + * If the file cannot be loaded for any reason, an exception will be + * thrown. + * + * @param file File to load from. + * @throws FileNotFoundException Thrown when the given file cannot be + * opened. + * @throws IOException Thrown when the given file cannot be read. + * @throws InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void load(String file) throws FileNotFoundException, IOException, InvalidConfigurationException { + Validate.notNull(file, "File cannot be null"); + + load(new File(file)); + } + + /** + * Loads this {@link FileConfiguration} from the specified string, as + * opposed to from file. + *

          + * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given string. + *

          + * If the string is invalid in any way, an exception will be thrown. + * + * @param contents Contents of a Configuration to load. + * @throws InvalidConfigurationException Thrown if the specified string is + * invalid. + * @throws IllegalArgumentException Thrown if contents is null. + */ + public abstract void loadFromString(String contents) throws InvalidConfigurationException; + + /** + * Compiles the header for this {@link FileConfiguration} and returns the + * result. + *

          + * This will use the header from {@link #options()} -> {@link + * FileConfigurationOptions#header()}, respecting the rules of {@link + * FileConfigurationOptions#copyHeader()} if set. + * + * @return Compiled header + */ + protected abstract String buildHeader(); + + @Override + public FileConfigurationOptions options() { + if (options == null) { + options = new FileConfigurationOptions(this); + } + + return (FileConfigurationOptions) options; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/file/FileConfigurationOptions.java b/vspigot-api/src/main/java/org/bukkit/configuration/file/FileConfigurationOptions.java new file mode 100644 index 0000000..ccf81e0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/file/FileConfigurationOptions.java @@ -0,0 +1,118 @@ +package org.bukkit.configuration.file; + +import org.bukkit.configuration.*; + +/** + * Various settings for controlling the input and output of a {@link + * FileConfiguration} + */ +public class FileConfigurationOptions extends MemoryConfigurationOptions { + private String header = null; + private boolean copyHeader = true; + + protected FileConfigurationOptions(MemoryConfiguration configuration) { + super(configuration); + } + + @Override + public FileConfiguration configuration() { + return (FileConfiguration) super.configuration(); + } + + @Override + public FileConfigurationOptions copyDefaults(boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public FileConfigurationOptions pathSeparator(char value) { + super.pathSeparator(value); + return this; + } + + /** + * Gets the header that will be applied to the top of the saved output. + *

          + * This header will be commented out and applied directly at the top of + * the generated output of the {@link FileConfiguration}. It is not + * required to include a newline at the end of the header as it will + * automatically be applied, but you may include one if you wish for extra + * spacing. + *

          + * Null is a valid value which will indicate that no header is to be + * applied. The default value is null. + * + * @return Header + */ + public String header() { + return header; + } + + /** + * Sets the header that will be applied to the top of the saved output. + *

          + * This header will be commented out and applied directly at the top of + * the generated output of the {@link FileConfiguration}. It is not + * required to include a newline at the end of the header as it will + * automatically be applied, but you may include one if you wish for extra + * spacing. + *

          + * Null is a valid value which will indicate that no header is to be + * applied. + * + * @param value New header + * @return This object, for chaining + */ + public FileConfigurationOptions header(String value) { + this.header = value; + return this; + } + + /** + * Gets whether or not the header should be copied from a default source. + *

          + * If this is true, if a default {@link FileConfiguration} is passed to + * {@link + * FileConfiguration#setDefaults(org.bukkit.configuration.Configuration)} + * then upon saving it will use the header from that config, instead of + * the one provided here. + *

          + * If no default is set on the configuration, or the default is not of + * type FileConfiguration, or that config has no header ({@link #header()} + * returns null) then the header specified in this configuration will be + * used. + *

          + * Defaults to true. + * + * @return Whether or not to copy the header + */ + public boolean copyHeader() { + return copyHeader; + } + + /** + * Sets whether or not the header should be copied from a default source. + *

          + * If this is true, if a default {@link FileConfiguration} is passed to + * {@link + * FileConfiguration#setDefaults(org.bukkit.configuration.Configuration)} + * then upon saving it will use the header from that config, instead of + * the one provided here. + *

          + * If no default is set on the configuration, or the default is not of + * type FileConfiguration, or that config has no header ({@link #header()} + * returns null) then the header specified in this configuration will be + * used. + *

          + * Defaults to true. + * + * @param value Whether or not to copy the header + * @return This object, for chaining + */ + public FileConfigurationOptions copyHeader(boolean value) { + copyHeader = value; + + return this; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java new file mode 100644 index 0000000..ea4c2b3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java @@ -0,0 +1,249 @@ +package org.bukkit.configuration.file; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Map; +import java.util.logging.Level; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.representer.Representer; + +/** + * An implementation of {@link Configuration} which saves all files in Yaml. + * Note that this implementation is not synchronized. + */ +public class YamlConfiguration extends FileConfiguration { + protected static final String COMMENT_PREFIX = "# "; + protected static final String BLANK_CONFIG = "{}\n"; + private final DumperOptions yamlOptions = new DumperOptions(); + private final Representer yamlRepresenter = new YamlRepresenter(); + private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions); + + @Override + public String saveToString() { + yamlOptions.setIndent(options().indent()); + yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + yamlOptions.setAllowUnicode(SYSTEM_UTF); + yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + String header = buildHeader(); + String dump = yaml.dump(getValues(false)); + + if (dump.equals(BLANK_CONFIG)) { + dump = ""; + } + + return header + dump; + } + + @Override + public void loadFromString(String contents) throws InvalidConfigurationException { + Validate.notNull(contents, "Contents cannot be null"); + + Map input; + try { + input = (Map) yaml.load(contents); + } catch (YAMLException e) { + throw new InvalidConfigurationException(e); + } catch (ClassCastException e) { + throw new InvalidConfigurationException("Top level is not a Map."); + } + + String header = parseHeader(contents); + if (header.length() > 0) { + options().header(header); + } + + if (input != null) { + convertMapsToSections(input, this); + } + } + + protected void convertMapsToSections(Map input, ConfigurationSection section) { + for (Map.Entry entry : input.entrySet()) { + String key = entry.getKey().toString(); + Object value = entry.getValue(); + + if (value instanceof Map) { + convertMapsToSections((Map) value, section.createSection(key)); + } else { + section.set(key, value); + } + } + } + + protected String parseHeader(String input) { + String[] lines = input.split("\r?\n", -1); + StringBuilder result = new StringBuilder(); + boolean readingHeader = true; + boolean foundHeader = false; + + for (int i = 0; (i < lines.length) && (readingHeader); i++) { + String line = lines[i]; + + if (line.startsWith(COMMENT_PREFIX)) { + if (i > 0) { + result.append("\n"); + } + + if (line.length() > COMMENT_PREFIX.length()) { + result.append(line.substring(COMMENT_PREFIX.length())); + } + + foundHeader = true; + } else if ((foundHeader) && (line.length() == 0)) { + result.append("\n"); + } else if (foundHeader) { + readingHeader = false; + } + } + + return result.toString(); + } + + @Override + protected String buildHeader() { + String header = options().header(); + + if (options().copyHeader()) { + Configuration def = getDefaults(); + + if ((def != null) && (def instanceof FileConfiguration)) { + FileConfiguration filedefaults = (FileConfiguration) def; + String defaultsHeader = filedefaults.buildHeader(); + + if ((defaultsHeader != null) && (defaultsHeader.length() > 0)) { + return defaultsHeader; + } + } + } + + if (header == null) { + return ""; + } + + StringBuilder builder = new StringBuilder(); + String[] lines = header.split("\r?\n", -1); + boolean startedHeader = false; + + for (int i = lines.length - 1; i >= 0; i--) { + builder.insert(0, "\n"); + + if ((startedHeader) || (lines[i].length() != 0)) { + builder.insert(0, lines[i]); + builder.insert(0, COMMENT_PREFIX); + startedHeader = true; + } + } + + return builder.toString(); + } + + @Override + public YamlConfigurationOptions options() { + if (options == null) { + options = new YamlConfigurationOptions(this); + } + + return (YamlConfigurationOptions) options; + } + + /** + * Creates a new {@link YamlConfiguration}, loading from the given file. + *

          + * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + *

          + * The encoding used may follow the system dependent default. + * + * @param file Input file + * @return Resulting configuration + * @throws IllegalArgumentException Thrown if file is null + */ + public static YamlConfiguration loadConfiguration(File file) { + Validate.notNull(file, "File cannot be null"); + + YamlConfiguration config = new YamlConfiguration(); + + try { + config.load(file); + } catch (FileNotFoundException ex) { + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file, ex); + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file , ex); + } + + return config; + } + + /** + * Creates a new {@link YamlConfiguration}, loading from the given stream. + *

          + * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + * + * @param stream Input stream + * @return Resulting configuration + * @throws IllegalArgumentException Thrown if stream is null + * @deprecated does not properly consider encoding + * @see #load(InputStream) + * @see #loadConfiguration(Reader) + */ + @Deprecated + public static YamlConfiguration loadConfiguration(InputStream stream) { + Validate.notNull(stream, "Stream cannot be null"); + + YamlConfiguration config = new YamlConfiguration(); + + try { + config.load(stream); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", ex); + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", ex); + } + + return config; + } + + + /** + * Creates a new {@link YamlConfiguration}, loading from the given reader. + *

          + * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + * + * @param reader input + * @return resulting configuration + * @throws IllegalArgumentException Thrown if stream is null + */ + public static YamlConfiguration loadConfiguration(Reader reader) { + Validate.notNull(reader, "Stream cannot be null"); + + YamlConfiguration config = new YamlConfiguration(); + + try { + config.load(reader); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", ex); + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", ex); + } + + return config; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConfigurationOptions.java b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConfigurationOptions.java new file mode 100644 index 0000000..57894e3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConfigurationOptions.java @@ -0,0 +1,71 @@ +package org.bukkit.configuration.file; + +import org.apache.commons.lang.Validate; + +/** + * Various settings for controlling the input and output of a {@link + * YamlConfiguration} + */ +public class YamlConfigurationOptions extends FileConfigurationOptions { + private int indent = 2; + + protected YamlConfigurationOptions(YamlConfiguration configuration) { + super(configuration); + } + + @Override + public YamlConfiguration configuration() { + return (YamlConfiguration) super.configuration(); + } + + @Override + public YamlConfigurationOptions copyDefaults(boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public YamlConfigurationOptions pathSeparator(char value) { + super.pathSeparator(value); + return this; + } + + @Override + public YamlConfigurationOptions header(String value) { + super.header(value); + return this; + } + + @Override + public YamlConfigurationOptions copyHeader(boolean value) { + super.copyHeader(value); + return this; + } + + /** + * Gets how much spaces should be used to indent each line. + *

          + * The minimum value this may be is 2, and the maximum is 9. + * + * @return How much to indent by + */ + public int indent() { + return indent; + } + + /** + * Sets how much spaces should be used to indent each line. + *

          + * The minimum value this may be is 2, and the maximum is 9. + * + * @param value New indent + * @return This object, for chaining + */ + public YamlConfigurationOptions indent(int value) { + Validate.isTrue(value >= 2, "Indent must be at least 2 characters"); + Validate.isTrue(value <= 9, "Indent cannot be greater than 9 characters"); + + this.indent = value; + return this; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConstructor.java b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConstructor.java new file mode 100644 index 0000000..73ad722 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlConstructor.java @@ -0,0 +1,49 @@ +package org.bukkit.configuration.file; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.nodes.Tag; + +import org.bukkit.configuration.serialization.ConfigurationSerialization; + +public class YamlConstructor extends SafeConstructor { + + public YamlConstructor() { + this.yamlConstructors.put(Tag.MAP, new ConstructCustomObject()); + } + + private class ConstructCustomObject extends ConstructYamlMap { + @Override + public Object construct(Node node) { + if (node.isTwoStepsConstruction()) { + throw new YAMLException("Unexpected referential mapping structure. Node: " + node); + } + + Map raw = (Map) super.construct(node); + + if (raw.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) { + Map typed = new LinkedHashMap(raw.size()); + for (Map.Entry entry : raw.entrySet()) { + typed.put(entry.getKey().toString(), entry.getValue()); + } + + try { + return ConfigurationSerialization.deserializeObject(typed); + } catch (IllegalArgumentException ex) { + throw new YAMLException("Could not deserialize object", ex); + } + } + + return raw; + } + + @Override + public void construct2ndStep(Node node, Object object) { + throw new YAMLException("Unexpected referential mapping structure. Node: " + node); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlRepresenter.java b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlRepresenter.java new file mode 100644 index 0000000..bc9c098 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/file/YamlRepresenter.java @@ -0,0 +1,38 @@ +package org.bukkit.configuration.file; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; + +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.representer.Representer; + +public class YamlRepresenter extends Representer { + + public YamlRepresenter() { + this.multiRepresenters.put(ConfigurationSection.class, new RepresentConfigurationSection()); + this.multiRepresenters.put(ConfigurationSerializable.class, new RepresentConfigurationSerializable()); + } + + private class RepresentConfigurationSection extends RepresentMap { + @Override + public Node representData(Object data) { + return super.representData(((ConfigurationSection) data).getValues(false)); + } + } + + private class RepresentConfigurationSerializable extends RepresentMap { + @Override + public Node representData(Object data) { + ConfigurationSerializable serializable = (ConfigurationSerializable) data; + Map values = new LinkedHashMap(); + values.put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, ConfigurationSerialization.getAlias(serializable.getClass())); + values.putAll(serializable.serialize()); + + return super.representData(values); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerializable.java b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerializable.java new file mode 100644 index 0000000..ba3c8c4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerializable.java @@ -0,0 +1,35 @@ +package org.bukkit.configuration.serialization; + +import java.util.Map; + +/** + * Represents an object that may be serialized. + *

          + * These objects MUST implement one of the following, in addition to the + * methods as defined by this interface: + *

            + *
          • A static method "deserialize" that accepts a single {@link Map}< + * {@link String}, {@link Object}> and returns the class.
          • + *
          • A static method "valueOf" that accepts a single {@link Map}<{@link + * String}, {@link Object}> and returns the class.
          • + *
          • A constructor that accepts a single {@link Map}<{@link String}, + * {@link Object}>.
          • + *
          + * In addition to implementing this interface, you must register the class + * with {@link ConfigurationSerialization#registerClass(Class)}. + * + * @see DelegateDeserialization + * @see SerializableAs + */ +public interface ConfigurationSerializable { + + /** + * Creates a Map representation of this class. + *

          + * This class must provide a method to restore this class, as defined in + * the {@link ConfigurationSerializable} interface javadocs. + * + * @return Map containing the current state of this class + */ + public Map serialize(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerialization.java b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerialization.java new file mode 100644 index 0000000..96a806f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerialization.java @@ -0,0 +1,281 @@ +package org.bukkit.configuration.serialization; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.configuration.Configuration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.BlockVector; +import org.bukkit.util.Vector; + +/** + * Utility class for storing and retrieving classes for {@link Configuration}. + */ +public class ConfigurationSerialization { + public static final String SERIALIZED_TYPE_KEY = "=="; + private final Class clazz; + private static Map> aliases = new HashMap>(); + + static { + registerClass(Vector.class); + registerClass(BlockVector.class); + registerClass(ItemStack.class); + registerClass(Color.class); + registerClass(PotionEffect.class); + registerClass(FireworkEffect.class); + } + + protected ConfigurationSerialization(Class clazz) { + this.clazz = clazz; + } + + protected Method getMethod(String name, boolean isStatic) { + try { + Method method = clazz.getDeclaredMethod(name, Map.class); + + if (!ConfigurationSerializable.class.isAssignableFrom(method.getReturnType())) { + return null; + } + if (Modifier.isStatic(method.getModifiers()) != isStatic) { + return null; + } + + return method; + } catch (NoSuchMethodException ex) { + return null; + } catch (SecurityException ex) { + return null; + } + } + + protected Constructor getConstructor() { + try { + return clazz.getConstructor(Map.class); + } catch (NoSuchMethodException ex) { + return null; + } catch (SecurityException ex) { + return null; + } + } + + protected ConfigurationSerializable deserializeViaMethod(Method method, Map args) { + try { + ConfigurationSerializable result = (ConfigurationSerializable) method.invoke(null, args); + + if (result == null) { + Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + clazz + " for deserialization: method returned null"); + } else { + return result; + } + } catch (Throwable ex) { + Logger.getLogger(ConfigurationSerialization.class.getName()).log( + Level.SEVERE, + "Could not call method '" + method.toString() + "' of " + clazz + " for deserialization", + ex instanceof InvocationTargetException ? ex.getCause() : ex); + } + + return null; + } + + protected ConfigurationSerializable deserializeViaCtor(Constructor ctor, Map args) { + try { + return ctor.newInstance(args); + } catch (Throwable ex) { + Logger.getLogger(ConfigurationSerialization.class.getName()).log( + Level.SEVERE, + "Could not call constructor '" + ctor.toString() + "' of " + clazz + " for deserialization", + ex instanceof InvocationTargetException ? ex.getCause() : ex); + } + + return null; + } + + public ConfigurationSerializable deserialize(Map args) { + Validate.notNull(args, "Args must not be null"); + + ConfigurationSerializable result = null; + Method method = null; + + if (result == null) { + method = getMethod("deserialize", true); + + if (method != null) { + result = deserializeViaMethod(method, args); + } + } + + if (result == null) { + method = getMethod("valueOf", true); + + if (method != null) { + result = deserializeViaMethod(method, args); + } + } + + if (result == null) { + Constructor constructor = getConstructor(); + + if (constructor != null) { + result = deserializeViaCtor(constructor, args); + } + } + + return result; + } + + /** + * Attempts to deserialize the given arguments into a new instance of the + * given class. + *

          + * The class must implement {@link ConfigurationSerializable}, including + * the extra methods as specified in the javadoc of + * ConfigurationSerializable. + *

          + * If a new instance could not be made, an example being the class not + * fully implementing the interface, null will be returned. + * + * @param args Arguments for deserialization + * @param clazz Class to deserialize into + * @return New instance of the specified class + */ + public static ConfigurationSerializable deserializeObject(Map args, Class clazz) { + return new ConfigurationSerialization(clazz).deserialize(args); + } + + /** + * Attempts to deserialize the given arguments into a new instance of the + * given class. + *

          + * The class must implement {@link ConfigurationSerializable}, including + * the extra methods as specified in the javadoc of + * ConfigurationSerializable. + *

          + * If a new instance could not be made, an example being the class not + * fully implementing the interface, null will be returned. + * + * @param args Arguments for deserialization + * @return New instance of the specified class + */ + public static ConfigurationSerializable deserializeObject(Map args) { + Class clazz = null; + + if (args.containsKey(SERIALIZED_TYPE_KEY)) { + try { + String alias = (String) args.get(SERIALIZED_TYPE_KEY); + + if (alias == null) { + throw new IllegalArgumentException("Cannot have null alias"); + } + clazz = getClassByAlias(alias); + if (clazz == null) { + throw new IllegalArgumentException("Specified class does not exist ('" + alias + "')"); + } + } catch (ClassCastException ex) { + ex.fillInStackTrace(); + throw ex; + } + } else { + throw new IllegalArgumentException("Args doesn't contain type key ('" + SERIALIZED_TYPE_KEY + "')"); + } + + return new ConfigurationSerialization(clazz).deserialize(args); + } + + /** + * Registers the given {@link ConfigurationSerializable} class by its + * alias + * + * @param clazz Class to register + */ + public static void registerClass(Class clazz) { + DelegateDeserialization delegate = clazz.getAnnotation(DelegateDeserialization.class); + + if (delegate == null) { + registerClass(clazz, getAlias(clazz)); + registerClass(clazz, clazz.getName()); + } + } + + /** + * Registers the given alias to the specified {@link + * ConfigurationSerializable} class + * + * @param clazz Class to register + * @param alias Alias to register as + * @see SerializableAs + */ + public static void registerClass(Class clazz, String alias) { + aliases.put(alias, clazz); + } + + /** + * Unregisters the specified alias to a {@link ConfigurationSerializable} + * + * @param alias Alias to unregister + */ + public static void unregisterClass(String alias) { + aliases.remove(alias); + } + + /** + * Unregisters any aliases for the specified {@link + * ConfigurationSerializable} class + * + * @param clazz Class to unregister + */ + public static void unregisterClass(Class clazz) { + while (aliases.values().remove(clazz)) { + ; + } + } + + /** + * Attempts to get a registered {@link ConfigurationSerializable} class by + * its alias + * + * @param alias Alias of the serializable + * @return Registered class, or null if not found + */ + public static Class getClassByAlias(String alias) { + return aliases.get(alias); + } + + /** + * Gets the correct alias for the given {@link ConfigurationSerializable} + * class + * + * @param clazz Class to get alias for + * @return Alias to use for the class + */ + public static String getAlias(Class clazz) { + DelegateDeserialization delegate = clazz.getAnnotation(DelegateDeserialization.class); + + if (delegate != null) { + if ((delegate.value() == null) || (delegate.value() == clazz)) { + delegate = null; + } else { + return getAlias(delegate.value()); + } + } + + if (delegate == null) { + SerializableAs alias = clazz.getAnnotation(SerializableAs.class); + + if ((alias != null) && (alias.value() != null)) { + return alias.value(); + } + } + + return clazz.getName(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/serialization/DelegateDeserialization.java b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/DelegateDeserialization.java new file mode 100644 index 0000000..1cfae94 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/DelegateDeserialization.java @@ -0,0 +1,22 @@ +package org.bukkit.configuration.serialization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Applies to a {@link ConfigurationSerializable} that will delegate all + * deserialization to another {@link ConfigurationSerializable}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DelegateDeserialization { + /** + * Which class should be used as a delegate for this classes + * deserialization + * + * @return Delegate class + */ + public Class value(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/configuration/serialization/SerializableAs.java b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/SerializableAs.java new file mode 100644 index 0000000..c5ee998 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/configuration/serialization/SerializableAs.java @@ -0,0 +1,34 @@ +package org.bukkit.configuration.serialization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Represents an "alias" that a {@link ConfigurationSerializable} may be + * stored as. + * If this is not present on a {@link ConfigurationSerializable} class, it + * will use the fully qualified name of the class. + *

          + * This value will be stored in the configuration so that the configuration + * deserialization can determine what type it is. + *

          + * Using this annotation on any other class than a {@link + * ConfigurationSerializable} will have no effect. + * + * @see ConfigurationSerialization#registerClass(Class, String) + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface SerializableAs { + /** + * This is the name your class will be stored and retrieved as. + *

          + * This name MUST be unique. We recommend using names such as + * "MyPluginThing" instead of "Thing". + * + * @return Name to serialize the class as. + */ + public String value(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/BooleanPrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/BooleanPrompt.java new file mode 100644 index 0000000..81ef78c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/BooleanPrompt.java @@ -0,0 +1,37 @@ +package org.bukkit.conversations; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.BooleanUtils; + +/** + * BooleanPrompt is the base class for any prompt that requires a boolean + * response from the user. + */ +public abstract class BooleanPrompt extends ValidatingPrompt{ + + public BooleanPrompt() { + super(); + } + + @Override + protected boolean isInputValid(ConversationContext context, String input) { + String[] accepted = {"true", "false", "on", "off", "yes", "no" /* Spigot: */, "y", "n", "1", "0", "right", "wrong", "correct", "incorrect", "valid", "invalid"}; // Spigot + return ArrayUtils.contains(accepted, input.toLowerCase()); + } + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, String input) { + if (input.equalsIgnoreCase("y") || input.equals("1") || input.equalsIgnoreCase("right") || input.equalsIgnoreCase("correct") || input.equalsIgnoreCase("valid")) input = "true"; // Spigot + return acceptValidatedInput(context, BooleanUtils.toBoolean(input)); + } + + /** + * Override this method to perform some action with the user's boolean + * response. + * + * @param context Context information about the conversation. + * @param input The user's boolean response. + * @return The next {@link Prompt} in the prompt graph. + */ + protected abstract Prompt acceptValidatedInput(ConversationContext context, boolean input); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/Conversable.java b/vspigot-api/src/main/java/org/bukkit/conversations/Conversable.java new file mode 100644 index 0000000..55674b5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/Conversable.java @@ -0,0 +1,57 @@ +package org.bukkit.conversations; + +import org.bukkit.command.CommandSender; + +/** + * The Conversable interface is used to indicate objects that can have + * conversations. + */ +public interface Conversable { + + /** + * Tests to see of a Conversable object is actively engaged in a + * conversation. + * + * @return True if a conversation is in progress + */ + public boolean isConversing(); + + /** + * Accepts input into the active conversation. If no conversation is in + * progress, this method does nothing. + * + * @param input The input message into the conversation + */ + public void acceptConversationInput(String input); + + /** + * Enters into a dialog with a Conversation object. + * + * @param conversation The conversation to begin + * @return True if the conversation should proceed, false if it has been + * enqueued + */ + public boolean beginConversation(Conversation conversation); + + /** + * Abandons an active conversation. + * + * @param conversation The conversation to abandon + */ + public void abandonConversation(Conversation conversation); + + /** + * Abandons an active conversation. + * + * @param conversation The conversation to abandon + * @param details Details about why the conversation was abandoned + */ + public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details); + + /** + * Sends this sender a message raw + * + * @param message Message to be displayed + */ + public void sendRawMessage(String message); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/Conversation.java b/vspigot-api/src/main/java/org/bukkit/conversations/Conversation.java new file mode 100644 index 0000000..46912c8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/Conversation.java @@ -0,0 +1,304 @@ +package org.bukkit.conversations; + +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The Conversation class is responsible for tracking the current state of a + * conversation, displaying prompts to the user, and dispatching the user's + * response to the appropriate place. Conversation objects are not typically + * instantiated directly. Instead a {@link ConversationFactory} is used to + * construct identical conversations on demand. + *

          + * Conversation flow consists of a directed graph of {@link Prompt} objects. + * Each time a prompt gets input from the user, it must return the next prompt + * in the graph. Since each Prompt chooses the next Prompt, complex + * conversation trees can be implemented where the nature of the player's + * response directs the flow of the conversation. + *

          + * Each conversation has a {@link ConversationPrefix} that prepends all output + * from the conversation to the player. The ConversationPrefix can be used to + * display the plugin name or conversation status as the conversation evolves. + *

          + * Each conversation has a timeout measured in the number of inactive seconds + * to wait before abandoning the conversation. If the inactivity timeout is + * reached, the conversation is abandoned and the user's incoming and outgoing + * chat is returned to normal. + *

          + * You should not construct a conversation manually. Instead, use the {@link + * ConversationFactory} for access to all available options. + */ +public class Conversation { + + private Prompt firstPrompt; + private boolean abandoned; + protected Prompt currentPrompt; + protected ConversationContext context; + protected boolean modal; + protected boolean localEchoEnabled; + protected ConversationPrefix prefix; + protected List cancellers; + protected List abandonedListeners; + + /** + * Initializes a new Conversation. + * + * @param plugin The plugin that owns this conversation. + * @param forWhom The entity for whom this conversation is mediating. + * @param firstPrompt The first prompt in the conversation graph. + */ + public Conversation(Plugin plugin, Conversable forWhom, Prompt firstPrompt) { + this(plugin, forWhom, firstPrompt, new HashMap()); + } + + /** + * Initializes a new Conversation. + * + * @param plugin The plugin that owns this conversation. + * @param forWhom The entity for whom this conversation is mediating. + * @param firstPrompt The first prompt in the conversation graph. + * @param initialSessionData Any initial values to put in the conversation + * context sessionData map. + */ + public Conversation(Plugin plugin, Conversable forWhom, Prompt firstPrompt, Map initialSessionData) { + this.firstPrompt = firstPrompt; + this.context = new ConversationContext(plugin, forWhom, initialSessionData); + this.modal = true; + this.localEchoEnabled = true; + this.prefix = new NullConversationPrefix(); + this.cancellers = new ArrayList(); + this.abandonedListeners = new ArrayList(); + } + + /** + * Gets the entity for whom this conversation is mediating. + * + * @return The entity. + */ + public Conversable getForWhom() { + return context.getForWhom(); + } + + /** + * Gets the modality of this conversation. If a conversation is modal, all + * messages directed to the player are suppressed for the duration of the + * conversation. + * + * @return The conversation modality. + */ + public boolean isModal() { + return modal; + } + + /** + * Sets the modality of this conversation. If a conversation is modal, + * all messages directed to the player are suppressed for the duration of + * the conversation. + * + * @param modal The new conversation modality. + */ + void setModal(boolean modal) { + this.modal = modal; + } + + /** + * Gets the status of local echo for this conversation. If local echo is + * enabled, any text submitted to a conversation gets echoed back into the + * submitter's chat window. + * + * @return The status of local echo. + */ + public boolean isLocalEchoEnabled() { + return localEchoEnabled; + } + + /** + * Sets the status of local echo for this conversation. If local echo is + * enabled, any text submitted to a conversation gets echoed back into the + * submitter's chat window. + * + * @param localEchoEnabled The status of local echo. + */ + public void setLocalEchoEnabled(boolean localEchoEnabled) { + this.localEchoEnabled = localEchoEnabled; + } + + /** + * Gets the {@link ConversationPrefix} that prepends all output from this + * conversation. + * + * @return The ConversationPrefix in use. + */ + public ConversationPrefix getPrefix() { + return prefix; + } + + /** + * Sets the {@link ConversationPrefix} that prepends all output from this + * conversation. + * + * @param prefix The ConversationPrefix to use. + */ + void setPrefix(ConversationPrefix prefix) { + this.prefix = prefix; + } + + /** + * Adds a {@link ConversationCanceller} to the cancellers collection. + * + * @param canceller The {@link ConversationCanceller} to add. + */ + void addConversationCanceller(ConversationCanceller canceller) { + canceller.setConversation(this); + this.cancellers.add(canceller); + } + + /** + * Gets the list of {@link ConversationCanceller}s + * + * @return The list. + */ + public List getCancellers() { + return cancellers; + } + + /** + * Returns the Conversation's {@link ConversationContext}. + * + * @return The ConversationContext. + */ + public ConversationContext getContext() { + return context; + } + + /** + * Displays the first prompt of this conversation and begins redirecting + * the user's chat responses. + */ + public void begin() { + if (currentPrompt == null) { + abandoned = false; + currentPrompt = firstPrompt; + context.getForWhom().beginConversation(this); + } + } + + /** + * Returns Returns the current state of the conversation. + * + * @return The current state of the conversation. + */ + public ConversationState getState() { + if (currentPrompt != null) { + return ConversationState.STARTED; + } else if (abandoned) { + return ConversationState.ABANDONED; + } else { + return ConversationState.UNSTARTED; + } + } + + /** + * Passes player input into the current prompt. The next prompt (as + * determined by the current prompt) is then displayed to the user. + * + * @param input The user's chat text. + */ + public void acceptInput(String input) { + try { // Spigot + if (currentPrompt != null) { + + // Echo the user's input + if (localEchoEnabled) { + context.getForWhom().sendRawMessage(prefix.getPrefix(context) + input); + } + + // Test for conversation abandonment based on input + for(ConversationCanceller canceller : cancellers) { + if (canceller.cancelBasedOnInput(context, input)) { + abandon(new ConversationAbandonedEvent(this, canceller)); + return; + } + } + + // Not abandoned, output the next prompt + currentPrompt = currentPrompt.acceptInput(context, input); + outputNextPrompt(); + } + // Spigot Start + } catch ( Throwable t ) + { + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.SEVERE, "Error handling conversation prompt", t ); + } + // Spigot End + } + + /** + * Adds a {@link ConversationAbandonedListener}. + * + * @param listener The listener to add. + */ + public synchronized void addConversationAbandonedListener(ConversationAbandonedListener listener) { + abandonedListeners.add(listener); + } + + /** + * Removes a {@link ConversationAbandonedListener}. + * + * @param listener The listener to remove. + */ + public synchronized void removeConversationAbandonedListener(ConversationAbandonedListener listener) { + abandonedListeners.remove(listener); + } + + /** + * Abandons and resets the current conversation. Restores the user's + * normal chat behavior. + */ + public void abandon() { + abandon(new ConversationAbandonedEvent(this, new ManuallyAbandonedConversationCanceller())); + } + + /** + * Abandons and resets the current conversation. Restores the user's + * normal chat behavior. + * + * @param details Details about why the conversation was abandoned + */ + public synchronized void abandon(ConversationAbandonedEvent details) { + if (!abandoned) { + abandoned = true; + currentPrompt = null; + context.getForWhom().abandonConversation(this); + for (ConversationAbandonedListener listener : abandonedListeners) { + listener.conversationAbandoned(details); + } + } + } + + /** + * Displays the next user prompt and abandons the conversation if the next + * prompt is null. + */ + public void outputNextPrompt() { + if (currentPrompt == null) { + abandon(new ConversationAbandonedEvent(this)); + } else { + context.getForWhom().sendRawMessage(prefix.getPrefix(context) + currentPrompt.getPromptText(context)); + if (!currentPrompt.blocksForInput(context)) { + currentPrompt = currentPrompt.acceptInput(context, null); + outputNextPrompt(); + } + } + } + + public enum ConversationState { + UNSTARTED, + STARTED, + ABANDONED + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java new file mode 100644 index 0000000..63c4a2a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationAbandonedEvent.java @@ -0,0 +1,53 @@ +package org.bukkit.conversations; + +import java.util.EventObject; + +/** + * ConversationAbandonedEvent contains information about an abandoned + * conversation. + */ +public class ConversationAbandonedEvent extends EventObject { + + private ConversationContext context; + private ConversationCanceller canceller; + + public ConversationAbandonedEvent(Conversation conversation) { + this(conversation, null); + } + + public ConversationAbandonedEvent(Conversation conversation, ConversationCanceller canceller) { + super(conversation); + this.context = conversation.getContext(); + this.canceller = canceller; + } + + /** + * Gets the object that caused the conversation to be abandoned. + * + * @return The object that abandoned the conversation. + */ + public ConversationCanceller getCanceller() { + return canceller; + } + + /** + * Gets the abandoned conversation's conversation context. + * + * @return The abandoned conversation's conversation context. + */ + public ConversationContext getContext() { + return context; + } + + /** + * Indicates how the conversation was abandoned - naturally as part of the + * prompt chain or prematurely via a {@link ConversationCanceller}. + * + * @return True if the conversation is abandoned gracefully by a {@link + * Prompt} returning null or the next prompt. False of the + * conversations is abandoned prematurely by a ConversationCanceller. + */ + public boolean gracefulExit() { + return canceller == null; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java new file mode 100644 index 0000000..dc046b1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationAbandonedListener.java @@ -0,0 +1,15 @@ +package org.bukkit.conversations; + +import java.util.EventListener; + +/** + */ +public interface ConversationAbandonedListener extends EventListener { + /** + * Called whenever a {@link Conversation} is abandoned. + * + * @param abandonedEvent Contains details about the abandoned + * conversation. + */ + public void conversationAbandoned(ConversationAbandonedEvent abandonedEvent); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ConversationCanceller.java b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationCanceller.java new file mode 100644 index 0000000..db43bb1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationCanceller.java @@ -0,0 +1,34 @@ +package org.bukkit.conversations; + +/** + * A ConversationCanceller is a class that cancels an active {@link + * Conversation}. A Conversation can have more than one ConversationCanceller. + */ +public interface ConversationCanceller extends Cloneable { + + /** + * Sets the conversation this ConversationCanceller can optionally cancel. + * + * @param conversation A conversation. + */ + public void setConversation(Conversation conversation); + + /** + * Cancels a conversation based on user input. + * + * @param context Context information about the conversation. + * @param input The input text from the user. + * @return True to cancel the conversation, False otherwise. + */ + public boolean cancelBasedOnInput(ConversationContext context, String input); + + /** + * Allows the {@link ConversationFactory} to duplicate this + * ConversationCanceller when creating a new {@link Conversation}. + *

          + * Implementing this method should reset any internal object state. + * + * @return A clone. + */ + public ConversationCanceller clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ConversationContext.java b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationContext.java new file mode 100644 index 0000000..7390a77 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationContext.java @@ -0,0 +1,79 @@ +package org.bukkit.conversations; + +import org.bukkit.plugin.Plugin; + +import java.util.Map; + +/** + * A ConversationContext provides continuity between nodes in the prompt graph + * by giving the developer access to the subject of the conversation and a + * generic map for storing values that are shared between all {@link Prompt} + * invocations. + */ +public class ConversationContext { + private Conversable forWhom; + private Map sessionData; + private Plugin plugin; + + /** + * @param plugin The owning plugin. + * @param forWhom The subject of the conversation. + * @param initialSessionData Any initial values to put in the sessionData + * map. + */ + public ConversationContext(Plugin plugin, Conversable forWhom, Map initialSessionData) { + this.plugin = plugin; + this.forWhom = forWhom; + this.sessionData = initialSessionData; + } + + /** + * Gets the plugin that owns this conversation. + * + * @return The owning plugin. + */ + public Plugin getPlugin() { + return plugin; + } + + /** + * Gets the subject of the conversation. + * + * @return The subject of the conversation. + */ + public Conversable getForWhom() { + return forWhom; + } + + /** + * Gets the entire sessionData map. + * @return The full sessionData map. + */ + public Map getAllSessionData() { + return sessionData; + } + + /** + * Gets session data shared between all {@link Prompt} invocations. Use + * this as a way to pass data through each Prompt as the conversation + * develops. + * + * @param key The session data key. + * @return The requested session data. + */ + public Object getSessionData(Object key) { + return sessionData.get(key); + } + + /** + * Sets session data shared between all {@link Prompt} invocations. Use + * this as a way to pass data through each prompt as the conversation + * develops. + * + * @param key The session data key. + * @param value The session data value. + */ + public void setSessionData(Object key, Object value) { + sessionData.put(key, value); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ConversationFactory.java b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationFactory.java new file mode 100644 index 0000000..e7cbd52 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationFactory.java @@ -0,0 +1,228 @@ +package org.bukkit.conversations; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A ConversationFactory is responsible for creating a {@link Conversation} + * from a predefined template. A ConversationFactory is typically created when + * a plugin is instantiated and builds a Conversation each time a user + * initiates a conversation with the plugin. Each Conversation maintains its + * own state and calls back as needed into the plugin. + *

          + * The ConversationFactory implements a fluid API, allowing parameters to be + * set as an extension to the constructor. + */ +public class ConversationFactory { + + protected Plugin plugin; + protected boolean isModal; + protected boolean localEchoEnabled; + protected ConversationPrefix prefix; + protected Prompt firstPrompt; + protected Map initialSessionData; + protected String playerOnlyMessage; + protected List cancellers; + protected List abandonedListeners; + + /** + * Constructs a ConversationFactory. + * + * @param plugin The plugin that owns the factory. + */ + public ConversationFactory(Plugin plugin) + { + this.plugin = plugin; + isModal = true; + localEchoEnabled = true; + prefix = new NullConversationPrefix(); + firstPrompt = Prompt.END_OF_CONVERSATION; + initialSessionData = new HashMap(); + playerOnlyMessage = null; + cancellers = new ArrayList(); + abandonedListeners = new ArrayList(); + } + + /** + * Sets the modality of all {@link Conversation}s created by this factory. + * If a conversation is modal, all messages directed to the player are + * suppressed for the duration of the conversation. + *

          + * The default is True. + * + * @param modal The modality of all conversations to be created. + * @return This object. + */ + public ConversationFactory withModality(boolean modal) + { + isModal = modal; + return this; + } + + /** + * Sets the local echo status for all {@link Conversation}s created by + * this factory. If local echo is enabled, any text submitted to a + * conversation gets echoed back into the submitter's chat window. + * + * @param localEchoEnabled The status of local echo. + * @return This object. + */ + public ConversationFactory withLocalEcho(boolean localEchoEnabled) { + this.localEchoEnabled = localEchoEnabled; + return this; + } + + /** + * Sets the {@link ConversationPrefix} that prepends all output from all + * generated conversations. + *

          + * The default is a {@link NullConversationPrefix}; + * + * @param prefix The ConversationPrefix to use. + * @return This object. + */ + public ConversationFactory withPrefix(ConversationPrefix prefix) { + this.prefix = prefix; + return this; + } + + /** + * Sets the number of inactive seconds to wait before automatically + * abandoning all generated conversations. + *

          + * The default is 600 seconds (5 minutes). + * + * @param timeoutSeconds The number of seconds to wait. + * @return This object. + */ + public ConversationFactory withTimeout(int timeoutSeconds) { + return withConversationCanceller(new InactivityConversationCanceller(plugin, timeoutSeconds)); + } + + /** + * Sets the first prompt to use in all generated conversations. + *

          + * The default is Prompt.END_OF_CONVERSATION. + * + * @param firstPrompt The first prompt. + * @return This object. + */ + public ConversationFactory withFirstPrompt(Prompt firstPrompt) { + this.firstPrompt = firstPrompt; + return this; + } + + /** + * Sets any initial data with which to populate the conversation context + * sessionData map. + * + * @param initialSessionData The conversation context's initial + * sessionData. + * @return This object. + */ + public ConversationFactory withInitialSessionData(Map initialSessionData) { + this.initialSessionData = initialSessionData; + return this; + } + + /** + * Sets the player input that, when received, will immediately terminate + * the conversation. + * + * @param escapeSequence Input to terminate the conversation. + * @return This object. + */ + public ConversationFactory withEscapeSequence(String escapeSequence) { + return withConversationCanceller(new ExactMatchConversationCanceller(escapeSequence)); + } + + + /** + * Adds a {@link ConversationCanceller} to constructed conversations. + * + * @param canceller The {@link ConversationCanceller} to add. + * @return This object. + */ + public ConversationFactory withConversationCanceller(ConversationCanceller canceller) { + cancellers.add(canceller); + return this; + } + + /** + * Prevents this factory from creating a conversation for non-player + * {@link Conversable} objects. + * + * @param playerOnlyMessage The message to return to a non-play in lieu of + * starting a conversation. + * @return This object. + */ + public ConversationFactory thatExcludesNonPlayersWithMessage(String playerOnlyMessage) { + this.playerOnlyMessage = playerOnlyMessage; + return this; + } + + /** + * Adds a {@link ConversationAbandonedListener} to all conversations + * constructed by this factory. + * + * @param listener The listener to add. + * @return This object. + */ + public ConversationFactory addConversationAbandonedListener(ConversationAbandonedListener listener) { + abandonedListeners.add(listener); + return this; + } + + /** + * Constructs a {@link Conversation} in accordance with the defaults set + * for this factory. + * + * @param forWhom The entity for whom the new conversation is mediating. + * @return A new conversation. + */ + public Conversation buildConversation(Conversable forWhom) { + //Abort conversation construction if we aren't supposed to talk to non-players + if (playerOnlyMessage != null && !(forWhom instanceof Player)) { + return new Conversation(plugin, forWhom, new NotPlayerMessagePrompt()); + } + + //Clone any initial session data + Map copiedInitialSessionData = new HashMap(); + copiedInitialSessionData.putAll(initialSessionData); + + //Build and return a conversation + Conversation conversation = new Conversation(plugin, forWhom, firstPrompt, copiedInitialSessionData); + conversation.setModal(isModal); + conversation.setLocalEchoEnabled(localEchoEnabled); + conversation.setPrefix(prefix); + + //Clone the conversation cancellers + for (ConversationCanceller canceller : cancellers) { + conversation.addConversationCanceller(canceller.clone()); + } + + //Add the ConversationAbandonedListeners + for (ConversationAbandonedListener listener : abandonedListeners) { + conversation.addConversationAbandonedListener(listener); + } + + return conversation; + } + + private class NotPlayerMessagePrompt extends MessagePrompt { + + public String getPromptText(ConversationContext context) { + return playerOnlyMessage; + } + + @Override + protected Prompt getNextPrompt(ConversationContext context) { + return Prompt.END_OF_CONVERSATION; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ConversationPrefix.java b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationPrefix.java new file mode 100644 index 0000000..9889f17 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ConversationPrefix.java @@ -0,0 +1,19 @@ +package org.bukkit.conversations; + +import org.bukkit.command.CommandSender; + +/** + * A ConversationPrefix implementation prepends all output from the + * conversation to the player. The ConversationPrefix can be used to display + * the plugin name or conversation status as the conversation evolves. + */ +public interface ConversationPrefix { + + /** + * Gets the prefix to use before each message to the player. + * + * @param context Context information about the conversation. + * @return The prefix text. + */ + String getPrefix(ConversationContext context); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ExactMatchConversationCanceller.java b/vspigot-api/src/main/java/org/bukkit/conversations/ExactMatchConversationCanceller.java new file mode 100644 index 0000000..327b9d9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ExactMatchConversationCanceller.java @@ -0,0 +1,29 @@ +package org.bukkit.conversations; + +/** + * An ExactMatchConversationCanceller cancels a conversation if the user + * enters an exact input string + */ +public class ExactMatchConversationCanceller implements ConversationCanceller { + private String escapeSequence; + + /** + * Builds an ExactMatchConversationCanceller. + * + * @param escapeSequence The string that, if entered by the user, will + * cancel the conversation. + */ + public ExactMatchConversationCanceller(String escapeSequence) { + this.escapeSequence = escapeSequence; + } + + public void setConversation(Conversation conversation) {} + + public boolean cancelBasedOnInput(ConversationContext context, String input) { + return input.equals(escapeSequence); + } + + public ConversationCanceller clone() { + return new ExactMatchConversationCanceller(escapeSequence); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/FixedSetPrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/FixedSetPrompt.java new file mode 100644 index 0000000..b867c11 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/FixedSetPrompt.java @@ -0,0 +1,46 @@ +package org.bukkit.conversations; + +import org.apache.commons.lang.StringUtils; + +import java.util.Arrays; +import java.util.List; + +/** + * FixedSetPrompt is the base class for any prompt that requires a fixed set + * response from the user. + */ +public abstract class FixedSetPrompt extends ValidatingPrompt { + + protected List fixedSet; + + /** + * Creates a FixedSetPrompt from a set of strings. + *

          + * foo = new FixedSetPrompt("bar", "cheese", "panda"); + * + * @param fixedSet A fixed set of strings, one of which the user must + * type. + */ + public FixedSetPrompt(String... fixedSet) { + super(); + this.fixedSet = Arrays.asList(fixedSet); + } + + private FixedSetPrompt() {} + + @Override + protected boolean isInputValid(ConversationContext context, String input) { + return fixedSet.contains(input); + } + + /** + * Utility function to create a formatted string containing all the + * options declared in the constructor. + * + * @return the options formatted like "[bar, cheese, panda]" if bar, + * cheese, and panda were the options used + */ + protected String formatFixedSet() { + return "[" + StringUtils.join(fixedSet, ", ") + "]"; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java b/vspigot-api/src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java new file mode 100644 index 0000000..760a518 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/InactivityConversationCanceller.java @@ -0,0 +1,79 @@ +package org.bukkit.conversations; + +import org.bukkit.Server; +import org.bukkit.plugin.Plugin; + +/** + * An InactivityConversationCanceller will cancel a {@link Conversation} after + * a period of inactivity by the user. + */ +public class InactivityConversationCanceller implements ConversationCanceller { + protected Plugin plugin; + protected int timeoutSeconds; + protected Conversation conversation; + private int taskId = -1; + + /** + * Creates an InactivityConversationCanceller. + * + * @param plugin The owning plugin. + * @param timeoutSeconds The number of seconds of inactivity to wait. + */ + public InactivityConversationCanceller(Plugin plugin, int timeoutSeconds) { + this.plugin = plugin; + this.timeoutSeconds = timeoutSeconds; + } + + public void setConversation(Conversation conversation) { + this.conversation = conversation; + startTimer(); + } + + public boolean cancelBasedOnInput(ConversationContext context, String input) { + // Reset the inactivity timer + stopTimer(); + startTimer(); + return false; + } + + public ConversationCanceller clone() { + return new InactivityConversationCanceller(plugin, timeoutSeconds); + } + + /** + * Starts an inactivity timer. + */ + private void startTimer() { + taskId = plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { + public void run() { + if (conversation.getState() == Conversation.ConversationState.UNSTARTED) { + startTimer(); + } else if (conversation.getState() == Conversation.ConversationState.STARTED) { + cancelling(conversation); + conversation.abandon(new ConversationAbandonedEvent(conversation, InactivityConversationCanceller.this)); + } + } + }, timeoutSeconds * 20); + } + + /** + * Stops the active inactivity timer. + */ + private void stopTimer() { + if (taskId != -1) { + plugin.getServer().getScheduler().cancelTask(taskId); + taskId = -1; + } + } + + /** + * Subclasses of InactivityConversationCanceller can override this method + * to take additional actions when the inactivity timer abandons the + * conversation. + * + * @param conversation The conversation being abandoned. + */ + protected void cancelling(Conversation conversation) { + + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java b/vspigot-api/src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java new file mode 100644 index 0000000..3e80de1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ManuallyAbandonedConversationCanceller.java @@ -0,0 +1,20 @@ +package org.bukkit.conversations; + +/** + * The ManuallyAbandonedConversationCanceller is only used as part of a {@link + * ConversationAbandonedEvent} to indicate that the conversation was manually + * abandoned by programmatically calling the abandon() method on it. + */ +public class ManuallyAbandonedConversationCanceller implements ConversationCanceller{ + public void setConversation(Conversation conversation) { + throw new UnsupportedOperationException(); + } + + public boolean cancelBasedOnInput(ConversationContext context, String input) { + throw new UnsupportedOperationException(); + } + + public ConversationCanceller clone() { + throw new UnsupportedOperationException(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/MessagePrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/MessagePrompt.java new file mode 100644 index 0000000..fa1775a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/MessagePrompt.java @@ -0,0 +1,42 @@ +package org.bukkit.conversations; + +/** + * MessagePrompt is the base class for any prompt that only displays a message + * to the user and requires no input. + */ +public abstract class MessagePrompt implements Prompt{ + + public MessagePrompt() { + super(); + } + + /** + * Message prompts never wait for user input before continuing. + * + * @param context Context information about the conversation. + * @return Always false. + */ + public boolean blocksForInput(ConversationContext context) { + return false; + } + + /** + * Accepts and ignores any user input, returning the next prompt in the + * prompt graph instead. + * + * @param context Context information about the conversation. + * @param input Ignored. + * @return The next prompt in the prompt graph. + */ + public Prompt acceptInput(ConversationContext context, String input) { + return getNextPrompt(context); + } + + /** + * Override this method to return the next prompt in the prompt graph. + * + * @param context Context information about the conversation. + * @return The next prompt in the prompt graph. + */ + protected abstract Prompt getNextPrompt(ConversationContext context); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/NullConversationPrefix.java b/vspigot-api/src/main/java/org/bukkit/conversations/NullConversationPrefix.java new file mode 100644 index 0000000..7d8a7d8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/NullConversationPrefix.java @@ -0,0 +1,20 @@ +package org.bukkit.conversations; + +import org.bukkit.command.CommandSender; + +/** + * NullConversationPrefix is a {@link ConversationPrefix} implementation that + * displays nothing in front of conversation output. + */ +public class NullConversationPrefix implements ConversationPrefix{ + + /** + * Prepends each conversation message with an empty string. + * + * @param context Context information about the conversation. + * @return An empty string. + */ + public String getPrefix(ConversationContext context) { + return ""; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/NumericPrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/NumericPrompt.java new file mode 100644 index 0000000..f0fdea1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/NumericPrompt.java @@ -0,0 +1,83 @@ +package org.bukkit.conversations; + +import org.apache.commons.lang.math.NumberUtils; + +/** + * NumericPrompt is the base class for any prompt that requires a {@link + * Number} response from the user. + */ +public abstract class NumericPrompt extends ValidatingPrompt{ + public NumericPrompt() { + super(); + } + + @Override + protected boolean isInputValid(ConversationContext context, String input) { + return NumberUtils.isNumber(input) && isNumberValid(context, NumberUtils.createNumber(input)); + } + + /** + * Override this method to do further validation on the numeric player + * input after the input has been determined to actually be a number. + * + * @param context Context information about the conversation. + * @param input The number the player provided. + * @return The validity of the player's input. + */ + protected boolean isNumberValid(ConversationContext context, Number input) { + return true; + } + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, String input) { + try + { + return acceptValidatedInput(context, NumberUtils.createNumber(input)); + } catch (NumberFormatException e) { + return acceptValidatedInput(context, NumberUtils.INTEGER_ZERO); + } + } + + /** + * Override this method to perform some action with the user's integer + * response. + * + * @param context Context information about the conversation. + * @param input The user's response as a {@link Number}. + * @return The next {@link Prompt} in the prompt graph. + */ + protected abstract Prompt acceptValidatedInput(ConversationContext context, Number input); + + @Override + protected String getFailedValidationText(ConversationContext context, String invalidInput) { + if (NumberUtils.isNumber(invalidInput)) { + return getFailedValidationText(context, NumberUtils.createNumber(invalidInput)); + } else { + return getInputNotNumericText(context, invalidInput); + } + } + + /** + * Optionally override this method to display an additional message if the + * user enters an invalid number. + * + * @param context Context information about the conversation. + * @param invalidInput The invalid input provided by the user. + * @return A message explaining how to correct the input. + */ + protected String getInputNotNumericText(ConversationContext context, String invalidInput) { + return null; + } + + /** + * Optionally override this method to display an additional message if the + * user enters an invalid numeric input. + * + * @param context Context information about the conversation. + * @param invalidInput The invalid input provided by the user. + * @return A message explaining how to correct the input. + */ + protected String getFailedValidationText(ConversationContext context, Number invalidInput) { + return null; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/PlayerNamePrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/PlayerNamePrompt.java new file mode 100644 index 0000000..feeb715 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/PlayerNamePrompt.java @@ -0,0 +1,38 @@ +package org.bukkit.conversations; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * PlayerNamePrompt is the base class for any prompt that requires the player + * to enter another player's name. + */ +public abstract class PlayerNamePrompt extends ValidatingPrompt{ + private Plugin plugin; + + public PlayerNamePrompt(Plugin plugin) { + super(); + this.plugin = plugin; + } + + @Override + protected boolean isInputValid(ConversationContext context, String input) { + return plugin.getServer().getPlayer(input) != null; + + } + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, String input) { + return acceptValidatedInput(context, plugin.getServer().getPlayer(input)); + } + + /** + * Override this method to perform some action with the user's player name + * response. + * + * @param context Context information about the conversation. + * @param input The user's player name response. + * @return The next {@link Prompt} in the prompt graph. + */ + protected abstract Prompt acceptValidatedInput(ConversationContext context, Player input); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/PluginNameConversationPrefix.java b/vspigot-api/src/main/java/org/bukkit/conversations/PluginNameConversationPrefix.java new file mode 100644 index 0000000..2290979 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/PluginNameConversationPrefix.java @@ -0,0 +1,40 @@ +package org.bukkit.conversations; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + +/** + * PluginNameConversationPrefix is a {@link ConversationPrefix} implementation + * that displays the plugin name in front of conversation output. + */ +public class PluginNameConversationPrefix implements ConversationPrefix { + + protected String separator; + protected ChatColor prefixColor; + protected Plugin plugin; + + private String cachedPrefix; + + public PluginNameConversationPrefix(Plugin plugin) { + this(plugin, " > ", ChatColor.LIGHT_PURPLE); + } + + public PluginNameConversationPrefix(Plugin plugin, String separator, ChatColor prefixColor) { + this.separator = separator; + this.prefixColor = prefixColor; + this.plugin = plugin; + + cachedPrefix = prefixColor + plugin.getDescription().getName() + separator + ChatColor.WHITE; + } + + /** + * Prepends each conversation message with the plugin name. + * + * @param context Context information about the conversation. + * @return An empty string. + */ + public String getPrefix(ConversationContext context) { + return cachedPrefix; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/Prompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/Prompt.java new file mode 100644 index 0000000..7519c84 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/Prompt.java @@ -0,0 +1,45 @@ +package org.bukkit.conversations; + +/** + * A Prompt is the main constituent of a {@link Conversation}. Each prompt + * displays text to the user and optionally waits for a user's response. + * Prompts are chained together into a directed graph that represents the + * conversation flow. To halt a conversation, END_OF_CONVERSATION is returned + * in liu of another Prompt object. + */ +public interface Prompt extends Cloneable { + + /** + * A convenience constant for indicating the end of a conversation. + */ + static final Prompt END_OF_CONVERSATION = null; + + /** + * Gets the text to display to the user when this prompt is first + * presented. + * + * @param context Context information about the conversation. + * @return The text to display. + */ + String getPromptText(ConversationContext context); + + /** + * Checks to see if this prompt implementation should wait for user input + * or immediately display the next prompt. + * + * @param context Context information about the conversation. + * @return If true, the {@link Conversation} will wait for input before + * continuing. + */ + boolean blocksForInput(ConversationContext context); + + /** + * Accepts and processes input from the user. Using the input, the next + * Prompt in the prompt graph is returned. + * + * @param context Context information about the conversation. + * @param input The input text from the user. + * @return The next Prompt in the prompt graph. + */ + Prompt acceptInput(ConversationContext context, String input); +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/RegexPrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/RegexPrompt.java new file mode 100644 index 0000000..a3c7d1f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/RegexPrompt.java @@ -0,0 +1,28 @@ +package org.bukkit.conversations; + +import java.util.regex.Pattern; + +/** + * RegexPrompt is the base class for any prompt that requires an input + * validated by a regular expression. + */ +public abstract class RegexPrompt extends ValidatingPrompt { + + private Pattern pattern; + + public RegexPrompt(String regex) { + this(Pattern.compile(regex)); + } + + public RegexPrompt(Pattern pattern) { + super(); + this.pattern = pattern; + } + + private RegexPrompt() {} + + @Override + protected boolean isInputValid(ConversationContext context, String input) { + return pattern.matcher(input).matches(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/StringPrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/StringPrompt.java new file mode 100644 index 0000000..2934459 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/StringPrompt.java @@ -0,0 +1,18 @@ +package org.bukkit.conversations; + +/** + * StringPrompt is the base class for any prompt that accepts an arbitrary + * string from the user. + */ +public abstract class StringPrompt implements Prompt{ + + /** + * Ensures that the prompt waits for the user to provide input. + * + * @param context Context information about the conversation. + * @return True. + */ + public boolean blocksForInput(ConversationContext context) { + return true; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/conversations/ValidatingPrompt.java b/vspigot-api/src/main/java/org/bukkit/conversations/ValidatingPrompt.java new file mode 100644 index 0000000..f41adb4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/conversations/ValidatingPrompt.java @@ -0,0 +1,78 @@ +package org.bukkit.conversations; + +import org.bukkit.ChatColor; + +/** + * ValidatingPrompt is the base class for any prompt that requires validation. + * ValidatingPrompt will keep replaying the prompt text until the user enters + * a valid response. + */ +public abstract class ValidatingPrompt implements Prompt { + public ValidatingPrompt() { + super(); + } + + /** + * Accepts and processes input from the user and validates it. If + * validation fails, this prompt is returned for re-execution, otherwise + * the next Prompt in the prompt graph is returned. + * + * @param context Context information about the conversation. + * @param input The input text from the user. + * @return This prompt or the next Prompt in the prompt graph. + */ + public Prompt acceptInput(ConversationContext context, String input) { + if (isInputValid(context, input)) { + return acceptValidatedInput(context, input); + } else { + String failPrompt = getFailedValidationText(context, input); + if (failPrompt != null) { + context.getForWhom().sendRawMessage(ChatColor.RED + failPrompt); + } + // Redisplay this prompt to the user to re-collect input + return this; + } + } + + /** + * Ensures that the prompt waits for the user to provide input. + * + * @param context Context information about the conversation. + * @return True. + */ + public boolean blocksForInput(ConversationContext context) { + return true; + } + + /** + * Override this method to check the validity of the player's input. + * + * @param context Context information about the conversation. + * @param input The player's raw console input. + * @return True or false depending on the validity of the input. + */ + protected abstract boolean isInputValid(ConversationContext context, String input); + + /** + * Override this method to accept and processes the validated input from + * the user. Using the input, the next Prompt in the prompt graph should + * be returned. + * + * @param context Context information about the conversation. + * @param input The validated input text from the user. + * @return The next Prompt in the prompt graph. + */ + protected abstract Prompt acceptValidatedInput(ConversationContext context, String input); + + /** + * Optionally override this method to display an additional message if the + * user enters an invalid input. + * + * @param context Context information about the conversation. + * @param invalidInput The invalid input provided by the user. + * @return A message explaining how to correct the input. + */ + protected String getFailedValidationText(ConversationContext context, String invalidInput) { + return null; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/enchantments/Enchantment.java b/vspigot-api/src/main/java/org/bukkit/enchantments/Enchantment.java new file mode 100644 index 0000000..e038412 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/enchantments/Enchantment.java @@ -0,0 +1,292 @@ +package org.bukkit.enchantments; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.command.defaults.EnchantCommand; +import org.bukkit.inventory.ItemStack; + +/** + * The various type of enchantments that may be added to armour or weapons + */ +public abstract class Enchantment { + /** + * Provides protection against environmental damage + */ + public static final Enchantment PROTECTION_ENVIRONMENTAL = new EnchantmentWrapper(0); + + /** + * Provides protection against fire damage + */ + public static final Enchantment PROTECTION_FIRE = new EnchantmentWrapper(1); + + /** + * Provides protection against fall damage + */ + public static final Enchantment PROTECTION_FALL = new EnchantmentWrapper(2); + + /** + * Provides protection against explosive damage + */ + public static final Enchantment PROTECTION_EXPLOSIONS = new EnchantmentWrapper(3); + + /** + * Provides protection against projectile damage + */ + public static final Enchantment PROTECTION_PROJECTILE = new EnchantmentWrapper(4); + + /** + * Decreases the rate of air loss whilst underwater + */ + public static final Enchantment OXYGEN = new EnchantmentWrapper(5); + + /** + * Increases the speed at which a player may mine underwater + */ + public static final Enchantment WATER_WORKER = new EnchantmentWrapper(6); + + /** + * Damages the attacker + */ + public static final Enchantment THORNS = new EnchantmentWrapper(7); + + /** + * Increases damage against all targets + */ + public static final Enchantment DAMAGE_ALL = new EnchantmentWrapper(16); + + /** + * Increases damage against undead targets + */ + public static final Enchantment DAMAGE_UNDEAD = new EnchantmentWrapper(17); + + /** + * Increases damage against arthropod targets + */ + public static final Enchantment DAMAGE_ARTHROPODS = new EnchantmentWrapper(18); + + /** + * All damage to other targets will knock them back when hit + */ + public static final Enchantment KNOCKBACK = new EnchantmentWrapper(19); + + /** + * When attacking a target, has a chance to set them on fire + */ + public static final Enchantment FIRE_ASPECT = new EnchantmentWrapper(20); + + /** + * Provides a chance of gaining extra loot when killing monsters + */ + public static final Enchantment LOOT_BONUS_MOBS = new EnchantmentWrapper(21); + + /** + * Increases the rate at which you mine/dig + */ + public static final Enchantment DIG_SPEED = new EnchantmentWrapper(32); + + /** + * Allows blocks to drop themselves instead of fragments (for example, + * stone instead of cobblestone) + */ + public static final Enchantment SILK_TOUCH = new EnchantmentWrapper(33); + + /** + * Decreases the rate at which a tool looses durability + */ + public static final Enchantment DURABILITY = new EnchantmentWrapper(34); + + /** + * Provides a chance of gaining extra loot when destroying blocks + */ + public static final Enchantment LOOT_BONUS_BLOCKS = new EnchantmentWrapper(35); + + /** + * Provides extra damage when shooting arrows from bows + */ + public static final Enchantment ARROW_DAMAGE = new EnchantmentWrapper(48); + + /** + * Provides a knockback when an entity is hit by an arrow from a bow + */ + public static final Enchantment ARROW_KNOCKBACK = new EnchantmentWrapper(49); + + /** + * Sets entities on fire when hit by arrows shot from a bow + */ + public static final Enchantment ARROW_FIRE = new EnchantmentWrapper(50); + + /** + * Provides infinite arrows when shooting a bow + */ + public static final Enchantment ARROW_INFINITE = new EnchantmentWrapper(51); + + /** + * Decreases odds of catching worthless junk + */ + public static final Enchantment LUCK = new EnchantmentWrapper(61); + + /** + * Increases rate of fish biting your hook + */ + public static final Enchantment LURE = new EnchantmentWrapper(62); + + private static final Map byId = new HashMap(); + private static final Map byName = new HashMap(); + private static boolean acceptingNew = true; + private final int id; + + public Enchantment(int id) { + this.id = id; + } + + /** + * Gets the unique ID of this enchantment + * + * @return Unique ID + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + + /** + * Gets the unique name of this enchantment + * + * @return Unique name + */ + public abstract String getName(); + + /** + * Gets the maximum level that this Enchantment may become. + * + * @return Maximum level of the Enchantment + */ + public abstract int getMaxLevel(); + + /** + * Gets the level that this Enchantment should start at + * + * @return Starting level of the Enchantment + */ + public abstract int getStartLevel(); + + /** + * Gets the type of {@link ItemStack} that may fit this Enchantment. + * + * @return Target type of the Enchantment + */ + public abstract EnchantmentTarget getItemTarget(); + + /** + * Check if this enchantment conflicts with another enchantment. + * + * @param other The enchantment to check against + * @return True if there is a conflict. + */ + public abstract boolean conflictsWith(Enchantment other); + + /** + * Checks if this Enchantment may be applied to the given {@link + * ItemStack}. + *

          + * This does not check if it conflicts with any enchantments already + * applied to the item. + * + * @param item Item to test + * @return True if the enchantment may be applied, otherwise False + */ + public abstract boolean canEnchantItem(ItemStack item); + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof Enchantment)) { + return false; + } + final Enchantment other = (Enchantment) obj; + if (this.id != other.id) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return id; + } + + @Override + public String toString() { + return "Enchantment[" + id + ", " + getName() + "]"; + } + + /** + * Registers an enchantment with the given ID and object. + *

          + * Generally not to be used from within a plugin. + * + * @param enchantment Enchantment to register + */ + public static void registerEnchantment(Enchantment enchantment) { + if (byId.containsKey(enchantment.id) || byName.containsKey(enchantment.getName())) { + throw new IllegalArgumentException("Cannot set already-set enchantment"); + } else if (!isAcceptingRegistrations()) { + throw new IllegalStateException("No longer accepting new enchantments (can only be done by the server implementation)"); + } + + byId.put(enchantment.id, enchantment); + byName.put(enchantment.getName(), enchantment); + } + + /** + * Checks if this is accepting Enchantment registrations. + * + * @return True if the server Implementation may add enchantments + */ + public static boolean isAcceptingRegistrations() { + return acceptingNew; + } + + /** + * Stops accepting any enchantment registrations + */ + public static void stopAcceptingRegistrations() { + acceptingNew = false; + EnchantCommand.buildEnchantments(); + } + + /** + * Gets the Enchantment at the specified ID + * + * @param id ID to fetch + * @return Resulting Enchantment, or null if not found + * @deprecated Magic value + */ + @Deprecated + public static Enchantment getById(int id) { + return byId.get(id); + } + + /** + * Gets the Enchantment at the specified name + * + * @param name Name to fetch + * @return Resulting Enchantment, or null if not found + */ + public static Enchantment getByName(String name) { + return byName.get(name); + } + + /** + * Gets an array of all the registered {@link Enchantment}s + * + * @return Array of enchantments + */ + public static Enchantment[] values() { + return byId.values().toArray(new Enchantment[byId.size()]); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java b/vspigot-api/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java new file mode 100644 index 0000000..d9b98ed --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java @@ -0,0 +1,172 @@ +package org.bukkit.enchantments; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +/** + * Represents the applicable target for a {@link Enchantment} + */ +public enum EnchantmentTarget { + /** + * Allows the Enchantment to be placed on all items + */ + ALL { + @Override + public boolean includes(Material item) { + return true; + } + }, + + /** + * Allows the Enchantment to be placed on armor + */ + ARMOR { + @Override + public boolean includes(Material item) { + return ARMOR_FEET.includes(item) + || ARMOR_LEGS.includes(item) + || ARMOR_HEAD.includes(item) + || ARMOR_TORSO.includes(item); + } + }, + + /** + * Allows the Enchantment to be placed on feet slot armor + */ + ARMOR_FEET { + @Override + public boolean includes(Material item) { + return item.equals(Material.LEATHER_BOOTS) + || item.equals(Material.CHAINMAIL_BOOTS) + || item.equals(Material.IRON_BOOTS) + || item.equals(Material.DIAMOND_BOOTS) + || item.equals(Material.GOLD_BOOTS); + } + }, + + /** + * Allows the Enchantment to be placed on leg slot armor + */ + ARMOR_LEGS { + @Override + public boolean includes(Material item) { + return item.equals(Material.LEATHER_LEGGINGS) + || item.equals(Material.CHAINMAIL_LEGGINGS) + || item.equals(Material.IRON_LEGGINGS) + || item.equals(Material.DIAMOND_LEGGINGS) + || item.equals(Material.GOLD_LEGGINGS); + } + }, + + /** + * Allows the Enchantment to be placed on torso slot armor + */ + ARMOR_TORSO { + @Override + public boolean includes(Material item) { + return item.equals(Material.LEATHER_CHESTPLATE) + || item.equals(Material.CHAINMAIL_CHESTPLATE) + || item.equals(Material.IRON_CHESTPLATE) + || item.equals(Material.DIAMOND_CHESTPLATE) + || item.equals(Material.GOLD_CHESTPLATE); + } + }, + + /** + * Allows the Enchantment to be placed on head slot armor + */ + ARMOR_HEAD { + @Override + public boolean includes(Material item) { + return item.equals(Material.LEATHER_HELMET) + || item.equals(Material.CHAINMAIL_HELMET) + || item.equals(Material.DIAMOND_HELMET) + || item.equals(Material.IRON_HELMET) + || item.equals(Material.GOLD_HELMET); + } + }, + + /** + * Allows the Enchantment to be placed on weapons (swords) + */ + WEAPON { + @Override + public boolean includes(Material item) { + return item.equals(Material.WOOD_SWORD) + || item.equals(Material.STONE_SWORD) + || item.equals(Material.IRON_SWORD) + || item.equals(Material.DIAMOND_SWORD) + || item.equals(Material.GOLD_SWORD); + } + }, + + /** + * Allows the Enchantment to be placed on tools (spades, pickaxe, hoes, + * axes) + */ + TOOL { + @Override + public boolean includes(Material item) { + return item.equals(Material.WOOD_SPADE) + || item.equals(Material.STONE_SPADE) + || item.equals(Material.IRON_SPADE) + || item.equals(Material.DIAMOND_SPADE) + || item.equals(Material.GOLD_SPADE) + || item.equals(Material.WOOD_PICKAXE) + || item.equals(Material.STONE_PICKAXE) + || item.equals(Material.IRON_PICKAXE) + || item.equals(Material.DIAMOND_PICKAXE) + || item.equals(Material.GOLD_PICKAXE) + || item.equals(Material.WOOD_HOE) // NOTE: No vanilla enchantments for this + || item.equals(Material.STONE_HOE) // NOTE: No vanilla enchantments for this + || item.equals(Material.IRON_HOE) // NOTE: No vanilla enchantments for this + || item.equals(Material.DIAMOND_HOE) // NOTE: No vanilla enchantments for this + || item.equals(Material.GOLD_HOE) // NOTE: No vanilla enchantments for this + || item.equals(Material.WOOD_AXE) + || item.equals(Material.STONE_AXE) + || item.equals(Material.IRON_AXE) + || item.equals(Material.DIAMOND_AXE) + || item.equals(Material.GOLD_AXE) + || item.equals(Material.SHEARS) // NOTE: No vanilla enchantments for this + || item.equals(Material.FLINT_AND_STEEL); // NOTE: No vanilla enchantments for this + } + }, + + /** + * Allows the Enchantment to be placed on bows. + */ + BOW { + @Override + public boolean includes(Material item) { + return item.equals(Material.BOW); + } + }, + + /** + * Allows the Enchantment to be placed on fishing rods. + */ + FISHING_ROD { + @Override + public boolean includes(Material item) { + return item.equals(Material.FISHING_ROD); + } + }; + + /** + * Check whether this target includes the specified item. + * + * @param item The item to check + * @return True if the target includes the item + */ + public abstract boolean includes(Material item); + + /** + * Check whether this target includes the specified item. + * + * @param item The item to check + * @return True if the target includes the item + */ + public boolean includes(ItemStack item) { + return includes(item.getType()); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java b/vspigot-api/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java new file mode 100644 index 0000000..6a0aeb3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java @@ -0,0 +1,51 @@ +package org.bukkit.enchantments; + +import org.bukkit.inventory.ItemStack; + +/** + * A simple wrapper for ease of selecting {@link Enchantment}s + */ +public class EnchantmentWrapper extends Enchantment { + public EnchantmentWrapper(int id) { + super(id); + } + + /** + * Gets the enchantment bound to this wrapper + * + * @return Enchantment + */ + public Enchantment getEnchantment() { + return Enchantment.getById(getId()); + } + + @Override + public int getMaxLevel() { + return getEnchantment().getMaxLevel(); + } + + @Override + public int getStartLevel() { + return getEnchantment().getStartLevel(); + } + + @Override + public EnchantmentTarget getItemTarget() { + return getEnchantment().getItemTarget(); + } + + @Override + public boolean canEnchantItem(ItemStack item) { + return getEnchantment().canEnchantItem(item); + } + + @Override + public String getName() { + return getEnchantment().getName(); + } + + @Override + public boolean conflictsWith(Enchantment other) { + return getEnchantment().conflictsWith(other); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Ageable.java b/vspigot-api/src/main/java/org/bukkit/entity/Ageable.java new file mode 100644 index 0000000..e9fccb2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Ageable.java @@ -0,0 +1,67 @@ +package org.bukkit.entity; + +/** + * Represents an entity that can age and breed. + */ +public interface Ageable extends Creature { + /** + * Gets the age of this animal. + * + * @return Age + */ + public int getAge(); + + /** + * Sets the age of this animal. + * + * @param age New age + */ + public void setAge(int age); + + /** + * Lock the age of the animal, setting this will prevent the animal from + * maturing or getting ready for mating. + * + * @param lock new lock + */ + public void setAgeLock(boolean lock); + + /** + * Gets the current agelock. + * + * @return the current agelock + */ + public boolean getAgeLock(); + + /** + * Sets the age of the animal to a baby + */ + public void setBaby(); + + /** + * Sets the age of the animal to an adult + */ + public void setAdult(); + + /** + * Returns true if the animal is an adult. + * + * @return return true if the animal is an adult + */ + public boolean isAdult(); + + /** + * Return the ability to breed of the animal. + * + * @return the ability to breed of the animal + */ + public boolean canBreed(); + + /** + * Set breedability of the animal, if the animal is a baby and set to + * breed it will instantly grow up. + * + * @param breed breedability of the animal + */ + public void setBreed(boolean breed); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Ambient.java b/vspigot-api/src/main/java/org/bukkit/entity/Ambient.java new file mode 100644 index 0000000..779e389 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Ambient.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents an ambient mob + */ +public interface Ambient extends LivingEntity {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/AnimalTamer.java b/vspigot-api/src/main/java/org/bukkit/entity/AnimalTamer.java new file mode 100644 index 0000000..5f74f0d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/AnimalTamer.java @@ -0,0 +1,20 @@ +package org.bukkit.entity; + +import java.util.UUID; + +public interface AnimalTamer { + + /** + * This is the name of the specified AnimalTamer. + * + * @return The name to reference on tamed animals or null if a name cannot be obtained + */ + public String getName(); + + /** + * This is the UUID of the specified AnimalTamer. + * + * @return The UUID to reference on tamed animals + */ + public UUID getUniqueId(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Animals.java b/vspigot-api/src/main/java/org/bukkit/entity/Animals.java new file mode 100644 index 0000000..f0dc157 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Animals.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents an Animal. + */ +public interface Animals extends Ageable {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Arrow.java b/vspigot-api/src/main/java/org/bukkit/entity/Arrow.java new file mode 100644 index 0000000..e7a32f7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Arrow.java @@ -0,0 +1,58 @@ +package org.bukkit.entity; + +/** + * Represents an arrow. + */ +public interface Arrow extends Projectile { + + /** + * Gets the knockback strength for an arrow, which is the + * {@link org.bukkit.enchantments.Enchantment#KNOCKBACK KnockBack} level + * of the bow that shot it. + * + * @return the knockback strength value + */ + public int getKnockbackStrength(); + + /** + * Sets the knockback strength for an arrow. + * + * @param knockbackStrength the knockback strength value + */ + public void setKnockbackStrength(int knockbackStrength); + + /** + * Gets whether this arrow is critical. + *

          + * Critical arrows have increased damage and cause particle effects. + *

          + * Critical arrows generally occur when a player fully draws a bow before + * firing. + * + * @return true if it is critical + */ + public boolean isCritical(); + + /** + * Sets whether or not this arrow should be critical. + * + * @param critical whether or not it should be critical + */ + public void setCritical(boolean critical); + + public class Spigot extends Entity.Spigot + { + + public double getDamage() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setDamage(double damage) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Bat.java b/vspigot-api/src/main/java/org/bukkit/entity/Bat.java new file mode 100644 index 0000000..bd73f22 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Bat.java @@ -0,0 +1,27 @@ +package org.bukkit.entity; + +/** + * Represents a Bat + */ +public interface Bat extends Ambient { + + /** + * Checks the current waking state of this bat. + *

          + * This does not imply any persistence of state past the method call. + * + * @return true if the bat is awake or false if it is currently hanging + * from a block + */ + boolean isAwake(); + + /** + * This method modifies the current waking state of this bat. + *

          + * This does not prevent a bat from spontaneously awaking itself, or from + * reattaching itself to a block. + * + * @param state the new state + */ + void setAwake(boolean state); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Blaze.java b/vspigot-api/src/main/java/org/bukkit/entity/Blaze.java new file mode 100644 index 0000000..7a5505b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Blaze.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a Blaze monster + */ +public interface Blaze extends Monster { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Boat.java b/vspigot-api/src/main/java/org/bukkit/entity/Boat.java new file mode 100644 index 0000000..ed2d178 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Boat.java @@ -0,0 +1,72 @@ +package org.bukkit.entity; + +/** + * Represents a boat entity. + */ +public interface Boat extends Vehicle { + + /** + * Gets the maximum speed of a boat. The speed is unrelated to the + * velocity. + * + * @return The max speed. + */ + public double getMaxSpeed(); + + /** + * Sets the maximum speed of a boat. Must be nonnegative. Default is 0.4D. + * + * @param speed The max speed. + */ + public void setMaxSpeed(double speed); + + /** + * Gets the deceleration rate (newSpeed = curSpeed * rate) of occupied + * boats. The default is 0.2. + * + * @return The rate of deceleration + */ + public double getOccupiedDeceleration(); + + /** + * Sets the deceleration rate (newSpeed = curSpeed * rate) of occupied + * boats. Setting this to a higher value allows for quicker acceleration. + * The default is 0.2. + * + * @param rate deceleration rate + */ + public void setOccupiedDeceleration(double rate); + + /** + * Gets the deceleration rate (newSpeed = curSpeed * rate) of unoccupied + * boats. The default is -1. Values below 0 indicate that no additional + * deceleration is imposed. + * + * @return The rate of deceleration + */ + public double getUnoccupiedDeceleration(); + + /** + * Sets the deceleration rate (newSpeed = curSpeed * rate) of unoccupied + * boats. Setting this to a higher value allows for quicker deceleration + * of boats when a player disembarks. The default is -1. Values below 0 + * indicate that no additional deceleration is imposed. + * + * @param rate deceleration rate + */ + public void setUnoccupiedDeceleration(double rate); + + /** + * Get whether boats can work on land. + * + * @return whether boats can work on land + */ + public boolean getWorkOnLand(); + + /** + * Set whether boats can work on land. + * + * @param workOnLand whether boats can work on land + */ + public void setWorkOnLand(boolean workOnLand); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/CaveSpider.java b/vspigot-api/src/main/java/org/bukkit/entity/CaveSpider.java new file mode 100644 index 0000000..9c37646 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/CaveSpider.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Spider. + */ +public interface CaveSpider extends Spider {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Chicken.java b/vspigot-api/src/main/java/org/bukkit/entity/Chicken.java new file mode 100644 index 0000000..cb3ec6e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Chicken.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Chicken. + */ +public interface Chicken extends Animals {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/ComplexEntityPart.java b/vspigot-api/src/main/java/org/bukkit/entity/ComplexEntityPart.java new file mode 100644 index 0000000..f4ab0bb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/ComplexEntityPart.java @@ -0,0 +1,14 @@ +package org.bukkit.entity; + +/** + * Represents a single part of a {@link ComplexLivingEntity} + */ +public interface ComplexEntityPart extends Entity { + + /** + * Gets the parent {@link ComplexLivingEntity} of this part. + * + * @return Parent complex entity + */ + public ComplexLivingEntity getParent(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/ComplexLivingEntity.java b/vspigot-api/src/main/java/org/bukkit/entity/ComplexLivingEntity.java new file mode 100644 index 0000000..f74411c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/ComplexLivingEntity.java @@ -0,0 +1,16 @@ +package org.bukkit.entity; + +import java.util.Set; + +/** + * Represents a complex living entity - one that is made up of various smaller + * parts + */ +public interface ComplexLivingEntity extends LivingEntity { + /** + * Gets a list of parts that belong to this complex entity + * + * @return List of parts + */ + public Set getParts(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Cow.java b/vspigot-api/src/main/java/org/bukkit/entity/Cow.java new file mode 100644 index 0000000..cd4ed4d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Cow.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Cow. + */ +public interface Cow extends Animals {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Creature.java b/vspigot-api/src/main/java/org/bukkit/entity/Creature.java new file mode 100644 index 0000000..f223f55 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Creature.java @@ -0,0 +1,26 @@ +package org.bukkit.entity; + +/** + * Represents a Creature. Creatures are non-intelligent monsters or animals + * which have very simple abilities. + */ +public interface Creature extends LivingEntity { + + /** + * Instructs this Creature to set the specified LivingEntity as its + * target. + *

          + * Hostile creatures may attack their target, and friendly creatures may + * follow their target. + * + * @param target New LivingEntity to target, or null to clear the target + */ + public void setTarget(LivingEntity target); + + /** + * Gets the current target of this Creature + * + * @return Current target of this creature, or null if none exists + */ + public LivingEntity getTarget(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/CreatureType.java b/vspigot-api/src/main/java/org/bukkit/entity/CreatureType.java new file mode 100644 index 0000000..fd23093 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/CreatureType.java @@ -0,0 +1,102 @@ +package org.bukkit.entity; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a type of creature. + * + * @deprecated Use EntityType instead. + */ +@Deprecated +public enum CreatureType { + // These strings MUST match the strings in nms.EntityTypes and are case sensitive. + CREEPER("Creeper", Creeper.class, 50), + SKELETON("Skeleton", Skeleton.class, 51), + SPIDER("Spider", Spider.class, 52), + GIANT("Giant", Giant.class, 53), + ZOMBIE("Zombie", Zombie.class, 54), + SLIME("Slime", Slime.class, 55), + GHAST("Ghast", Ghast.class, 56), + PIG_ZOMBIE("PigZombie", PigZombie.class, 57), + ENDERMAN("Enderman", Enderman.class, 58), + CAVE_SPIDER("CaveSpider", CaveSpider.class, 59), + SILVERFISH("Silverfish", Silverfish.class, 60), + BLAZE("Blaze", Blaze.class, 61), + MAGMA_CUBE("LavaSlime", MagmaCube.class, 62), + ENDER_DRAGON("EnderDragon", EnderDragon.class, 63), + PIG("Pig", Pig.class, 90), + SHEEP("Sheep", Sheep.class, 91), + COW("Cow", Cow.class, 92), + CHICKEN("Chicken", Chicken.class, 93), + SQUID("Squid", Squid.class, 94), + WOLF("Wolf", Wolf.class, 95), + MUSHROOM_COW("MushroomCow", MushroomCow.class, 96), + SNOWMAN("SnowMan", Snowman.class, 97), + VILLAGER("Villager", Villager.class, 120); + + private String name; + private Class clazz; + private short typeId; + + private static final Map NAME_MAP = new HashMap(); + private static final Map ID_MAP = new HashMap(); + + static { + for (CreatureType type : EnumSet.allOf(CreatureType.class)) { + NAME_MAP.put(type.name, type); + if (type.typeId != 0) { + ID_MAP.put(type.typeId, type); + } + } + } + + private CreatureType(String name, Class clazz, int typeId) { + this.name = name; + this.clazz = clazz; + this.typeId = (short) typeId; + } + + public String getName() { + return name; + } + + public Class getEntityClass() { + return clazz; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public short getTypeId() { + return typeId; + } + + public static CreatureType fromName(String name) { + return NAME_MAP.get(name); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public static CreatureType fromId(int id) { + if (id > Short.MAX_VALUE) { + return null; + } + return ID_MAP.get((short) id); + } + + @Deprecated + public EntityType toEntityType() { + return EntityType.fromName(getName()); + } + + public static CreatureType fromEntityType(EntityType creatureType) { + return fromName(creatureType.getName()); + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Creeper.java b/vspigot-api/src/main/java/org/bukkit/entity/Creeper.java new file mode 100644 index 0000000..a2f7809 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Creeper.java @@ -0,0 +1,21 @@ +package org.bukkit.entity; + +/** + * Represents a Creeper + */ +public interface Creeper extends Monster { + + /** + * Checks if this Creeper is powered (Electrocuted) + * + * @return true if this creeper is powered + */ + public boolean isPowered(); + + /** + * Sets the Powered status of this Creeper + * + * @param value New Powered status + */ + public void setPowered(boolean value); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Damageable.java b/vspigot-api/src/main/java/org/bukkit/entity/Damageable.java new file mode 100644 index 0000000..53877a8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Damageable.java @@ -0,0 +1,112 @@ +package org.bukkit.entity; + +/** + * Represents an {@link Entity} that has health and can take damage. + */ +public interface Damageable extends Entity { + /** + * Deals the given amount of damage to this entity. + * + * @param amount Amount of damage to deal + */ + void damage(double amount); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + void _INVALID_damage(int amount); + + /** + * Deals the given amount of damage to this entity, from a specified + * entity. + * + * @param amount Amount of damage to deal + * @param source Entity which to attribute this damage from + */ + void damage(double amount, Entity source); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + void _INVALID_damage(int amount, Entity source); + + /** + * Gets the entity's health from 0 to {@link #getMaxHealth()}, where 0 is dead. + * + * @return Health represented from 0 to max + */ + double getHealth(); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + int _INVALID_getHealth(); + + /** + * Sets the entity's health from 0 to {@link #getMaxHealth()}, where 0 is + * dead. + * + * @param health New health represented from 0 to max + * @throws IllegalArgumentException Thrown if the health is < 0 or > + * {@link #getMaxHealth()} + */ + void setHealth(double health); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + void _INVALID_setHealth(int health); + + /** + * Gets the maximum health this entity has. + * + * @return Maximum health + */ + double getMaxHealth(); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + int _INVALID_getMaxHealth(); + + /** + * Sets the maximum health this entity can have. + *

          + * If the health of the entity is above the value provided it will be set + * to that value. + *

          + * Note: An entity with a health bar ({@link Player}, {@link EnderDragon}, + * {@link Wither}, etc...} will have their bar scaled accordingly. + * + * @param health amount of health to set the maximum to + */ + void setMaxHealth(double health); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + void _INVALID_setMaxHealth(int health); + + /** + * Resets the max health to the original amount. + */ + void resetMaxHealth(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Egg.java b/vspigot-api/src/main/java/org/bukkit/entity/Egg.java new file mode 100644 index 0000000..2dcc00b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Egg.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a thrown egg. + */ +public interface Egg extends Projectile {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/EnderCrystal.java b/vspigot-api/src/main/java/org/bukkit/entity/EnderCrystal.java new file mode 100644 index 0000000..bac547e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/EnderCrystal.java @@ -0,0 +1,7 @@ +package org.bukkit.entity; + +/** + * A crystal that heals nearby EnderDragons + */ +public interface EnderCrystal extends Entity { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/EnderDragon.java b/vspigot-api/src/main/java/org/bukkit/entity/EnderDragon.java new file mode 100644 index 0000000..609f3ba --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/EnderDragon.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents an Ender Dragon + */ +public interface EnderDragon extends ComplexLivingEntity { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/EnderDragonPart.java b/vspigot-api/src/main/java/org/bukkit/entity/EnderDragonPart.java new file mode 100644 index 0000000..9516f56 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/EnderDragonPart.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents an ender dragon part + */ +public interface EnderDragonPart extends ComplexEntityPart, Damageable { + public EnderDragon getParent(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/EnderPearl.java b/vspigot-api/src/main/java/org/bukkit/entity/EnderPearl.java new file mode 100644 index 0000000..db18a90 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/EnderPearl.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a thrown Ender Pearl entity + */ +public interface EnderPearl extends Projectile { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/EnderSignal.java b/vspigot-api/src/main/java/org/bukkit/entity/EnderSignal.java new file mode 100644 index 0000000..3d2d76c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/EnderSignal.java @@ -0,0 +1,9 @@ +package org.bukkit.entity; + +/** + * Represents an Ender Signal, which is often created upon throwing an ender + * eye + */ +public interface EnderSignal extends Entity { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Enderman.java b/vspigot-api/src/main/java/org/bukkit/entity/Enderman.java new file mode 100644 index 0000000..0b66a92 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Enderman.java @@ -0,0 +1,23 @@ +package org.bukkit.entity; + +import org.bukkit.material.MaterialData; + +/** + * Represents an Enderman. + */ +public interface Enderman extends Monster { + + /** + * Get the id and data of the block that the Enderman is carrying. + * + * @return MaterialData containing the id and data of the block + */ + public MaterialData getCarriedMaterial(); + + /** + * Set the id and data of the block that the Enderman is carring. + * + * @param material data to set the carried block to + */ + public void setCarriedMaterial(MaterialData material); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Entity.java b/vspigot-api/src/main/java/org/bukkit/entity/Entity.java new file mode 100644 index 0000000..5a1e895 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Entity.java @@ -0,0 +1,316 @@ +package org.bukkit.entity; + +import org.bukkit.Location; +import org.bukkit.EntityEffect; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.metadata.Metadatable; +import org.bukkit.util.Vector; + +import java.util.List; +import java.util.UUID; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +/** + * Represents a base entity in the world + */ +public interface Entity extends Metadatable { + + /** + * Gets the entity's current position + * + * @return a new copy of Location containing the position of this entity + */ + public Location getLocation(); + + /** + * Stores the entity's current position in the provided Location object. + *

          + * If the provided Location is null this method does nothing and returns + * null. + * + * @return The Location object provided or null + */ + public Location getLocation(Location loc); + + /** + * Sets this entity's velocity + * + * @param velocity New velocity to travel with + */ + public void setVelocity(Vector velocity); + + /** + * Gets this entity's current velocity + * + * @return Current travelling velocity of this entity + */ + public Vector getVelocity(); + + /** + * Returns true if the entity is supported by a block. This value is a + * state updated by the server and is not recalculated unless the entity + * moves. + * + * @return True if entity is on ground. + */ + public boolean isOnGround(); + + /** + * Gets the current world this entity resides in + * + * @return World + */ + public World getWorld(); + + /** + * Teleports this entity to the given location. If this entity is riding a + * vehicle, it will be dismounted prior to teleportation. + * + * @param location New location to teleport this entity to + * @return true if the teleport was successful + */ + public boolean teleport(Location location); + + /** + * Teleports this entity to the given location. If this entity is riding a + * vehicle, it will be dismounted prior to teleportation. + * + * @param location New location to teleport this entity to + * @param cause The cause of this teleportation + * @return true if the teleport was successful + */ + public boolean teleport(Location location, TeleportCause cause); + + /** + * Teleports this entity to the target Entity. If this entity is riding a + * vehicle, it will be dismounted prior to teleportation. + * + * @param destination Entity to teleport this entity to + * @return true if the teleport was successful + */ + public boolean teleport(Entity destination); + + /** + * Teleports this entity to the target Entity. If this entity is riding a + * vehicle, it will be dismounted prior to teleportation. + * + * @param destination Entity to teleport this entity to + * @param cause The cause of this teleportation + * @return true if the teleport was successful + */ + public boolean teleport(Entity destination, TeleportCause cause); + + /** + * Returns a list of entities within a bounding box centered around this + * entity + * + * @param x 1/2 the size of the box along x axis + * @param y 1/2 the size of the box along y axis + * @param z 1/2 the size of the box along z axis + * @return List List of entities nearby + */ + public List getNearbyEntities(double x, double y, double z); + + /** + * Returns a unique id for this entity + * + * @return Entity id + */ + public int getEntityId(); + + /** + * Returns the entity's current fire ticks (ticks before the entity stops + * being on fire). + * + * @return int fireTicks + */ + public int getFireTicks(); + + /** + * Returns the entity's maximum fire ticks. + * + * @return int maxFireTicks + */ + public int getMaxFireTicks(); + + /** + * Sets the entity's current fire ticks (ticks before the entity stops + * being on fire). + * + * @param ticks Current ticks remaining + */ + public void setFireTicks(int ticks); + + /** + * Mark the entity's removal. + */ + public void remove(); + + /** + * Returns true if this entity has been marked for removal. + * + * @return True if it is dead. + */ + public boolean isDead(); + + /** + * Returns false if the entity has died or been despawned for some other + * reason. + * + * @return True if valid. + */ + public boolean isValid(); + + /** + * Gets the {@link Server} that contains this Entity + * + * @return Server instance running this Entity + */ + public Server getServer(); + + /** + * Gets the primary passenger of a vehicle. For vehicles that could have + * multiple passengers, this will only return the primary passenger. + * + * @return an entity + */ + public abstract Entity getPassenger(); + + /** + * Set the passenger of a vehicle. + * + * @param passenger The new passenger. + * @return false if it could not be done for whatever reason + */ + public abstract boolean setPassenger(Entity passenger); + + /** + * Check if a vehicle has passengers. + * + * @return True if the vehicle has no passengers. + */ + public abstract boolean isEmpty(); + + /** + * Eject any passenger. + * + * @return True if there was a passenger. + */ + public abstract boolean eject(); + + /** + * Returns the distance this entity has fallen + * + * @return The distance. + */ + public float getFallDistance(); + + /** + * Sets the fall distance for this entity + * + * @param distance The new distance. + */ + public void setFallDistance(float distance); + + /** + * Record the last {@link EntityDamageEvent} inflicted on this entity + * + * @param event a {@link EntityDamageEvent} + */ + public void setLastDamageCause(EntityDamageEvent event); + + /** + * Retrieve the last {@link EntityDamageEvent} inflicted on this entity. + * This event may have been cancelled. + * + * @return the last known {@link EntityDamageEvent} or null if hitherto + * unharmed + */ + public EntityDamageEvent getLastDamageCause(); + + /** + * Returns a unique and persistent id for this entity + * + * @return unique id + */ + public UUID getUniqueId(); + + /** + * Gets the amount of ticks this entity has lived for. + *

          + * This is the equivalent to "age" in entities. + * + * @return Age of entity + */ + public int getTicksLived(); + + /** + * Sets the amount of ticks this entity has lived for. + *

          + * This is the equivalent to "age" in entities. May not be less than one + * tick. + * + * @param value Age of entity + */ + public void setTicksLived(int value); + + /** + * Performs the specified {@link EntityEffect} for this entity. + *

          + * This will be viewable to all players near the entity. + * + * @param type Effect to play. + */ + public void playEffect(EntityEffect type); + + /** + * Get the type of the entity. + * + * @return The entity type. + */ + public EntityType getType(); + + /** + * Returns whether this entity is inside a vehicle. + * + * @return True if the entity is in a vehicle. + */ + public boolean isInsideVehicle(); + + /** + * Leave the current vehicle. If the entity is currently in a vehicle (and + * is removed from it), true will be returned, otherwise false will be + * returned. + * + * @return True if the entity was in a vehicle. + */ + public boolean leaveVehicle(); + + /** + * Get the vehicle that this player is inside. If there is no vehicle, + * null will be returned. + * + * @return The current vehicle. + */ + public Entity getVehicle(); + + // Spigot Start + public class Spigot + { + + /** + * Returns whether this entity is invulnerable. + * + * @return True if the entity is invulnerable. + */ + public boolean isInvulnerable() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); + // Spigot End +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/EntityType.java b/vspigot-api/src/main/java/org/bukkit/entity/EntityType.java new file mode 100644 index 0000000..39ecb13 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/EntityType.java @@ -0,0 +1,267 @@ +package org.bukkit.entity; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.entity.minecart.SpawnerMinecart; +import org.bukkit.entity.minecart.RideableMinecart; +import org.bukkit.entity.minecart.ExplosiveMinecart; +import org.bukkit.entity.minecart.PoweredMinecart; +import org.bukkit.entity.minecart.StorageMinecart; +import org.bukkit.inventory.ItemStack; +import org.bukkit.Location; +import org.bukkit.World; + +public enum EntityType { + + // These strings MUST match the strings in nms.EntityTypes and are case sensitive. + /** + * An item resting on the ground. + *

          + * Spawn with {@link World#dropItem(Location, ItemStack)} or {@link + * World#dropItemNaturally(Location, ItemStack)} + */ + DROPPED_ITEM("Item", Item.class, 1, false), + /** + * An experience orb. + */ + EXPERIENCE_ORB("XPOrb", ExperienceOrb.class, 2), + /** + * A leash attached to a fencepost. + */ + LEASH_HITCH("LeashKnot", LeashHitch.class, 8), + /** + * A painting on a wall. + */ + PAINTING("Painting", Painting.class, 9), + /** + * An arrow projectile; may get stuck in the ground. + */ + ARROW("Arrow", Arrow.class, 10), + /** + * A flying snowball. + */ + SNOWBALL("Snowball", Snowball.class, 11), + /** + * A flying large fireball, as thrown by a Ghast for example. + */ + FIREBALL("Fireball", LargeFireball.class, 12), + /** + * A flying small fireball, such as thrown by a Blaze or player. + */ + SMALL_FIREBALL("SmallFireball", SmallFireball.class, 13), + /** + * A flying ender pearl. + */ + ENDER_PEARL("ThrownEnderpearl", EnderPearl.class, 14), + /** + * An ender eye signal. + */ + ENDER_SIGNAL("EyeOfEnderSignal", EnderSignal.class, 15), + /** + * A flying experience bottle. + */ + THROWN_EXP_BOTTLE("ThrownExpBottle", ThrownExpBottle.class, 17), + /** + * An item frame on a wall. + */ + ITEM_FRAME("ItemFrame", ItemFrame.class, 18), + /** + * A flying wither skull projectile. + */ + WITHER_SKULL("WitherSkull", WitherSkull.class, 19), + /** + * Primed TNT that is about to explode. + */ + PRIMED_TNT("PrimedTnt", TNTPrimed.class, 20), + /** + * A block that is going to or is about to fall. + */ + FALLING_BLOCK("FallingSand", FallingBlock.class, 21, false), + FIREWORK("FireworksRocketEntity", Firework.class, 22, false), + /** + * @see CommandMinecart + */ + MINECART_COMMAND("MinecartCommandBlock", CommandMinecart.class, 40), + /** + * A placed boat. + */ + BOAT("Boat", Boat.class, 41), + /** + * @see RideableMinecart + */ + MINECART("MinecartRideable", RideableMinecart.class, 42), + /** + * @see StorageMinecart + */ + MINECART_CHEST("MinecartChest", StorageMinecart.class, 43), + /** + * @see PoweredMinecart + */ + MINECART_FURNACE("MinecartFurnace", PoweredMinecart.class, 44), + /** + * @see ExplosiveMinecart + */ + MINECART_TNT("MinecartTNT", ExplosiveMinecart.class, 45), + /** + * @see HopperMinecart + */ + MINECART_HOPPER("MinecartHopper", HopperMinecart.class, 46), + /** + * @see SpawnerMinecart + */ + MINECART_MOB_SPAWNER("MinecartMobSpawner", SpawnerMinecart.class, 47), + CREEPER("Creeper", Creeper.class, 50), + SKELETON("Skeleton", Skeleton.class, 51), + SPIDER("Spider", Spider.class, 52), + GIANT("Giant", Giant.class, 53), + ZOMBIE("Zombie", Zombie.class, 54), + SLIME("Slime", Slime.class, 55), + GHAST("Ghast", Ghast.class, 56), + PIG_ZOMBIE("PigZombie", PigZombie.class, 57), + ENDERMAN("Enderman", Enderman.class, 58), + CAVE_SPIDER("CaveSpider", CaveSpider.class, 59), + SILVERFISH("Silverfish", Silverfish.class, 60), + BLAZE("Blaze", Blaze.class, 61), + MAGMA_CUBE("LavaSlime", MagmaCube.class, 62), + ENDER_DRAGON("EnderDragon", EnderDragon.class, 63), + WITHER("WitherBoss", Wither.class, 64), + BAT("Bat", Bat.class, 65), + WITCH("Witch", Witch.class, 66), + PIG("Pig", Pig.class, 90), + SHEEP("Sheep", Sheep.class, 91), + COW("Cow", Cow.class, 92), + CHICKEN("Chicken", Chicken.class, 93), + SQUID("Squid", Squid.class, 94), + WOLF("Wolf", Wolf.class, 95), + MUSHROOM_COW("MushroomCow", MushroomCow.class, 96), + SNOWMAN("SnowMan", Snowman.class, 97), + OCELOT("Ozelot", Ocelot.class, 98), + IRON_GOLEM("VillagerGolem", IronGolem.class, 99), + HORSE("EntityHorse", Horse.class, 100), + VILLAGER("Villager", Villager.class, 120), + ENDER_CRYSTAL("EnderCrystal", EnderCrystal.class, 200), + // These don't have an entity ID in nms.EntityTypes. + /** + * A flying splash potion. + */ + SPLASH_POTION(null, ThrownPotion.class, -1, false), + /** + * A flying chicken egg. + */ + EGG(null, Egg.class, -1, false), + /** + * A fishing line and bobber. + */ + FISHING_HOOK(null, Fish.class, -1, false), + /** + * A bolt of lightning. + *

          + * Spawn with {@link World#strikeLightning(Location)}. + */ + LIGHTNING(null, LightningStrike.class, -1, false), + WEATHER(null, Weather.class, -1, false), + PLAYER(null, Player.class, -1, false), + COMPLEX_PART(null, ComplexEntityPart.class, -1, false), + /** + * An unknown entity without an Entity Class + */ + UNKNOWN(null, null, -1, false); + + private String name; + private Class clazz; + private short typeId; + private boolean independent, living; + + private static final Map NAME_MAP = new HashMap(); + private static final Map ID_MAP = new HashMap(); + + static { + for (EntityType type : values()) { + if (type.name != null) { + NAME_MAP.put(type.name.toLowerCase(), type); + } + if (type.typeId > 0) { + ID_MAP.put(type.typeId, type); + } + } + } + + private EntityType(String name, Class clazz, int typeId) { + this(name, clazz, typeId, true); + } + + private EntityType(String name, Class clazz, int typeId, boolean independent) { + this.name = name; + this.clazz = clazz; + this.typeId = (short) typeId; + this.independent = independent; + if (clazz != null) { + this.living = LivingEntity.class.isAssignableFrom(clazz); + } + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public String getName() { + return name; + } + + public Class getEntityClass() { + return clazz; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public short getTypeId() { + return typeId; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public static EntityType fromName(String name) { + if (name == null) { + return null; + } + return NAME_MAP.get(name.toLowerCase()); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public static EntityType fromId(int id) { + if (id > Short.MAX_VALUE) { + return null; + } + return ID_MAP.get((short) id); + } + + /** + * Some entities cannot be spawned using {@link + * World#spawnEntity(Location, EntityType)} or {@link + * World#spawn(Location, Class)}, usually because they require additional + * information in order to spawn. + * + * @return False if the entity type cannot be spawned + */ + public boolean isSpawnable() { + return independent; + } + + public boolean isAlive() { + return living; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/ExperienceOrb.java b/vspigot-api/src/main/java/org/bukkit/entity/ExperienceOrb.java new file mode 100644 index 0000000..c286edf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/ExperienceOrb.java @@ -0,0 +1,21 @@ +package org.bukkit.entity; + +/** + * Represents an Experience Orb. + */ +public interface ExperienceOrb extends Entity { + + /** + * Gets how much experience is contained within this orb + * + * @return Amount of experience + */ + public int getExperience(); + + /** + * Sets how much experience is contained within this orb + * + * @param value Amount of experience + */ + public void setExperience(int value); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Explosive.java b/vspigot-api/src/main/java/org/bukkit/entity/Explosive.java new file mode 100644 index 0000000..48650f6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Explosive.java @@ -0,0 +1,35 @@ +package org.bukkit.entity; + +/** + * A representation of an explosive entity + */ +public interface Explosive extends Entity { + + /** + * Set the radius affected by this explosive's explosion + * + * @param yield The explosive yield + */ + public void setYield(float yield); + + /** + * Return the radius or yield of this explosive's explosion + * + * @return the radius of blocks affected + */ + public float getYield(); + + /** + * Set whether or not this explosive's explosion causes fire + * + * @param isIncendiary Whether it should cause fire + */ + public void setIsIncendiary(boolean isIncendiary); + + /** + * Return whether or not this explosive creates a fire when exploding + * + * @return true if the explosive creates fire, false otherwise + */ + public boolean isIncendiary(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/FallingBlock.java b/vspigot-api/src/main/java/org/bukkit/entity/FallingBlock.java new file mode 100644 index 0000000..799c43a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/FallingBlock.java @@ -0,0 +1,50 @@ +package org.bukkit.entity; + +import org.bukkit.Material; + +/** + * Represents a falling block + */ +public interface FallingBlock extends Entity { + + /** + * Get the Material of the falling block + * + * @return Material of the block + */ + Material getMaterial(); + + /** + * Get the ID of the falling block + * + * @return ID type of the block + * @deprecated Magic value + */ + @Deprecated + int getBlockId(); + + /** + * Get the data for the falling block + * + * @return data of the block + * @deprecated Magic value + */ + @Deprecated + byte getBlockData(); + + /** + * Get if the falling block will break into an item if it cannot be placed + * + * @return true if the block will break into an item when obstructed + */ + boolean getDropItem(); + + /** + * Set if the falling block will break into an item if it cannot be placed + * + * @param drop true to break into an item when obstructed + */ + void setDropItem(boolean drop); + + org.bukkit.Location getSourceLoc(); // PaperSpigot - Add FallingBlock and TNT source location API +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/FallingSand.java b/vspigot-api/src/main/java/org/bukkit/entity/FallingSand.java new file mode 100644 index 0000000..758d47d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/FallingSand.java @@ -0,0 +1,9 @@ +package org.bukkit.entity; + +/** + * Represents a falling block. + * + * @deprecated See {@link FallingBlock} + */ +@Deprecated +public interface FallingSand extends FallingBlock {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Fireball.java b/vspigot-api/src/main/java/org/bukkit/entity/Fireball.java new file mode 100644 index 0000000..56ed578 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Fireball.java @@ -0,0 +1,24 @@ +package org.bukkit.entity; + +import org.bukkit.util.Vector; + +/** + * Represents a Fireball. + */ +public interface Fireball extends Projectile, Explosive { + + /** + * Fireballs fly straight and do not take setVelocity(...) well. + * + * @param direction the direction this fireball is flying toward + */ + public void setDirection(Vector direction); + + /** + * Retrieve the direction this fireball is heading toward + * + * @return the direction + */ + public Vector getDirection(); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Firework.java b/vspigot-api/src/main/java/org/bukkit/entity/Firework.java new file mode 100644 index 0000000..b8a8c07 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Firework.java @@ -0,0 +1,26 @@ +package org.bukkit.entity; + +import org.bukkit.inventory.meta.FireworkMeta; + +public interface Firework extends Entity { + + /** + * Get a copy of the fireworks meta + * + * @return A copy of the current Firework meta + */ + FireworkMeta getFireworkMeta(); + + /** + * Apply the provided meta to the fireworks + * + * @param meta The FireworkMeta to apply + */ + void setFireworkMeta(FireworkMeta meta); + + /** + * Cause this firework to explode at earliest opportunity, as if it has no + * remaining fuse. + */ + void detonate(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Fish.java b/vspigot-api/src/main/java/org/bukkit/entity/Fish.java new file mode 100644 index 0000000..12ed1ed --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Fish.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a fishing hook. + * @deprecated in favor of {@link FishHook} + */ +public interface Fish extends FishHook { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/FishHook.java b/vspigot-api/src/main/java/org/bukkit/entity/FishHook.java new file mode 100644 index 0000000..45b7f03 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/FishHook.java @@ -0,0 +1,28 @@ +package org.bukkit.entity; + +/** + * Represents a fishing hook. + */ +public interface FishHook extends Projectile { + /** + * Gets the chance of a fish biting. + *

          + * 0.0 = No Chance.
          + * 1.0 = Instant catch. + * + * @return chance the bite chance + */ + public double getBiteChance(); + + /** + * Sets the chance of a fish biting. + *

          + * 0.0 = No Chance.
          + * 1.0 = Instant catch. + * + * @param chance the bite chance + * @throws IllegalArgumentException if the bite chance is not between 0 + * and 1 + */ + public void setBiteChance(double chance) throws IllegalArgumentException; +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Flying.java b/vspigot-api/src/main/java/org/bukkit/entity/Flying.java new file mode 100644 index 0000000..4f16a26 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Flying.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Flying Entity. + */ +public interface Flying extends LivingEntity {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Ghast.java b/vspigot-api/src/main/java/org/bukkit/entity/Ghast.java new file mode 100644 index 0000000..3f5edf7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Ghast.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Ghast. + */ +public interface Ghast extends Flying {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Giant.java b/vspigot-api/src/main/java/org/bukkit/entity/Giant.java new file mode 100644 index 0000000..610de57 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Giant.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Giant. + */ +public interface Giant extends Monster {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Golem.java b/vspigot-api/src/main/java/org/bukkit/entity/Golem.java new file mode 100644 index 0000000..4165977 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Golem.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * A mechanical creature that may harm enemies. + */ +public interface Golem extends Creature { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Hanging.java b/vspigot-api/src/main/java/org/bukkit/entity/Hanging.java new file mode 100644 index 0000000..67e9b61 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Hanging.java @@ -0,0 +1,22 @@ +package org.bukkit.entity; + +import org.bukkit.block.BlockFace; +import org.bukkit.material.Attachable; + +/** + * Represents a Hanging entity + */ +public interface Hanging extends Entity, Attachable { + + /** + * Sets the direction of the hanging entity, potentially overriding rules + * of placement. Note that if the result is not valid the object would + * normally drop as an item. + * + * @param face The new direction. + * @param force Whether to force it. + * @return False if force was false and there was no block for it to + * attach to in order to face the given direction. + */ + public boolean setFacingDirection(BlockFace face, boolean force); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Horse.java b/vspigot-api/src/main/java/org/bukkit/entity/Horse.java new file mode 100644 index 0000000..e90d318 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Horse.java @@ -0,0 +1,256 @@ +package org.bukkit.entity; + +import org.bukkit.inventory.HorseInventory; +import org.bukkit.inventory.InventoryHolder; + +/** + * Represents a Horse. + */ +public interface Horse extends Animals, Vehicle, InventoryHolder, Tameable { + + /** + * Represents the different types of horses that may exist. + */ + public enum Variant { + /** + * A normal horse + */ + HORSE, + /** + * A donkey + */ + DONKEY, + /** + * A mule + */ + MULE, + /** + * An undead horse + */ + UNDEAD_HORSE, + /** + * A skeleton horse + */ + SKELETON_HORSE, + ; + } + + /** + * Represents the base color that the horse has. + */ + public enum Color { + /** + * Snow white + */ + WHITE, + /** + * Very light brown + */ + CREAMY, + /** + * Chestnut + */ + CHESTNUT, + /** + * Light brown + */ + BROWN, + /** + * Pitch black + */ + BLACK, + /** + * Gray + */ + GRAY, + /** + * Dark brown + */ + DARK_BROWN, + ; + } + + /** + * Represents the style, or markings, that the horse has. + */ + public enum Style { + /** + * No markings + */ + NONE, + /** + * White socks or stripes + */ + WHITE, + /** + * Milky splotches + */ + WHITEFIELD, + /** + * Round white dots + */ + WHITE_DOTS, + /** + * Small black dots + */ + BLACK_DOTS, + ; + } + + /** + * Gets the horse's variant. + *

          + * A horse's variant defines its physical appearance and capabilities. + * Whether a horse is a regular horse, donkey, mule, or other kind of + * horse is determined using the variant. + * + * @return a {@link Variant} representing the horse's variant + */ + public Variant getVariant(); + + /** + * Sets the horse's variant. + *

          + * A horse's variant defines its physical appearance and capabilities. + * Whether a horse is a regular horse, donkey, mule, or other kind of + * horse can be set using the variant. + *

          + * Setting a horse's variant does not change its attributes such as + * its owner and its tamed status, but changing a mule or donkey + * with a chest to another variant which does not support a chest + * will remove the chest and its contents. + * + * @param variant a {@link Variant} for this horse + */ + public void setVariant(Variant variant); + + /** + * Gets the horse's color. + *

          + * Colors only apply to horses, not to donkeys, mules, skeleton horses + * or undead horses. + * + * @return a {@link Color} representing the horse's group + */ + public Color getColor(); + + /** + * Sets the horse's color. + *

          + * Attempting to set a color for any donkey, mule, skeleton horse or + * undead horse will not result in a change. + * + * @param color a {@link Color} for this horse + */ + public void setColor(Color color); + + /** + * Gets the horse's style. + * Styles determine what kind of markings or patterns a horse has. + *

          + * Styles only apply to horses, not to donkeys, mules, skeleton horses + * or undead horses. + * + * @return a {@link Style} representing the horse's style + */ + public Style getStyle(); + + /** + * Sets the style of this horse. + * Styles determine what kind of markings or patterns a horse has. + *

          + * Attempting to set a style for any donkey, mule, skeleton horse or + * undead horse will not result in a change. + * + * @param style a {@link Style} for this horse + */ + public void setStyle(Style style); + + /** + * Gets whether the horse has a chest equipped. + * + * @return true if the horse has chest storage + */ + public boolean isCarryingChest(); + + /** + * Sets whether the horse has a chest equipped. + * Removing a chest will also clear the chest's inventory. + * + * @param chest true if the horse should have a chest + */ + public void setCarryingChest(boolean chest); + + /** + * Gets the domestication level of this horse. + *

          + * A higher domestication level indicates that the horse is closer to + * becoming tame. As the domestication level gets closer to the max + * domestication level, the chance of the horse becoming tame increases. + * + * @return domestication level + */ + public int getDomestication(); + + /** + * Sets the domestication level of this horse. + *

          + * Setting the domestication level to a high value will increase the + * horse's chances of becoming tame. + *

          + * Domestication level must be greater than zero and no greater than + * the max domestication level of the horse, determined with + * {@link #getMaxDomestication()} + * + * @param level domestication level + */ + public void setDomestication(int level); + + /** + * Gets the maximum domestication level of this horse. + *

          + * The higher this level is, the longer it will likely take + * for the horse to be tamed. + * + * @return the max domestication level + */ + public int getMaxDomestication(); + + /** + * Sets the maximum domestication level of this horse. + *

          + * Setting a higher max domestication will increase the amount of + * domesticating (feeding, riding, etc.) necessary in order to tame it, + * while setting a lower max value will have the opposite effect. + *

          + * Maximum domestication must be greater than zero. + * + * @param level the max domestication level + */ + public void setMaxDomestication(int level); + + /** + * Gets the jump strength of this horse. + *

          + * Jump strength defines how high the horse can jump. A higher jump strength + * increases how high a jump will go. + * + * @return the horse's jump strength + */ + public double getJumpStrength(); + + /** + * Sets the jump strength of this horse. + *

          + * A higher jump strength increases how high a jump will go. + * Setting a jump strength to 0 will result in no jump. + * You cannot set a jump strength to a value below 0 or + * above 2. + * + * @param strength jump strength for this horse + */ + public void setJumpStrength(double strength); + + @Override + public HorseInventory getInventory(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/HumanEntity.java b/vspigot-api/src/main/java/org/bukkit/entity/HumanEntity.java new file mode 100644 index 0000000..3f8646d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/HumanEntity.java @@ -0,0 +1,177 @@ +package org.bukkit.entity; + +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.permissions.Permissible; + +/** + * Represents a human entity, such as an NPC or a player + */ +public interface HumanEntity extends LivingEntity, AnimalTamer, Permissible, InventoryHolder { + + /** + * Returns the name of this player + * + * @return Player name + */ + public String getName(); + + /** + * Get the player's inventory. + * + * @return The inventory of the player, this also contains the armor + * slots. + */ + public PlayerInventory getInventory(); + + /** + * Get the player's EnderChest inventory + * + * @return The EnderChest of the player + */ + public Inventory getEnderChest(); + + /** + * If the player currently has an inventory window open, this method will + * set a property of that window, such as the state of a progress bar. + * + * @param prop The property. + * @param value The value to set the property to. + * @return True if the property was successfully set. + */ + public boolean setWindowProperty(InventoryView.Property prop, int value); + + /** + * Gets the inventory view the player is currently viewing. If they do not + * have an inventory window open, it returns their internal crafting view. + * + * @return The inventory view. + */ + public InventoryView getOpenInventory(); + + /** + * Opens an inventory window with the specified inventory on the top and + * the player's inventory on the bottom. + * + * @param inventory The inventory to open + * @return The newly opened inventory view + */ + public InventoryView openInventory(Inventory inventory); + + /** + * Opens an empty workbench inventory window with the player's inventory + * on the bottom. + * + * @param location The location to attach it to. If null, the player's + * location is used. + * @param force If false, and there is no workbench block at the location, + * no inventory will be opened and null will be returned. + * @return The newly opened inventory view, or null if it could not be + * opened. + */ + public InventoryView openWorkbench(Location location, boolean force); + + /** + * Opens an empty enchanting inventory window with the player's inventory + * on the bottom. + * + * @param location The location to attach it to. If null, the player's + * location is used. + * @param force If false, and there is no enchanting table at the + * location, no inventory will be opened and null will be returned. + * @return The newly opened inventory view, or null if it could not be + * opened. + */ + public InventoryView openEnchanting(Location location, boolean force); + + /** + * Opens an inventory window to the specified inventory view. + * + * @param inventory The view to open + */ + public void openInventory(InventoryView inventory); + + /** + * Force-closes the currently open inventory view for this player, if any. + */ + public void closeInventory(); + + /** + * Returns the ItemStack currently in your hand, can be empty. + * + * @return The ItemStack of the item you are currently holding. + */ + public ItemStack getItemInHand(); + + /** + * Sets the item to the given ItemStack, this will replace whatever the + * user was holding. + * + * @param item The ItemStack which will end up in the hand + */ + public void setItemInHand(ItemStack item); + + /** + * Returns the ItemStack currently on your cursor, can be empty. Will + * always be empty if the player currently has no open window. + * + * @return The ItemStack of the item you are currently moving around. + */ + public ItemStack getItemOnCursor(); + + /** + * Sets the item to the given ItemStack, this will replace whatever the + * user was moving. Will always be empty if the player currently has no + * open window. + * + * @param item The ItemStack which will end up in the hand + */ + public void setItemOnCursor(ItemStack item); + + /** + * Returns whether this player is slumbering. + * + * @return slumber state + */ + public boolean isSleeping(); + + /** + * Get the sleep ticks of the player. This value may be capped. + * + * @return slumber ticks + */ + public int getSleepTicks(); + + /** + * Gets this human's current {@link GameMode} + * + * @return Current game mode + */ + public GameMode getGameMode(); + + /** + * Sets this human's current {@link GameMode} + * + * @param mode New game mode + */ + public void setGameMode(GameMode mode); + + /** + * Check if the player is currently blocking (ie with a sword). + * + * @return Whether they are blocking. + */ + public boolean isBlocking(); + + /** + * Get the total amount of experience required for the player to level + * + * @return Experience required to level up + */ + public int getExpToLevel(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/IronGolem.java b/vspigot-api/src/main/java/org/bukkit/entity/IronGolem.java new file mode 100644 index 0000000..655e37c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/IronGolem.java @@ -0,0 +1,22 @@ +package org.bukkit.entity; + +/** + * An iron Golem that protects Villages. + */ +public interface IronGolem extends Golem { + + /** + * Gets whether this iron golem was built by a player. + * + * @return Whether this iron golem was built by a player + */ + public boolean isPlayerCreated(); + + /** + * Sets whether this iron golem was built by a player or not. + * + * @param playerCreated true if you want to set the iron golem as being + * player created, false if you want it to be a natural village golem. + */ + public void setPlayerCreated(boolean playerCreated); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Item.java b/vspigot-api/src/main/java/org/bukkit/entity/Item.java new file mode 100644 index 0000000..90260b7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Item.java @@ -0,0 +1,37 @@ +package org.bukkit.entity; + +import org.bukkit.inventory.ItemStack; + +/** + * Represents an Item. + */ +public interface Item extends Entity { + + /** + * Gets the item stack associated with this item drop. + * + * @return An item stack. + */ + public ItemStack getItemStack(); + + /** + * Sets the item stack associated with this item drop. + * + * @param stack An item stack. + */ + public void setItemStack(ItemStack stack); + + /** + * Gets the delay before this Item is available to be picked up by players + * + * @return Remaining delay + */ + public int getPickupDelay(); + + /** + * Sets the delay before this Item is available to be picked up by players + * + * @param delay New delay + */ + public void setPickupDelay(int delay); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/ItemFrame.java b/vspigot-api/src/main/java/org/bukkit/entity/ItemFrame.java new file mode 100644 index 0000000..8b86815 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/ItemFrame.java @@ -0,0 +1,39 @@ +package org.bukkit.entity; + +import org.bukkit.Rotation; +import org.bukkit.inventory.ItemStack; + +/** + * Represents an Item Frame + */ +public interface ItemFrame extends Hanging { + + /** + * Get the item in this frame + * + * @return a defensive copy the item in this item frame + */ + public ItemStack getItem(); + + /** + * Set the item in this frame + * + * @param item the new item + */ + public void setItem(ItemStack item); + + /** + * Get the rotation of the frame's item + * + * @return the direction + */ + public Rotation getRotation(); + + /** + * Set the rotation of the frame's item + * + * @param rotation the new rotation + * @throws IllegalArgumentException if rotation is null + */ + public void setRotation(Rotation rotation) throws IllegalArgumentException; +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/LargeFireball.java b/vspigot-api/src/main/java/org/bukkit/entity/LargeFireball.java new file mode 100644 index 0000000..fc3a109 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/LargeFireball.java @@ -0,0 +1,7 @@ +package org.bukkit.entity; + +/** + * Represents a large {@link Fireball} + */ +public interface LargeFireball extends Fireball { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/LeashHitch.java b/vspigot-api/src/main/java/org/bukkit/entity/LeashHitch.java new file mode 100644 index 0000000..9ac04c1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/LeashHitch.java @@ -0,0 +1,7 @@ +package org.bukkit.entity; + +/** + * Represents a Leash Hitch on a fence + */ +public interface LeashHitch extends Hanging { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/LightningStrike.java b/vspigot-api/src/main/java/org/bukkit/entity/LightningStrike.java new file mode 100644 index 0000000..1ed4ac9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/LightningStrike.java @@ -0,0 +1,32 @@ +package org.bukkit.entity; + +/** + * Represents an instance of a lightning strike. May or may not do damage. + */ +public interface LightningStrike extends Weather { + + /** + * Returns whether the strike is an effect that does no damage. + * + * @return whether the strike is an effect + */ + public boolean isEffect(); + + + public class Spigot extends Entity.Spigot + { + + /* + * Returns whether the strike is silent. + * + * @return whether the strike is silent. + */ + public boolean isSilent() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + } + + Spigot spigot(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/LivingEntity.java b/vspigot-api/src/main/java/org/bukkit/entity/LivingEntity.java new file mode 100644 index 0000000..e00f77b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/LivingEntity.java @@ -0,0 +1,409 @@ +package org.bukkit.entity; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import net.valorhcf.knockback.Knockback; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.projectiles.ProjectileSource; + +/** + * Represents a living entity, such as a monster or player + */ +public interface LivingEntity extends Entity, Damageable, ProjectileSource { + + // joeleoli start + /** + * Gets the knockback profile of this player. + */ + public Knockback getKbProfile(); + + /** + * Sets the knockback profile of this player. The player's knockback + * profile overrides the global knockback profile. + */ + public void setKbProfile(Knockback profile); + + // joeleoli end + + /** + * Gets the height of the living entity's eyes above its Location. + * + * @return height of the living entity's eyes above its location + */ + public double getEyeHeight(); + + /** + * Gets the height of the living entity's eyes above its Location. + * + * @param ignoreSneaking if set to true, the effects of sneaking will be + * ignored + * @return height of the living entity's eyes above its location + */ + public double getEyeHeight(boolean ignoreSneaking); + + /** + * Get a Location detailing the current eye position of the living entity. + * + * @return a location at the eyes of the living entity + */ + public Location getEyeLocation(); + + /** + * Gets all blocks along the living entity's line of sight. + *

          + * This list contains all blocks from the living entity's eye position to + * target inclusive. + * + * @param transparent HashSet containing all transparent block IDs (set to + * null for only air) + * @param maxDistance this is the maximum distance to scan (may be limited + * by server by at least 100 blocks, no less) + * @return list containing all blocks along the living entity's line of + * sight + * @deprecated Magic value + */ + @Deprecated + public List getLineOfSight(HashSet transparent, int maxDistance); + + /** + * Gets the block that the living entity has targeted. + * + * @param transparent HashSet containing all transparent block IDs (set to + * null for only air) + * @param maxDistance this is the maximum distance to scan (may be limited + * by server by at least 100 blocks, no less) + * @return block that the living entity has targeted + * @deprecated Magic value + */ + @Deprecated + public Block getTargetBlock(HashSet transparent, int maxDistance); + + /** + * Gets the last two blocks along the living entity's line of sight. + *

          + * The target block will be the last block in the list. + * + * @param transparent HashSet containing all transparent block IDs (set to + * null for only air) + * @param maxDistance this is the maximum distance to scan. This may be + * further limited by the server, but never to less than 100 blocks + * @return list containing the last 2 blocks along the living entity's + * line of sight + * @deprecated Magic value + */ + @Deprecated + public List getLastTwoTargetBlocks(HashSet transparent, int maxDistance); + + /** + * Throws an egg from the living entity. + * + * @deprecated use launchProjectile(Egg.class) instead + * @return the egg thrown + */ + @Deprecated + public Egg throwEgg(); + + /** + * Throws a snowball from the living entity. + * + * @deprecated use launchProjectile(Snowball.class) instead + * @return the snowball thrown + */ + @Deprecated + public Snowball throwSnowball(); + + /** + * Shoots an arrow from the living entity. + * + * @deprecated use launchProjectile(Arrow.class) instead + * @return the arrow shot + */ + @Deprecated + public Arrow shootArrow(); + + /** + * Returns the amount of air that the living entity has remaining, in + * ticks. + * + * @return amount of air remaining + */ + public int getRemainingAir(); + + /** + * Sets the amount of air that the living entity has remaining, in ticks. + * + * @param ticks amount of air remaining + */ + public void setRemainingAir(int ticks); + + /** + * Returns the maximum amount of air the living entity can have, in ticks. + * + * @return maximum amount of air + */ + public int getMaximumAir(); + + /** + * Sets the maximum amount of air the living entity can have, in ticks. + * + * @param ticks maximum amount of air + */ + public void setMaximumAir(int ticks); + + /** + * Returns the living entity's current maximum no damage ticks. + *

          + * This is the maximum duration in which the living entity will not take + * damage. + * + * @return maximum no damage ticks + */ + public int getMaximumNoDamageTicks(); + + /** + * Sets the living entity's current maximum no damage ticks. + * + * @param ticks maximum amount of no damage ticks + */ + public void setMaximumNoDamageTicks(int ticks); + + /** + * Returns the living entity's last damage taken in the current no damage + * ticks time. + *

          + * Only damage higher than this amount will further damage the living + * entity. + * + * @return damage taken since the last no damage ticks time period + */ + public double getLastDamage(); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public int _INVALID_getLastDamage(); + + /** + * Sets the damage dealt within the current no damage ticks time period. + * + * @param damage amount of damage + */ + public void setLastDamage(double damage); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public void _INVALID_setLastDamage(int damage); + + /** + * Returns the living entity's current no damage ticks. + * + * @return amount of no damage ticks + */ + public int getNoDamageTicks(); + + /** + * Sets the living entity's current no damage ticks. + * + * @param ticks amount of no damage ticks + */ + public void setNoDamageTicks(int ticks); + + /** + * Gets the player identified as the killer of the living entity. + *

          + * May be null. + * + * @return killer player, or null if none found + */ + public Player getKiller(); + + /** + * Adds the given {@link PotionEffect} to the living entity. + *

          + * Only one potion effect can be present for a given {@link + * PotionEffectType}. + * + * @param effect PotionEffect to be added + * @return whether the effect could be added + */ + public boolean addPotionEffect(PotionEffect effect); + + /** + * Adds the given {@link PotionEffect} to the living entity. + *

          + * Only one potion effect can be present for a given {@link + * PotionEffectType}. + * + * @param effect PotionEffect to be added + * @param force whether conflicting effects should be removed + * @return whether the effect could be added + */ + public boolean addPotionEffect(PotionEffect effect, boolean force); + + /** + * Attempts to add all of the given {@link PotionEffect} to the living + * entity. + * + * @param effects the effects to add + * @return whether all of the effects could be added + */ + public boolean addPotionEffects(Collection effects); + + /** + * Returns whether the living entity already has an existing effect of + * the given {@link PotionEffectType} applied to it. + * + * @param type the potion type to check + * @return whether the living entity has this potion effect active on them + */ + public boolean hasPotionEffect(PotionEffectType type); + + /** + * Removes any effects present of the given {@link PotionEffectType}. + * + * @param type the potion type to remove + */ + public void removePotionEffect(PotionEffectType type); + + /** + * Returns all currently active {@link PotionEffect}s on the living + * entity. + * + * @return a collection of {@link PotionEffect}s + */ + public Collection getActivePotionEffects(); + + /** + * Checks whether the living entity has block line of sight to another. + *

          + * This uses the same algorithm that hostile mobs use to find the closest + * player. + * + * @param other the entity to determine line of sight to + * @return true if there is a line of sight, false if not + */ + public boolean hasLineOfSight(Entity other); + + /** + * Returns if the living entity despawns when away from players or not. + *

          + * By default, animals are not removed while other mobs are. + * + * @return true if the living entity is removed when away from players + */ + public boolean getRemoveWhenFarAway(); + + /** + * Sets whether or not the living entity despawns when away from players + * or not. + * + * @param remove the removal status + */ + public void setRemoveWhenFarAway(boolean remove); + + /** + * Gets the inventory with the equipment worn by the living entity. + * + * @return the living entity's inventory + */ + public EntityEquipment getEquipment(); + + /** + * Sets whether or not the living entity can pick up items. + * + * @param pickup whether or not the living entity can pick up items + */ + public void setCanPickupItems(boolean pickup); + + /** + * Gets if the living entity can pick up items. + * + * @return whether or not the living entity can pick up items + */ + public boolean getCanPickupItems(); + + /** + * Sets a custom name on a mob. This name will be used in death messages + * and can be sent to the client as a nameplate over the mob. + *

          + * Setting the name to null or an empty string will clear it. + *

          + * This value has no effect on players, they will always use their real + * name. + * + * @param name the name to set + */ + public void setCustomName(String name); + + /** + * Gets the custom name on a mob. If there is no name this method will + * return null. + *

          + * This value has no effect on players, they will always use their real + * name. + * + * @return name of the mob or null + */ + public String getCustomName(); + + /** + * Sets whether or not to display the mob's custom name client side. The + * name will be displayed above the mob similarly to a player. + *

          + * This value has no effect on players, they will always display their + * name. + * + * @param flag custom name or not + */ + public void setCustomNameVisible(boolean flag); + + /** + * Gets whether or not the mob's custom name is displayed client side. + *

          + * This value has no effect on players, they will always display their + * name. + * + * @return if the custom name is displayed + */ + public boolean isCustomNameVisible(); + + /** + * Returns whether the entity is currently leashed. + * + * @return whether the entity is leashed + */ + public boolean isLeashed(); + + /** + * Gets the entity that is currently leading this entity. + * + * @return the entity holding the leash + * @throws IllegalStateException if not currently leashed + */ + public Entity getLeashHolder() throws IllegalStateException; + + /** + * Sets the leash on this entity to be held by the supplied entity. + *

          + * This method has no effect on EnderDragons, Withers, Players, or Bats. + * Non-living entities excluding leashes will not persist as leash + * holders. + * + * @param holder the entity to leash this entity to + * @return whether the operation was successful + */ + public boolean setLeashHolder(Entity holder); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/MagmaCube.java b/vspigot-api/src/main/java/org/bukkit/entity/MagmaCube.java new file mode 100644 index 0000000..714b442 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/MagmaCube.java @@ -0,0 +1,7 @@ +package org.bukkit.entity; + +/** + * Represents a MagmaCube. + */ +public interface MagmaCube extends Slime { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Minecart.java b/vspigot-api/src/main/java/org/bukkit/entity/Minecart.java new file mode 100644 index 0000000..a7bb094 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Minecart.java @@ -0,0 +1,108 @@ +package org.bukkit.entity; + +import org.bukkit.util.Vector; + +/** + * Represents a minecart entity. + */ +public interface Minecart extends Vehicle { + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public void _INVALID_setDamage(int damage); + + /** + * Sets a minecart's damage. + * + * @param damage over 40 to "kill" a minecart + */ + public void setDamage(double damage); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public int _INVALID_getDamage(); + + /** + * Gets a minecart's damage. + * + * @return The damage + */ + public double getDamage(); + + /** + * Gets the maximum speed of a minecart. The speed is unrelated to the + * velocity. + * + * @return The max speed + */ + public double getMaxSpeed(); + + /** + * Sets the maximum speed of a minecart. Must be nonnegative. Default is + * 0.4D. + * + * @param speed The max speed + */ + public void setMaxSpeed(double speed); + + /** + * Returns whether this minecart will slow down faster without a passenger + * occupying it + * + * @return Whether it decelerates faster + */ + public boolean isSlowWhenEmpty(); + + /** + * Sets whether this minecart will slow down faster without a passenger + * occupying it + * + * @param slow Whether it will decelerate faster + */ + public void setSlowWhenEmpty(boolean slow); + + /** + * Gets the flying velocity modifier. Used for minecarts that are in + * mid-air. A flying minecart's velocity is multiplied by this factor each + * tick. + * + * @return The vector factor + */ + public Vector getFlyingVelocityMod(); + + /** + * Sets the flying velocity modifier. Used for minecarts that are in + * mid-air. A flying minecart's velocity is multiplied by this factor each + * tick. + * + * @param flying velocity modifier vector + */ + public void setFlyingVelocityMod(Vector flying); + + /** + * Gets the derailed velocity modifier. Used for minecarts that are on the + * ground, but not on rails. + *

          + * A derailed minecart's velocity is multiplied by this factor each tick. + * + * @return derailed visible speed + */ + public Vector getDerailedVelocityMod(); + + /** + * Sets the derailed velocity modifier. Used for minecarts that are on the + * ground, but not on rails. A derailed minecart's velocity is multiplied + * by this factor each tick. + * + * @param derailed visible speed + */ + public void setDerailedVelocityMod(Vector derailed); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Monster.java b/vspigot-api/src/main/java/org/bukkit/entity/Monster.java new file mode 100644 index 0000000..fce2efd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Monster.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Monster. + */ +public interface Monster extends Creature {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/MushroomCow.java b/vspigot-api/src/main/java/org/bukkit/entity/MushroomCow.java new file mode 100644 index 0000000..84154de --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/MushroomCow.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a mushroom {@link Cow} + */ +public interface MushroomCow extends Cow { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/NPC.java b/vspigot-api/src/main/java/org/bukkit/entity/NPC.java new file mode 100644 index 0000000..0c6b175 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/NPC.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a non-player character + */ +public interface NPC extends Creature { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Ocelot.java b/vspigot-api/src/main/java/org/bukkit/entity/Ocelot.java new file mode 100644 index 0000000..d5d034d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Ocelot.java @@ -0,0 +1,83 @@ + +package org.bukkit.entity; + +/** + * A wild tameable cat + */ +public interface Ocelot extends Animals, Tameable { + + /** + * Gets the current type of this cat. + * + * @return Type of the cat. + */ + public Type getCatType(); + + /** + * Sets the current type of this cat. + * + * @param type New type of this cat. + */ + public void setCatType(Type type); + + /** + * Checks if this ocelot is sitting + * + * @return true if sitting + */ + public boolean isSitting(); + + /** + * Sets if this ocelot is sitting. Will remove any path that the ocelot + * was following beforehand. + * + * @param sitting true if sitting + */ + public void setSitting(boolean sitting); + + /** + * Represents the various different cat types there are. + */ + public enum Type { + WILD_OCELOT(0), + BLACK_CAT(1), + RED_CAT(2), + SIAMESE_CAT(3); + + private static final Type[] types = new Type[Type.values().length]; + private final int id; + + static { + for (Type type : values()) { + types[type.getId()] = type; + } + } + + private Type(int id) { + this.id = id; + } + + /** + * Gets the ID of this cat type. + * + * @return Type ID. + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + + /** + * Gets a cat type by its ID. + * + * @param id ID of the cat type to get. + * @return Resulting type, or null if not found. + * @deprecated Magic value + */ + @Deprecated + public static Type getType(int id) { + return (id >= types.length) ? null : types[id]; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Painting.java b/vspigot-api/src/main/java/org/bukkit/entity/Painting.java new file mode 100644 index 0000000..ca7a4cf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Painting.java @@ -0,0 +1,39 @@ +package org.bukkit.entity; + +import org.bukkit.Art; +import org.bukkit.event.painting.PaintingBreakEvent; + +/** + * Represents a Painting. + */ +public interface Painting extends Hanging { + + /** + * Get the art on this painting + * + * @return The art + */ + public Art getArt(); + + /** + * Set the art on this painting + * + * @param art The new art + * @return False if the new art won't fit at the painting's current + * location + */ + public boolean setArt(Art art); + + /** + * Set the art on this painting + * + * @param art The new art + * @param force If true, force the new art regardless of whether it fits + * at the current location. Note that forcing it where it can't fit + * normally causes it to drop as an item unless you override this by + * catching the {@link PaintingBreakEvent}. + * @return False if force was false and the new art won't fit at the + * painting's current location + */ + public boolean setArt(Art art, boolean force); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Pig.java b/vspigot-api/src/main/java/org/bukkit/entity/Pig.java new file mode 100644 index 0000000..28f59f2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Pig.java @@ -0,0 +1,21 @@ +package org.bukkit.entity; + +/** + * Represents a Pig. + */ +public interface Pig extends Animals, Vehicle { + + /** + * Check if the pig has a saddle. + * + * @return if the pig has been saddled. + */ + public boolean hasSaddle(); + + /** + * Sets if the pig has a saddle or not + * + * @param saddled set if the pig has a saddle or not. + */ + public void setSaddle(boolean saddled); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/PigZombie.java b/vspigot-api/src/main/java/org/bukkit/entity/PigZombie.java new file mode 100644 index 0000000..2f08672 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/PigZombie.java @@ -0,0 +1,36 @@ +package org.bukkit.entity; + +/** + * Represents a Pig Zombie. + */ +public interface PigZombie extends Zombie { + + /** + * Get the pig zombie's current anger level. + * + * @return The anger level. + */ + int getAnger(); + + /** + * Set the pig zombie's current anger level. + * + * @param level The anger level. Higher levels of anger take longer to + * wear off. + */ + void setAnger(int level); + + /** + * Shorthand; sets to either 0 or the default level. + * + * @param angry Whether the zombie should be angry. + */ + void setAngry(boolean angry); + + /** + * Shorthand; gets whether the zombie is angry. + * + * @return True if the zombie is angry, otherwise false. + */ + boolean isAngry(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Player.java b/vspigot-api/src/main/java/org/bukkit/entity/Player.java new file mode 100644 index 0000000..dd18274 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Player.java @@ -0,0 +1,1212 @@ +package org.bukkit.entity; + +import java.net.InetSocketAddress; + +import org.bukkit.Achievement; +import org.bukkit.ChatColor; +import org.bukkit.Effect; +import org.bukkit.Instrument; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Note; +import org.bukkit.OfflinePlayer; +import org.bukkit.Sound; +import org.bukkit.Statistic; +import org.bukkit.WeatherType; +import org.bukkit.command.CommandSender; +import org.bukkit.conversations.Conversable; +import org.bukkit.map.MapView; +import org.bukkit.plugin.messaging.PluginMessageRecipient; +import org.bukkit.scoreboard.Scoreboard; + +/** + * Represents a player, connected or not + */ +public interface Player extends HumanEntity, Conversable, CommandSender, OfflinePlayer, PluginMessageRecipient { + + /** + * Gets the "friendly" name to display of this player. This may include + * color. + *

          + * Note that this name will not be displayed in game, only in chat and + * places defined by plugins. + * + * @return the friendly name + */ + public String getDisplayName(); + + // MineHQ start - Disguises + /** + * Gets the disguised name of this player, or their + * actual name if they're not currently disguised. + */ + public String getDisguisedName(); + + /** + * Is this {@link Player} currently disguised? + */ + public boolean isDisguised(); + + /** + * Disguise this {@link Player} as someone else. + * This will disguise their tab-list name and tab-completion + * should take this into account. + * + * {@link #getDisplayName()} will return the disguised name, as + * well as {@link #getDisguisedName()}. {@link #getName()} should + * only be used in situations where we absolutely need the player's + * real name, and should never be outputted to other players. + * + * @param name The name of the player we want to disguise as + * @param texture We can add custom skins by doing this. + * @param signature We can add custom skins by doing this. + */ + public void disguise(String name, String texture, String signature); + + /** + * @see #disguise(String, String, String) + * This method does not allow for a custom skin. + */ + public void disguise(String name); + + /** + * Un-disguise this player. + */ + public void undisguise(); + // MineHQ end + + /** + * Sets the "friendly" name to display of this player. This may include + * color. + *

          + * Note that this name will not be displayed in game, only in chat and + * places defined by plugins. + * + * @param name The new display name. + */ + public void setDisplayName(String name); + + /** + * Gets the name that is shown on the player list. + * + * @return the player list name + */ + public String getPlayerListName(); + + /** + * Sets the name that is shown on the in-game player list. + *

          + * The name cannot be longer than 16 characters, but {@link ChatColor} is + * supported. + *

          + * If the value is null, the name will be identical to {@link #getName()}. + *

          + * This name is case sensitive and unique, two names with different casing + * will appear as two different people. If a player joins afterwards with + * a name that conflicts with a player's custom list name, the joining + * player's player list name will have a random number appended to it (1-2 + * characters long in the default implementation). If the joining player's + * name is 15 or 16 characters long, part of the name will be truncated at + * the end to allow the addition of the two digits. + * + * @param name new player list name + * @throws IllegalArgumentException if the name is already used by someone + * else + * @throws IllegalArgumentException if the length of the name is too long + */ + public void setPlayerListName(String name); + + /** + * Set the target of the player's compass. + * + * @param loc Location to point to + */ + public void setCompassTarget(Location loc); + + /** + * Get the previously set compass target. + * + * @return location of the target + */ + public Location getCompassTarget(); + + /** + * Gets the socket address of this player + * + * @return the player's address + */ + public InetSocketAddress getAddress(); + + /** + * Sends this sender a message raw + * + * @param message Message to be displayed + */ + public void sendRawMessage(String message); + + /** + * Kicks player with custom kick message. + * + * @param message kick message + */ + public void kickPlayer(String message); + + /** + * Says a message (or runs a command). + * + * @param msg message to print + */ + public void chat(String msg); + + /** + * Makes the player perform the given command + * + * @param command Command to perform + * @return true if the command was successful, otherwise false + */ + public boolean performCommand(String command); + + /** + * Returns if the player is in sneak mode + * + * @return true if player is in sneak mode + */ + public boolean isSneaking(); + + /** + * Sets the sneak mode the player + * + * @param sneak true if player should appear sneaking + */ + public void setSneaking(boolean sneak); + + /** + * Gets whether the player is sprinting or not. + * + * @return true if player is sprinting. + */ + public boolean isSprinting(); + + /** + * Sets whether the player is sprinting or not. + * + * @param sprinting true if the player should be sprinting + */ + public void setSprinting(boolean sprinting); + + /** + * Saves the players current location, health, inventory, motion, and + * other information into the username.dat file, in the world/player + * folder + */ + public void saveData(); + + /** + * Loads the players current location, health, inventory, motion, and + * other information from the username.dat file, in the world/player + * folder. + *

          + * Note: This will overwrite the players current inventory, health, + * motion, etc, with the state from the saved dat file. + */ + public void loadData(); + + /** + * Sets whether the player is ignored as not sleeping. If everyone is + * either sleeping or has this flag set, then time will advance to the + * next day. If everyone has this flag set but no one is actually in bed, + * then nothing will happen. + * + * @param isSleeping Whether to ignore. + */ + public void setSleepingIgnored(boolean isSleeping); + + /** + * Returns whether the player is sleeping ignored. + * + * @return Whether player is ignoring sleep. + */ + public boolean isSleepingIgnored(); + + /** + * Play a note for a player at a location. This requires a note block + * at the particular location (as far as the client is concerned). This + * will not work without a note block. This will not work with cake. + * + * @param loc The location of a note block. + * @param instrument The instrument ID. + * @param note The note ID. + * @deprecated Magic value + */ + @Deprecated + public void playNote(Location loc, byte instrument, byte note); + + /** + * Play a note for a player at a location. This requires a note block + * at the particular location (as far as the client is concerned). This + * will not work without a note block. This will not work with cake. + * + * @param loc The location of a note block + * @param instrument The instrument + * @param note The note + */ + public void playNote(Location loc, Instrument instrument, Note note); + + + /** + * Play a sound for a player at the location. + *

          + * This function will fail silently if Location or Sound are null. + * + * @param location The location to play the sound + * @param sound The sound to play + * @param volume The volume of the sound + * @param pitch The pitch of the sound + */ + public void playSound(Location location, Sound sound, float volume, float pitch); + + /** + * Play a sound for a player at the location. + *

          + * This function will fail silently if Location or Sound are null. No + * sound will be heard by the player if their client does not have the + * respective sound for the value passed. + * + * @param location the location to play the sound + * @param sound the internal sound name to play + * @param volume the volume of the sound + * @param pitch the pitch of the sound + * @deprecated Magic value + */ + @Deprecated + public void playSound(Location location, String sound, float volume, float pitch); + + /** + * Plays an effect to just this player. + * + * @param loc the location to play the effect at + * @param effect the {@link Effect} + * @param data a data bit needed for some effects + * @deprecated Magic value + */ + @Deprecated + public void playEffect(Location loc, Effect effect, int data); + + /** + * Plays an effect to just this player. + * + * @param loc the location to play the effect at + * @param effect the {@link Effect} + * @param data a data bit needed for some effects + */ + public void playEffect(Location loc, Effect effect, T data); + + /** + * Send a block change. This fakes a block change packet for a user at a + * certain location. This will not actually change the world in any way. + * + * @param loc The location of the changed block + * @param material The new block + * @param data The block data + * @deprecated Magic value + */ + @Deprecated + public void sendBlockChange(Location loc, Material material, byte data); + + /** + * Send a chunk change. This fakes a chunk change packet for a user at a + * certain location. The updated cuboid must be entirely within a single + * chunk. This will not actually change the world in any way. + *

          + * At least one of the dimensions of the cuboid must be even. The size of + * the data buffer must be 2.5*sx*sy*sz and formatted in accordance with + * the Packet51 format. + * + * @param loc The location of the cuboid + * @param sx The x size of the cuboid + * @param sy The y size of the cuboid + * @param sz The z size of the cuboid + * @param data The data to be sent + * @return true if the chunk change packet was sent + * @deprecated Magic value + */ + @Deprecated + public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data); + + /** + * Send a block change. This fakes a block change packet for a user at a + * certain location. This will not actually change the world in any way. + * + * @param loc The location of the changed block + * @param material The new block ID + * @param data The block data + * @deprecated Magic value + */ + @Deprecated + public void sendBlockChange(Location loc, int material, byte data); + + /** + * Send a sign change. This fakes a sign change packet for a user at + * a certain location. This will not actually change the world in any way. + * This method will use a sign at the location's block or a faked sign + * sent via {@link #sendBlockChange(org.bukkit.Location, int, byte)} or + * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}. + *

          + * If the client does not have a sign at the given location it will + * display an error message to the user. + * + * @param loc the location of the sign + * @param lines the new text on the sign or null to clear it + * @throws IllegalArgumentException if location is null + * @throws IllegalArgumentException if lines is non-null and has a length less than 4 + */ + public void sendSignChange(Location loc, String[] lines) throws IllegalArgumentException; + + /** + * Render a map and send it to the player in its entirety. This may be + * used when streaming the map in the normal manner is not desirable. + * + * @param map The map to be sent + */ + public void sendMap(MapView map); + + /** + * Forces an update of the player's entire inventory. + * + */ + //@Deprecated // Spigot - undeprecate + public void updateInventory(); + + /** + * Awards the given achievement and any parent achievements that the + * player does not have. + * + * @param achievement Achievement to award + * @throws IllegalArgumentException if achievement is null + */ + public void awardAchievement(Achievement achievement); + + /** + * Removes the given achievement and any children achievements that the + * player has. + * + * @param achievement Achievement to remove + * @throws IllegalArgumentException if achievement is null + */ + public void removeAchievement(Achievement achievement); + + /** + * Gets whether this player has the given achievement. + * + * @return whether the player has the achievement + * @throws IllegalArgumentException if achievement is null + */ + public boolean hasAchievement(Achievement achievement); + + /** + * Increments the given statistic for this player. + *

          + * This is equivalent to the following code: + * incrementStatistic(Statistic, 1) + * + * @param statistic Statistic to increment + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if the statistic requires an + * additional parameter + */ + public void incrementStatistic(Statistic statistic) throws IllegalArgumentException; + + /** + * Decrements the given statistic for this player. + *

          + * This is equivalent to the following code: + * decrementStatistic(Statistic, 1) + * + * @param statistic Statistic to decrement + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if the statistic requires an + * additional parameter + */ + public void decrementStatistic(Statistic statistic) throws IllegalArgumentException; + + /** + * Increments the given statistic for this player. + * + * @param statistic Statistic to increment + * @param amount Amount to increment this statistic by + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if the statistic requires an + * additional parameter + */ + public void incrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException; + + /** + * Decrements the given statistic for this player. + * + * @param statistic Statistic to decrement + * @param amount Amount to decrement this statistic by + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if the statistic requires an + * additional parameter + */ + public void decrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException; + + /** + * Sets the given statistic for this player. + * + * @param statistic Statistic to set + * @param newValue The value to set this statistic to + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if newValue is negative + * @throws IllegalArgumentException if the statistic requires an + * additional parameter + */ + public void setStatistic(Statistic statistic, int newValue) throws IllegalArgumentException; + + /** + * Gets the value of the given statistic for this player. + * + * @param statistic Statistic to check + * @return the value of the given statistic + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if the statistic requires an + * additional parameter + */ + public int getStatistic(Statistic statistic) throws IllegalArgumentException; + + /** + * Increments the given statistic for this player for the given material. + *

          + * This is equivalent to the following code: + * incrementStatistic(Statistic, Material, 1) + * + * @param statistic Statistic to increment + * @param material Material to offset the statistic with + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if material is null + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void incrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException; + + /** + * Decrements the given statistic for this player for the given material. + *

          + * This is equivalent to the following code: + * decrementStatistic(Statistic, Material, 1) + * + * @param statistic Statistic to decrement + * @param material Material to offset the statistic with + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if material is null + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void decrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException; + + /** + * Gets the value of the given statistic for this player. + * + * @param statistic Statistic to check + * @param material Material offset of the statistic + * @return the value of the given statistic + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if material is null + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public int getStatistic(Statistic statistic, Material material) throws IllegalArgumentException; + + /** + * Increments the given statistic for this player for the given material. + * + * @param statistic Statistic to increment + * @param material Material to offset the statistic with + * @param amount Amount to increment this statistic by + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if material is null + * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void incrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException; + + /** + * Decrements the given statistic for this player for the given material. + * + * @param statistic Statistic to decrement + * @param material Material to offset the statistic with + * @param amount Amount to decrement this statistic by + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if material is null + * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void decrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException; + + /** + * Sets the given statistic for this player for the given material. + * + * @param statistic Statistic to set + * @param material Material to offset the statistic with + * @param newValue The value to set this statistic to + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if material is null + * @throws IllegalArgumentException if newValue is negative + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void setStatistic(Statistic statistic, Material material, int newValue) throws IllegalArgumentException; + + /** + * Increments the given statistic for this player for the given entity. + *

          + * This is equivalent to the following code: + * incrementStatistic(Statistic, EntityType, 1) + * + * @param statistic Statistic to increment + * @param entityType EntityType to offset the statistic with + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if entityType is null + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void incrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException; + + /** + * Decrements the given statistic for this player for the given entity. + *

          + * This is equivalent to the following code: + * decrementStatistic(Statistic, EntityType, 1) + * + * @param statistic Statistic to decrement + * @param entityType EntityType to offset the statistic with + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if entityType is null + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void decrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException; + + /** + * Gets the value of the given statistic for this player. + * + * @param statistic Statistic to check + * @param entityType EntityType offset of the statistic + * @return the value of the given statistic + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if entityType is null + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public int getStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException; + + /** + * Increments the given statistic for this player for the given entity. + * + * @param statistic Statistic to increment + * @param entityType EntityType to offset the statistic with + * @param amount Amount to increment this statistic by + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if entityType is null + * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) throws IllegalArgumentException; + + /** + * Decrements the given statistic for this player for the given entity. + * + * @param statistic Statistic to decrement + * @param entityType EntityType to offset the statistic with + * @param amount Amount to decrement this statistic by + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if entityType is null + * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void decrementStatistic(Statistic statistic, EntityType entityType, int amount); + + /** + * Sets the given statistic for this player for the given entity. + * + * @param statistic Statistic to set + * @param entityType EntityType to offset the statistic with + * @param newValue The value to set this statistic to + * @throws IllegalArgumentException if statistic is null + * @throws IllegalArgumentException if entityType is null + * @throws IllegalArgumentException if newValue is negative + * @throws IllegalArgumentException if the given parameter is not valid + * for the statistic + */ + public void setStatistic(Statistic statistic, EntityType entityType, int newValue); + + /** + * Sets the current time on the player's client. When relative is true the + * player's time will be kept synchronized to its world time with the + * specified offset. + *

          + * When using non relative time the player's time will stay fixed at the + * specified time parameter. It's up to the caller to continue updating + * the player's time. To restore player time to normal use + * resetPlayerTime(). + * + * @param time The current player's perceived time or the player's time + * offset from the server time. + * @param relative When true the player time is kept relative to its world + * time. + */ + public void setPlayerTime(long time, boolean relative); + + /** + * Returns the player's current timestamp. + * + * @return The player's time + */ + public long getPlayerTime(); + + /** + * Returns the player's current time offset relative to server time, or + * the current player's fixed time if the player's time is absolute. + * + * @return The player's time + */ + public long getPlayerTimeOffset(); + + /** + * Returns true if the player's time is relative to the server time, + * otherwise the player's time is absolute and will not change its current + * time unless done so with setPlayerTime(). + * + * @return true if the player's time is relative to the server time. + */ + public boolean isPlayerTimeRelative(); + + /** + * Restores the normal condition where the player's time is synchronized + * with the server time. + *

          + * Equivalent to calling setPlayerTime(0, true). + */ + public void resetPlayerTime(); + + /** + * Sets the type of weather the player will see. When used, the weather + * status of the player is locked until {@link #resetPlayerWeather()} is + * used. + * + * @param type The WeatherType enum type the player should experience + */ + public void setPlayerWeather(WeatherType type); + + /** + * Returns the type of weather the player is currently experiencing. + * + * @return The WeatherType that the player is currently experiencing or + * null if player is seeing server weather. + */ + public WeatherType getPlayerWeather(); + + /** + * Restores the normal condition where the player's weather is controlled + * by server conditions. + */ + public void resetPlayerWeather(); + + /** + * Gives the player the amount of experience specified. + * + * @param amount Exp amount to give + */ + public void giveExp(int amount); + + /** + * Gives the player the amount of experience levels specified. Levels can + * be taken by specifying a negative amount. + * + * @param amount amount of experience levels to give or take + */ + public void giveExpLevels(int amount); + + /** + * Gets the players current experience points towards the next level. + *

          + * This is a percentage value. 0 is "no progress" and 1 is "next level". + * + * @return Current experience points + */ + public float getExp(); + + /** + * Sets the players current experience points towards the next level + *

          + * This is a percentage value. 0 is "no progress" and 1 is "next level". + * + * @param exp New experience points + */ + public void setExp(float exp); + + /** + * Gets the players current experience level + * + * @return Current experience level + */ + public int getLevel(); + + /** + * Sets the players current experience level + * + * @param level New experience level + */ + public void setLevel(int level); + + /** + * Gets the players total experience points + * + * @return Current total experience points + */ + public int getTotalExperience(); + + /** + * Sets the players current experience level + * + * @param exp New experience level + */ + public void setTotalExperience(int exp); + + /** + * Gets the players current exhaustion level. + *

          + * Exhaustion controls how fast the food level drops. While you have a + * certain amount of exhaustion, your saturation will drop to zero, and + * then your food will drop to zero. + * + * @return Exhaustion level + */ + public float getExhaustion(); + + /** + * Sets the players current exhaustion level + * + * @param value Exhaustion level + */ + public void setExhaustion(float value); + + /** + * Gets the players current saturation level. + *

          + * Saturation is a buffer for food level. Your food level will not drop if + * you are saturated > 0. + * + * @return Saturation level + */ + public float getSaturation(); + + /** + * Sets the players current saturation level + * + * @param value Saturation level + */ + public void setSaturation(float value); + + /** + * Gets the players current food level + * + * @return Food level + */ + public int getFoodLevel(); + + /** + * Sets the players current food level + * + * @param value New food level + */ + public void setFoodLevel(int value); + + /** + * 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(); + + /** + * Sets the Location where the player will spawn at their bed. + * + * @param location where to set the respawn location + */ + public void setBedSpawnLocation(Location location); + + /** + * Sets the Location where the player will spawn at their bed. + * + * @param location where to set the respawn location + * @param force whether to forcefully set the respawn location even if a + * valid bed is not present + */ + public void setBedSpawnLocation(Location location, boolean force); + + /** + * Determines if the Player is allowed to fly via jump key double-tap like + * in creative mode. + * + * @return True if the player is allowed to fly. + */ + public boolean getAllowFlight(); + + /** + * Sets if the Player is allowed to fly via jump key double-tap like in + * creative mode. + * + * @param flight If flight should be allowed. + */ + public void setAllowFlight(boolean flight); + + /** + * Hides a player from this player + * + * @param player Player to hide + */ + public void hidePlayer(Player player); + + /** + * Allows this player to see a player that was previously hidden + * + * @param player Player to show + */ + public void showPlayer(Player player); + + /** + * Checks to see if a player has been hidden from this player + * + * @param player Player to check + * @return True if the provided player is not being hidden from this + * player + */ + public boolean canSee(Player player); + + /** + * Checks to see if this player is currently standing on a block. This + * information may not be reliable, as it is a state provided by the + * client, and may therefore not be accurate. + * + * @return True if the player standing on a solid block, else false. + * @deprecated Inconsistent with {@link + * org.bukkit.entity.Entity#isOnGround()} + */ + @Deprecated + public boolean isOnGround(); + + /** + * Checks to see if this player is currently flying or not. + * + * @return True if the player is flying, else false. + */ + public boolean isFlying(); + + /** + * Makes this player start or stop flying. + * + * @param value True to fly. + */ + public void setFlying(boolean value); + + /** + * Sets the speed at which a client will fly. Negative values indicate + * reverse directions. + * + * @param value The new speed, from -1 to 1. + * @throws IllegalArgumentException If new speed is less than -1 or + * greater than 1 + */ + public void setFlySpeed(float value) throws IllegalArgumentException; + + /** + * Sets the speed at which a client will walk. Negative values indicate + * reverse directions. + * + * @param value The new speed, from -1 to 1. + * @throws IllegalArgumentException If new speed is less than -1 or + * greater than 1 + */ + public void setWalkSpeed(float value) throws IllegalArgumentException; + + /** + * Gets the current allowed speed that a client can fly. + * + * @return The current allowed speed, from -1 to 1 + */ + public float getFlySpeed(); + + /** + * Gets the current allowed speed that a client can walk. + * + * @return The current allowed speed, from -1 to 1 + */ + public float getWalkSpeed(); + + /** + * Request that the player's client download and switch texture packs. + *

          + * The player's client will download the new texture pack asynchronously + * in the background, and will automatically switch to it once the + * download is complete. If the client has downloaded and cached the same + * texture pack in the past, it will perform a quick timestamp check over + * the network to determine if the texture pack has changed and needs to + * be downloaded again. When this request is sent for the very first time + * from a given server, the client will first display a confirmation GUI + * to the player before proceeding with the download. + *

          + * Notes: + *

            + *
          • Players can disable server textures on their client, in which + * case this method will have no affect on them. + *
          • There is no concept of resetting texture packs back to default + * within Minecraft, so players will have to relog to do so. + *
          + * + * @param url The URL from which the client will download the texture + * pack. The string must contain only US-ASCII characters and should + * be encoded as per RFC 1738. + * @throws IllegalArgumentException Thrown if the URL is null. + * @throws IllegalArgumentException Thrown if the URL is too long. + * @deprecated Minecraft no longer uses textures packs. Instead you + * should use {@link #setResourcePack(String)}. + */ + @Deprecated + public void setTexturePack(String url); + + /** + * Request that the player's client download and switch resource packs. + *

          + * The player's client will download the new resource pack asynchronously + * in the background, and will automatically switch to it once the + * download is complete. If the client has downloaded and cached the same + * resource pack in the past, it will perform a quick timestamp check + * over the network to determine if the resource pack has changed and + * needs to be downloaded again. When this request is sent for the very + * first time from a given server, the client will first display a + * confirmation GUI to the player before proceeding with the download. + *

          + * Notes: + *

            + *
          • Players can disable server resources on their client, in which + * case this method will have no affect on them. + *
          • There is no concept of resetting resource packs back to default + * within Minecraft, so players will have to relog to do so. + *
          + * + * @param url The URL from which the client will download the resource + * pack. The string must contain only US-ASCII characters and should + * be encoded as per RFC 1738. + * @throws IllegalArgumentException Thrown if the URL is null. + * @throws IllegalArgumentException Thrown if the URL is too long. The + * length restriction is an implementation specific arbitrary value. + */ + public void setResourcePack(String url); + + /** + * Gets the Scoreboard displayed to this player + * + * @return The current scoreboard seen by this player + */ + public Scoreboard getScoreboard(); + + /** + * Sets the player's visible Scoreboard. + * + * @param scoreboard New Scoreboard for the player + * @throws IllegalArgumentException if scoreboard is null + * @throws IllegalArgumentException if scoreboard was not created by the + * {@link org.bukkit.scoreboard.ScoreboardManager scoreboard manager} + * @throws IllegalStateException if this is a player that is not logged + * yet or has logged out + */ + public void setScoreboard(Scoreboard scoreboard) throws IllegalArgumentException, IllegalStateException; + + /** + * Gets if the client is displayed a 'scaled' health, that is, health on a + * scale from 0-{@link #getHealthScale()}. + * + * @return if client health display is scaled + * @see Player#setHealthScaled(boolean) + */ + public boolean isHealthScaled(); + + /** + * Sets if the client is displayed a 'scaled' health, that is, health on a + * scale from 0-{@link #getHealthScale()}. + *

          + * Displayed health follows a simple formula displayedHealth = + * getHealth() / getMaxHealth() * getHealthScale(). + * + * @param scale if the client health display is scaled + */ + public void setHealthScaled(boolean scale); + + /** + * Sets the number to scale health to for the client; this will also + * {@link #setHealthScaled(boolean) setHealthScaled(true)}. + *

          + * Displayed health follows a simple formula displayedHealth = + * getHealth() / getMaxHealth() * getHealthScale(). + * + * @param scale the number to scale health to + * @throws IllegalArgumentException if scale is <0 + * @throws IllegalArgumentException if scale is {@link Double#NaN} + * @throws IllegalArgumentException if scale is too high + */ + public void setHealthScale(double scale) throws IllegalArgumentException; + + /** + * Gets the number that health is scaled to for the client. + * + * @return the number that health would be scaled to for the client if + * HealthScaling is set to true + * @see Player#setHealthScale(double) + * @see Player#setHealthScaled(boolean) + */ + public double getHealthScale(); + + // Spigot start + public class Spigot extends Entity.Spigot + { + + /** + * Gets the connection address of this player, regardless of whether it + * has been spoofed or not. + * + * @return the player's connection address + */ + public InetSocketAddress getRawAddress() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Gets whether the player collides with entities + * + * @return the player's collision toggle state + */ + public boolean getCollidesWithEntities() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Sets whether the player collides with entities + * + * @param collides whether the player should collide with entities or + * not. + */ + public void setCollidesWithEntities(boolean collides) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Respawns the player if dead. + */ + public void respawn() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Gets player locale language. + * + * @return the player's client language settings + */ + public String getLocale() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Gets all players hidden with {@link hidePlayer(org.bukkit.entity.Player)}. + * + * @return a Set with all hidden players + */ + public java.util.Set getHiddenPlayers() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Sends the component to the player + * + * @param component the components to send + */ + public void sendMessage(net.md_5.bungee.api.chat.BaseComponent component) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Sends an array of components as a single message to the + * player + * + * @param components the components to send + */ + public void sendMessage(net.md_5.bungee.api.chat.BaseComponent ...components) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Get whether the player affects mob spawning + * + * @return whether or not the player affects + * mob spawning. + */ + public boolean getAffectsSpawning() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Set whether or not the player affects mob spawning + * + * @param affects whether or not the player should affect + * spawning or not. + */ + public void setAffectsSpawning(boolean affects) + { + throw new UnsupportedOperationException( "Not supported yet" ); + } + + /** + * Get the view distance for this player + * + * @return View distance + */ + public int getViewDistance() + { + throw new UnsupportedOperationException( "Not supported yet" ); + } + + /** + * Set the view distance for this player + * + * @param viewDistance View distance + */ + public void setViewDistance(int viewDistance) + { + throw new UnsupportedOperationException( "Not supported yet" ); + } + } + + Spigot spigot(); + // Spigot end +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/PoweredMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/PoweredMinecart.java new file mode 100644 index 0000000..38240a9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/PoweredMinecart.java @@ -0,0 +1,9 @@ +package org.bukkit.entity; + +/** + * @deprecated This class has been moved into a sub package; {@link + * org.bukkit.entity.minecart.PoweredMinecart} should be used instead. + * @see org.bukkit.entity.minecart.PoweredMinecart + */ +@Deprecated +public interface PoweredMinecart extends org.bukkit.entity.minecart.PoweredMinecart {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Projectile.java b/vspigot-api/src/main/java/org/bukkit/entity/Projectile.java new file mode 100644 index 0000000..90ce3b3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Projectile.java @@ -0,0 +1,56 @@ +package org.bukkit.entity; + +import org.bukkit.projectiles.ProjectileSource; + +/** + * Represents a shootable entity. + */ +public interface Projectile extends Entity { + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public LivingEntity _INVALID_getShooter(); + + /** + * Retrieve the shooter of this projectile. + * + * @return the {@link ProjectileSource} that shot this projectile + */ + public ProjectileSource getShooter(); + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public void _INVALID_setShooter(LivingEntity shooter); + + /** + * Set the shooter of this projectile. + * + * @param source the {@link ProjectileSource} that shot this projectile + */ + public void setShooter(ProjectileSource source); + + /** + * Determine if this projectile should bounce or not when it hits. + *

          + * If a small fireball does not bounce it will set the target on fire. + * + * @return true if it should bounce. + */ + public boolean doesBounce(); + + /** + * Set whether or not this projectile should bounce or not when it hits + * something. + * + * @param doesBounce whether or not it should bounce. + */ + public void setBounce(boolean doesBounce); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Sheep.java b/vspigot-api/src/main/java/org/bukkit/entity/Sheep.java new file mode 100644 index 0000000..f4ce312 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Sheep.java @@ -0,0 +1,19 @@ +package org.bukkit.entity; + +import org.bukkit.material.Colorable; + +/** + * Represents a Sheep. + */ +public interface Sheep extends Animals, Colorable { + + /** + * @return Whether the sheep is sheared. + */ + public boolean isSheared(); + + /** + * @param flag Whether to shear the sheep + */ + public void setSheared(boolean flag); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Silverfish.java b/vspigot-api/src/main/java/org/bukkit/entity/Silverfish.java new file mode 100644 index 0000000..fe01007 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Silverfish.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Silverfish. + */ +public interface Silverfish extends Monster {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Skeleton.java b/vspigot-api/src/main/java/org/bukkit/entity/Skeleton.java new file mode 100644 index 0000000..02b76c3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Skeleton.java @@ -0,0 +1,65 @@ +package org.bukkit.entity; + +/** + * Represents a Skeleton. + */ +public interface Skeleton extends Monster { + + /** + * Gets the current type of this skeleton. + * + * @return Current type + */ + public SkeletonType getSkeletonType(); + + /** + * Sets the new type of this skeleton. + * + * @param type New type + */ + public void setSkeletonType(SkeletonType type); + + /* + * Represents the various different Skeleton types. + */ + public enum SkeletonType { + NORMAL(0), + WITHER(1); + + private static final SkeletonType[] types = new SkeletonType[SkeletonType.values().length]; + private final int id; + + static { + for (SkeletonType type : values()) { + types[type.getId()] = type; + } + } + + private SkeletonType(int id) { + this.id = id; + } + + /** + * Gets the ID of this skeleton type. + * + * @return Skeleton type ID + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + + /** + * Gets a skeleton type by its ID. + * + * @param id ID of the skeleton type to get. + * @return Resulting skeleton type, or null if not found. + * @deprecated Magic value + */ + @Deprecated + public static SkeletonType getType(int id) { + return (id >= types.length) ? null : types[id]; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Slime.java b/vspigot-api/src/main/java/org/bukkit/entity/Slime.java new file mode 100644 index 0000000..cbf50c8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Slime.java @@ -0,0 +1,17 @@ +package org.bukkit.entity; + +/** + * Represents a Slime. + */ +public interface Slime extends LivingEntity { + + /** + * @return The size of the slime + */ + public int getSize(); + + /** + * @param sz The new size of the slime. + */ + public void setSize(int sz); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/SmallFireball.java b/vspigot-api/src/main/java/org/bukkit/entity/SmallFireball.java new file mode 100644 index 0000000..33f54d3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/SmallFireball.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a small {@link Fireball} + */ +public interface SmallFireball extends Fireball { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Snowball.java b/vspigot-api/src/main/java/org/bukkit/entity/Snowball.java new file mode 100644 index 0000000..8c6b433 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Snowball.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a snowball. + */ +public interface Snowball extends Projectile {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Snowman.java b/vspigot-api/src/main/java/org/bukkit/entity/Snowman.java new file mode 100644 index 0000000..c8070ff --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Snowman.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a snowman entity + */ +public interface Snowman extends Golem { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Spider.java b/vspigot-api/src/main/java/org/bukkit/entity/Spider.java new file mode 100644 index 0000000..f9ee8cc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Spider.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Spider. + */ +public interface Spider extends Monster {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Squid.java b/vspigot-api/src/main/java/org/bukkit/entity/Squid.java new file mode 100644 index 0000000..fb47968 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Squid.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Squid. + */ +public interface Squid extends WaterMob {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/StorageMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/StorageMinecart.java new file mode 100644 index 0000000..5436d70 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/StorageMinecart.java @@ -0,0 +1,9 @@ +package org.bukkit.entity; + +/** + * @deprecated This class has been moved into a sub package; {@link + * org.bukkit.entity.minecart.StorageMinecart} should be used instead. + * @see org.bukkit.entity.minecart.StorageMinecart + */ +@Deprecated +public interface StorageMinecart extends org.bukkit.entity.minecart.StorageMinecart {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/TNTPrimed.java b/vspigot-api/src/main/java/org/bukkit/entity/TNTPrimed.java new file mode 100644 index 0000000..cc3ddca --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/TNTPrimed.java @@ -0,0 +1,40 @@ +package org.bukkit.entity; + +/** + * Represents a Primed TNT. + */ +public interface TNTPrimed extends Explosive { + + /** + * Set the number of ticks until the TNT blows up after being primed. + * + * @param fuseTicks The fuse ticks + */ + public void setFuseTicks(int fuseTicks); + + /** + * Retrieve the number of ticks until the explosion of this TNTPrimed + * entity + * + * @return the number of ticks until this TNTPrimed explodes + */ + public int getFuseTicks(); + + /** + * Gets the source of this primed TNT. The source is the entity + * responsible for the creation of this primed TNT. (I.E. player ignites + * TNT with flint and steel.) Take note that this can be null if there is + * no suitable source. (created by the {@link + * org.bukkit.World#spawn(Location, Class)} method, for example.) + *

          + * The source will become null if the chunk this primed TNT is in is + * unloaded then reloaded. If the source Entity becomes invalidated for + * any reason, such being removed from the world, the returned value will + * be null. + * + * @return the source of this primed TNT + */ + public Entity getSource(); + + org.bukkit.Location getSourceLoc(); // PaperSpigot - Add FallingBlock and TNT source location API +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Tameable.java b/vspigot-api/src/main/java/org/bukkit/entity/Tameable.java new file mode 100644 index 0000000..014885d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Tameable.java @@ -0,0 +1,44 @@ +package org.bukkit.entity; + +public interface Tameable { + + /** + * Check if this is tamed + *

          + * If something is tamed then a player can not tame it through normal + * methods, even if it does not belong to anyone in particular. + * + * @return true if this has been tamed + */ + public boolean isTamed(); + + /** + * Sets if this has been tamed. Not necessary if the method setOwner has + * been used, as it tames automatically. + *

          + * If something is tamed then a player can not tame it through normal + * methods, even if it does not belong to anyone in particular. + * + * @param tame true if tame + */ + public void setTamed(boolean tame); + + /** + * Gets the current owning AnimalTamer + * + * @return the owning AnimalTamer, or null if not owned + */ + public AnimalTamer getOwner(); + + /** + * Set this to be owned by given AnimalTamer. + *

          + * If the owner is not null, this will be tamed and will have any current + * path it is following removed. If the owner is set to null, this will be + * untamed, and the current owner removed. + * + * @param tamer the AnimalTamer who should own this + */ + public void setOwner(AnimalTamer tamer); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/ThrownExpBottle.java b/vspigot-api/src/main/java/org/bukkit/entity/ThrownExpBottle.java new file mode 100644 index 0000000..671282e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/ThrownExpBottle.java @@ -0,0 +1,8 @@ +package org.bukkit.entity; + +/** + * Represents a thrown Experience bottle. + */ +public interface ThrownExpBottle extends Projectile { + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/ThrownPotion.java b/vspigot-api/src/main/java/org/bukkit/entity/ThrownPotion.java new file mode 100644 index 0000000..8b382db --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/ThrownPotion.java @@ -0,0 +1,39 @@ +package org.bukkit.entity; + +import java.util.Collection; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; + +/** + * Represents a thrown potion bottle + */ +public interface ThrownPotion extends Projectile { + + /** + * Returns the effects that are applied by this potion. + * + * @return The potion effects + */ + public Collection getEffects(); + + /** + * Returns a copy of the ItemStack for this thrown potion. + *

          + * Altering this copy will not alter the thrown potion directly. If you + * want to alter the thrown potion, you must use the {@link + * #setItem(ItemStack) setItemStack} method. + * + * @return A copy of the ItemStack for this thrown potion. + */ + public ItemStack getItem(); + + /** + * Set the ItemStack for this thrown potion. + *

          + * The ItemStack must be a potion, otherwise an exception is thrown. + * + * @param item New ItemStack + */ + public void setItem(ItemStack item); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Vehicle.java b/vspigot-api/src/main/java/org/bukkit/entity/Vehicle.java new file mode 100644 index 0000000..7d7607c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Vehicle.java @@ -0,0 +1,23 @@ +package org.bukkit.entity; + +import org.bukkit.util.Vector; + +/** + * Represents a vehicle entity. + */ +public interface Vehicle extends Entity { + + /** + * Gets the vehicle's velocity. + * + * @return velocity vector + */ + public Vector getVelocity(); + + /** + * Sets the vehicle's velocity. + * + * @param vel velocity vector + */ + public void setVelocity(Vector vel); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Villager.java b/vspigot-api/src/main/java/org/bukkit/entity/Villager.java new file mode 100644 index 0000000..51035c9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Villager.java @@ -0,0 +1,69 @@ +package org.bukkit.entity; + +/** + * Represents a villager NPC + */ +public interface Villager extends Ageable, NPC { + + /** + * Gets the current profession of this villager. + * + * @return Current profession. + */ + public Profession getProfession(); + + /** + * Sets the new profession of this villager. + * + * @param profession New profession. + */ + public void setProfession(Profession profession); + + + /** + * Represents the various different Villager professions there may be. + */ + public enum Profession { + FARMER(0), + LIBRARIAN(1), + PRIEST(2), + BLACKSMITH(3), + BUTCHER(4); + + private static final Profession[] professions = new Profession[Profession.values().length]; + private final int id; + + static { + for (Profession type : values()) { + professions[type.getId()] = type; + } + } + + private Profession(int id) { + this.id = id; + } + + /** + * Gets the ID of this profession. + * + * @return Profession ID. + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + + /** + * Gets a profession by its ID. + * + * @param id ID of the profession to get. + * @return Resulting profession, or null if not found. + * @deprecated Magic value + */ + @Deprecated + public static Profession getProfession(int id) { + return (id >= professions.length) ? null : professions[id]; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/WaterMob.java b/vspigot-api/src/main/java/org/bukkit/entity/WaterMob.java new file mode 100644 index 0000000..62b4e89 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/WaterMob.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Water Mob + */ +public interface WaterMob extends Creature {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Weather.java b/vspigot-api/src/main/java/org/bukkit/entity/Weather.java new file mode 100644 index 0000000..6d77851 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Weather.java @@ -0,0 +1,6 @@ +package org.bukkit.entity; + +/** + * Represents a Weather related entity, such as a storm + */ +public interface Weather extends Entity {} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Witch.java b/vspigot-api/src/main/java/org/bukkit/entity/Witch.java new file mode 100644 index 0000000..9c5dc1f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Witch.java @@ -0,0 +1,7 @@ +package org.bukkit.entity; + +/** + * Represents a Witch + */ +public interface Witch extends Monster { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Wither.java b/vspigot-api/src/main/java/org/bukkit/entity/Wither.java new file mode 100644 index 0000000..0922c5c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Wither.java @@ -0,0 +1,7 @@ +package org.bukkit.entity; + +/** + * Represents a Wither boss + */ +public interface Wither extends Monster { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/WitherSkull.java b/vspigot-api/src/main/java/org/bukkit/entity/WitherSkull.java new file mode 100644 index 0000000..33d20ab --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/WitherSkull.java @@ -0,0 +1,21 @@ +package org.bukkit.entity; + +/** + * Represents a wither skull {@link Fireball}. + */ +public interface WitherSkull extends Fireball { + + /** + * Sets the charged status of the wither skull. + * + * @param charged whether it should be charged + */ + public void setCharged(boolean charged); + + /** + * Gets whether or not the wither skull is charged. + * + * @return whether the wither skull is charged + */ + public boolean isCharged(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Wolf.java b/vspigot-api/src/main/java/org/bukkit/entity/Wolf.java new file mode 100644 index 0000000..9d5a896 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Wolf.java @@ -0,0 +1,56 @@ +package org.bukkit.entity; + +import org.bukkit.DyeColor; + +/** + * Represents a Wolf + */ +public interface Wolf extends Animals, Tameable { + + /** + * Checks if this wolf is angry + * + * @return Anger true if angry + */ + public boolean isAngry(); + + /** + * Sets the anger of this wolf. + *

          + * An angry wolf can not be fed or tamed, and will actively look for + * targets to attack. + * + * @param angry true if angry + */ + public void setAngry(boolean angry); + + /** + * Checks if this wolf is sitting + * + * @return true if sitting + */ + public boolean isSitting(); + + /** + * Sets if this wolf is sitting. + *

          + * Will remove any path that the wolf was following beforehand. + * + * @param sitting true if sitting + */ + public void setSitting(boolean sitting); + + /** + * Get the collar color of this wolf + * + * @return the color of the collar + */ + public DyeColor getCollarColor(); + + /** + * Set the collar color of this wolf + * + * @param color the color to apply + */ + public void setCollarColor(DyeColor color); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/Zombie.java b/vspigot-api/src/main/java/org/bukkit/entity/Zombie.java new file mode 100644 index 0000000..59b52fd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/Zombie.java @@ -0,0 +1,35 @@ +package org.bukkit.entity; + +/** + * Represents a Zombie. + */ +public interface Zombie extends Monster { + + /** + * Gets whether the zombie is a baby + * + * @return Whether the zombie is a baby + */ + public boolean isBaby(); + + /** + * Sets whether the zombie is a baby + * + * @param flag Whether the zombie is a baby + */ + public void setBaby(boolean flag); + + /** + * Gets whether the zombie is a villager + * + * @return Whether the zombie is a villager + */ + public boolean isVillager(); + + /** + * Sets whether the zombie is a villager + * + * @param flag Whether the zombie is a villager + */ + public void setVillager(boolean flag); +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java new file mode 100644 index 0000000..e502680 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java @@ -0,0 +1,36 @@ +package org.bukkit.entity.minecart; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Minecart; + +public interface CommandMinecart extends Minecart, CommandSender { + + /** + * Gets the command that this CommandMinecart will run when activated. + * This will never return null. If the CommandMinecart does not have a + * command, an empty String will be returned instead. + * + * @return Command that this CommandMinecart will run when powered. + */ + public String getCommand(); + + /** + * Sets the command that this CommandMinecart will run when activated. + * Setting the command to null is the same as setting it to an empty + * String. + * + * @param command Command that this CommandMinecart will run when + * activated. + */ + public void setCommand(String command); + + /** + * Sets the name of this CommandMinecart. The name is used with commands + * that this CommandMinecart executes. Setting the name to null is the + * same as setting it to "@". + * + * @param name New name for this CommandMinecart. + */ + public void setName(String name); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/minecart/ExplosiveMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/minecart/ExplosiveMinecart.java new file mode 100644 index 0000000..a4411da --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/minecart/ExplosiveMinecart.java @@ -0,0 +1,9 @@ +package org.bukkit.entity.minecart; + +import org.bukkit.entity.Minecart; + +/** + * Represents a Minecart with TNT inside it that can explode when triggered. + */ +public interface ExplosiveMinecart extends Minecart { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java new file mode 100644 index 0000000..5da9ce4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java @@ -0,0 +1,10 @@ +package org.bukkit.entity.minecart; + +import org.bukkit.entity.Minecart; +import org.bukkit.inventory.InventoryHolder; + +/** + * Represents a Minecart with a Hopper inside it + */ +public interface HopperMinecart extends Minecart, InventoryHolder { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/minecart/PoweredMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/minecart/PoweredMinecart.java new file mode 100644 index 0000000..57e8b1d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/minecart/PoweredMinecart.java @@ -0,0 +1,10 @@ +package org.bukkit.entity.minecart; + +import org.bukkit.entity.Minecart; + +/** + * Represents a powered minecart. A powered minecart moves on its own when a + * player deposits {@link org.bukkit.Material#COAL fuel}. + */ +public interface PoweredMinecart extends Minecart { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/minecart/RideableMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/minecart/RideableMinecart.java new file mode 100644 index 0000000..1b82645 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/minecart/RideableMinecart.java @@ -0,0 +1,14 @@ +package org.bukkit.entity.minecart; + +import org.bukkit.entity.Minecart; + +/** + * Represents a minecart that can have certain {@link + * org.bukkit.entity.Entity entities} as passengers. Normal passengers + * include all {@link org.bukkit.entity.LivingEntity living entities} with + * the exception of {@link org.bukkit.entity.IronGolem iron golems}. + * Non-player entities that meet normal passenger criteria automatically + * mount these minecarts when close enough. + */ +public interface RideableMinecart extends Minecart { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/minecart/SpawnerMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/minecart/SpawnerMinecart.java new file mode 100644 index 0000000..0ce3592 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/minecart/SpawnerMinecart.java @@ -0,0 +1,10 @@ +package org.bukkit.entity.minecart; + +import org.bukkit.entity.Minecart; + +/** + * Represents a Minecart with an {@link org.bukkit.block.CreatureSpawner + * entity spawner} inside it. + */ +public interface SpawnerMinecart extends Minecart { +} diff --git a/vspigot-api/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java b/vspigot-api/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java new file mode 100644 index 0000000..4f04ab4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java @@ -0,0 +1,12 @@ +package org.bukkit.entity.minecart; + +import org.bukkit.entity.Minecart; +import org.bukkit.inventory.InventoryHolder; + +/** + * Represents a minecart with a chest. These types of {@link Minecart + * minecarts} have their own inventory that can be accessed using methods + * from the {@link InventoryHolder} interface. + */ +public interface StorageMinecart extends Minecart, InventoryHolder { +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/Cancellable.java b/vspigot-api/src/main/java/org/bukkit/event/Cancellable.java new file mode 100644 index 0000000..799b0b0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/Cancellable.java @@ -0,0 +1,20 @@ +package org.bukkit.event; + +public interface Cancellable { + + /** + * Gets the cancellation state of this event. A cancelled event will not + * be executed in the server, but will still pass to other plugins + * + * @return true if this event is cancelled + */ + public boolean isCancelled(); + + /** + * Sets the cancellation state of this event. A cancelled event will not + * be executed in the server, but will still pass to other plugins. + * + * @param cancel true if you wish to cancel this event + */ + public void setCancelled(boolean cancel); +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/Event.java b/vspigot-api/src/main/java/org/bukkit/event/Event.java new file mode 100644 index 0000000..fa29c27 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/Event.java @@ -0,0 +1,96 @@ +package org.bukkit.event; + +import org.bukkit.plugin.PluginManager; + +/** + * Represents an event. + * + * @see PluginManager#callEvent(Event) + * @see PluginManager#registerEvents(Listener,Plugin) + */ +public abstract class Event { + private String name; + private final boolean async; + + /** + * The default constructor is defined for cleaner code. This constructor + * assumes the event is synchronous. + */ + public Event() { + this(false); + } + + /** + * This constructor is used to explicitly declare an event as synchronous + * or asynchronous. + * + * @param isAsync true indicates the event will fire asynchronously, false + * by default from default constructor + */ + public Event(boolean isAsync) { + this.async = isAsync; + } + + /** + * Convenience method for providing a user-friendly identifier. By + * default, it is the event's class's {@linkplain Class#getSimpleName() + * simple name}. + * + * @return name of this event + */ + public String getEventName() { + if (name == null) { + name = getClass().getSimpleName(); + } + return name; + } + + public abstract HandlerList getHandlers(); + + /** + * Any custom event that should not by synchronized with other events must + * use the specific constructor. These are the caveats of using an + * asynchronous event: + *

            + *
          • The event is never fired from inside code triggered by a + * synchronous event. Attempting to do so results in an {@link + * java.lang.IllegalStateException}. + *
          • However, asynchronous event handlers may fire synchronous or + * asynchronous events + *
          • The event may be fired multiple times simultaneously and in any + * order. + *
          • Any newly registered or unregistered handler is ignored after an + * event starts execution. + *
          • The handlers for this event may block for any length of time. + *
          • Some implementations may selectively declare a specific event use + * as asynchronous. This behavior should be clearly defined. + *
          • Asynchronous calls are not calculated in the plugin timing system. + *
          + * + * @return false by default, true if the event fires asynchronously + */ + public final boolean isAsynchronous() { + return async; + } + + public enum Result { + + /** + * Deny the event. Depending on the event, the action indicated by the + * event will either not take place or will be reverted. Some actions + * may not be denied. + */ + DENY, + /** + * Neither deny nor allow the event. The server will proceed with its + * normal handling. + */ + DEFAULT, + /** + * Allow / Force the event. The action indicated by the event will + * take place if possible, even if the server would not normally allow + * the action. Some actions may not be allowed. + */ + ALLOW; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/EventException.java b/vspigot-api/src/main/java/org/bukkit/event/EventException.java new file mode 100644 index 0000000..84638e8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/EventException.java @@ -0,0 +1,53 @@ +package org.bukkit.event; + +public class EventException extends Exception { + private static final long serialVersionUID = 3532808232324183999L; + private final Throwable cause; + + /** + * Constructs a new EventException based on the given Exception + * + * @param throwable Exception that triggered this Exception + */ + public EventException(Throwable throwable) { + cause = throwable; + } + + /** + * Constructs a new EventException + */ + public EventException() { + cause = null; + } + + /** + * Constructs a new EventException with the given message + * + * @param cause The exception that caused this + * @param message The message + */ + public EventException(Throwable cause, String message) { + super(message); + this.cause = cause; + } + + /** + * Constructs a new EventException with the given message + * + * @param message The message + */ + public EventException(String message) { + super(message); + cause = null; + } + + /** + * If applicable, returns the Exception that triggered this Exception + * + * @return Inner exception, or null if one does not exist + */ + @Override + public Throwable getCause() { + return cause; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/EventHandler.java b/vspigot-api/src/main/java/org/bukkit/event/EventHandler.java new file mode 100644 index 0000000..e42acc1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/EventHandler.java @@ -0,0 +1,37 @@ +package org.bukkit.event; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation to mark methods as being event handler methods + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface EventHandler { + + /** + * Define the priority of the event. + *

          + * First priority to the last priority executed: + *

            + *
          1. LOWEST + *
          2. LOW + *
          3. NORMAL + *
          4. HIGH + *
          5. HIGHEST + *
          6. MONITOR + *
          + */ + EventPriority priority() default EventPriority.NORMAL; + + /** + * Define if the handler ignores a cancelled event. + *

          + * If ignoreCancelled is true and the event is cancelled, the method is + * not called. Otherwise, the method is always called. + */ + boolean ignoreCancelled() default false; +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/EventPriority.java b/vspigot-api/src/main/java/org/bukkit/event/EventPriority.java new file mode 100644 index 0000000..61ffa50 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/EventPriority.java @@ -0,0 +1,47 @@ +package org.bukkit.event; + +/** + * Represents an event's priority in execution + */ +public enum EventPriority { + + /** + * Event call is of very low importance and should be ran first, to allow + * other plugins to further customise the outcome + */ + LOWEST(0), + /** + * Event call is of low importance + */ + LOW(1), + /** + * Event call is neither important nor unimportant, and may be ran + * normally + */ + NORMAL(2), + /** + * Event call is of high importance + */ + HIGH(3), + /** + * Event call is critical and must have the final say in what happens + * to the event + */ + HIGHEST(4), + /** + * Event is listened to purely for monitoring the outcome of an event. + *

          + * No modifications to the event should be made under this priority + */ + MONITOR(5); + + private final int slot; + + private EventPriority(int slot) { + this.slot = slot; + } + + public int getSlot() { + return slot; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/HandlerList.java b/vspigot-api/src/main/java/org/bukkit/event/HandlerList.java new file mode 100644 index 0000000..7d5efff --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/HandlerList.java @@ -0,0 +1,231 @@ +package org.bukkit.event; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; + +import java.util.*; +import java.util.Map.Entry; + +/** + * A list of event handlers, stored per-event. Based on lahwran's fevents. + */ +public class HandlerList { + + /** + * Handler array. This field being an array is the key to this system's + * speed. + */ + private volatile RegisteredListener[] handlers = null; + + /** + * Dynamic handler lists. These are changed using register() and + * unregister() and are automatically baked to the handlers array any time + * they have changed. + */ + private final EnumMap> handlerslots; + + /** + * List of all HandlerLists which have been created, for use in bakeAll() + */ + private static ArrayList allLists = new ArrayList(); + + /** + * Bake all handler lists. Best used just after all normal event + * registration is complete, ie just after all plugins are loaded if + * you're using fevents in a plugin system. + */ + public static void bakeAll() { + synchronized (allLists) { + for (HandlerList h : allLists) { + h.bake(); + } + } + } + + /** + * Unregister all listeners from all handler lists. + */ + public static void unregisterAll() { + synchronized (allLists) { + for (HandlerList h : allLists) { + synchronized (h) { + for (List list : h.handlerslots.values()) { + list.clear(); + } + h.handlers = null; + } + } + } + } + + /** + * Unregister a specific plugin's listeners from all handler lists. + * + * @param plugin plugin to unregister + */ + public static void unregisterAll(Plugin plugin) { + synchronized (allLists) { + for (HandlerList h : allLists) { + h.unregister(plugin); + } + } + } + + /** + * Unregister a specific listener from all handler lists. + * + * @param listener listener to unregister + */ + public static void unregisterAll(Listener listener) { + synchronized (allLists) { + for (HandlerList h : allLists) { + h.unregister(listener); + } + } + } + + /** + * Create a new handler list and initialize using EventPriority. + *

          + * The HandlerList is then added to meta-list for use in bakeAll() + */ + public HandlerList() { + handlerslots = new EnumMap>(EventPriority.class); + for (EventPriority o : EventPriority.values()) { + handlerslots.put(o, new ArrayList()); + } + synchronized (allLists) { + allLists.add(this); + } + } + + /** + * Register a new listener in this handler list + * + * @param listener listener to register + */ + public synchronized void register(RegisteredListener listener) { + if (handlerslots.get(listener.getPriority()).contains(listener)) + throw new IllegalStateException("This listener is already registered to priority " + listener.getPriority().toString()); + handlers = null; + handlerslots.get(listener.getPriority()).add(listener); + } + + /** + * Register a collection of new listeners in this handler list + * + * @param listeners listeners to register + */ + public void registerAll(Collection listeners) { + for (RegisteredListener listener : listeners) { + register(listener); + } + } + + /** + * Remove a listener from a specific order slot + * + * @param listener listener to remove + */ + public synchronized void unregister(RegisteredListener listener) { + if (handlerslots.get(listener.getPriority()).remove(listener)) { + handlers = null; + } + } + + /** + * Remove a specific plugin's listeners from this handler + * + * @param plugin plugin to remove + */ + public synchronized void unregister(Plugin plugin) { + boolean changed = false; + for (List list : handlerslots.values()) { + for (ListIterator i = list.listIterator(); i.hasNext();) { + if (i.next().getPlugin().equals(plugin)) { + i.remove(); + changed = true; + } + } + } + if (changed) handlers = null; + } + + /** + * Remove a specific listener from this handler + * + * @param listener listener to remove + */ + public synchronized void unregister(Listener listener) { + boolean changed = false; + for (List list : handlerslots.values()) { + for (ListIterator i = list.listIterator(); i.hasNext();) { + if (i.next().getListener().equals(listener)) { + i.remove(); + changed = true; + } + } + } + if (changed) handlers = null; + } + + /** + * Bake HashMap and ArrayLists to 2d array - does nothing if not necessary + */ + public synchronized void bake() { + if (handlers != null) return; // don't re-bake when still valid + List entries = new ArrayList(); + for (Entry> entry : handlerslots.entrySet()) { + entries.addAll(entry.getValue()); + } + handlers = entries.toArray(new RegisteredListener[entries.size()]); + } + + /** + * Get the baked registered listeners associated with this handler list + * + * @return the array of registered listeners + */ + public RegisteredListener[] getRegisteredListeners() { + RegisteredListener[] handlers; + while ((handlers = this.handlers) == null) bake(); // This prevents fringe cases of returning null + return handlers; + } + + /** + * Get a specific plugin's registered listeners associated with this + * handler list + * + * @param plugin the plugin to get the listeners of + * @return the list of registered listeners + */ + public static ArrayList getRegisteredListeners(Plugin plugin) { + ArrayList listeners = new ArrayList(); + synchronized (allLists) { + for (HandlerList h : allLists) { + synchronized (h) { + for (List list : h.handlerslots.values()) { + for (RegisteredListener listener : list) { + if (listener.getPlugin().equals(plugin)) { + listeners.add(listener); + } + } + } + } + } + } + return listeners; + } + + /** + * Get a list of all handler lists for every event type + * + * @return the list of all handler lists + */ + @SuppressWarnings("unchecked") + public static ArrayList getHandlerLists() { + synchronized (allLists) { + return (ArrayList) allLists.clone(); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/Listener.java b/vspigot-api/src/main/java/org/bukkit/event/Listener.java new file mode 100644 index 0000000..ff083e6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/Listener.java @@ -0,0 +1,6 @@ +package org.bukkit.event; + +/** + * Simple interface for tagging all EventListeners + */ +public interface Listener {} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/Action.java b/vspigot-api/src/main/java/org/bukkit/event/block/Action.java new file mode 100644 index 0000000..25d26e3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/Action.java @@ -0,0 +1,33 @@ +package org.bukkit.event.block; + +public enum Action { + + /** + * Left-clicking a block + */ + LEFT_CLICK_BLOCK, + /** + * Right-clicking a block + */ + RIGHT_CLICK_BLOCK, + /** + * Left-clicking the air + */ + LEFT_CLICK_AIR, + /** + * Right-clicking the air + */ + RIGHT_CLICK_AIR, + /** + * Stepping onto or into a block (Ass-pressure) + * + * Examples: + *

            + *
          • Jumping on soil + *
          • Standing on pressure plate + *
          • Triggering redstone ore + *
          • Triggering tripwire + *
          + */ + PHYSICAL, +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BeaconEffectEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BeaconEffectEvent.java new file mode 100644 index 0000000..66e62ff --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BeaconEffectEvent.java @@ -0,0 +1,81 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockEvent; +import org.bukkit.potion.PotionEffect; + +/** + * Called when a beacon effect is being applied to a player. + */ +public class BeaconEffectEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private PotionEffect effect; + private Player player; + private boolean primary; + + public BeaconEffectEvent(Block block, PotionEffect effect, Player player, boolean primary) { + super(block); + this.effect = effect; + this.player = player; + this.primary = primary; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + /** + * Gets the potion effect being applied. + * + * @return Potion effect + */ + public PotionEffect getEffect() { + return effect; + } + + /** + * Sets the potion effect that will be applied. + * + * @param effect Potion effect + */ + public void setEffect(PotionEffect effect) { + this.effect = effect; + } + + /** + * Gets the player who the potion effect is being applied to. + * + * @return Affected player + */ + public Player getPlayer() { + return player; + } + + /** + * Gets whether the effect is a primary beacon effect. + * + * @return true if this event represents a primary effect + */ + public boolean isPrimary() { + return primary; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockBreakEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockBreakEvent.java new file mode 100644 index 0000000..e18b22f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockBreakEvent.java @@ -0,0 +1,65 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a block is broken by a player. + *

          + * If you wish to have the block drop experience, you must set the experience + * value above 0. By default, experience will be set in the event if: + *

            + *
          1. The player is not in creative or adventure mode + *
          2. The player can loot the block (ie: does not destroy it completely, by + * using the correct tool) + *
          3. The player does not have silk touch + *
          4. The block drops experience in vanilla Minecraft + *
          + *

          + * Note: + * Plugins wanting to simulate a traditional block drop should set the block + * to air and utilize their own methods for determining what the default drop + * for the block being broken is and what to do about it, if anything. + *

          + * If a Block Break event is cancelled, the block will not break and + * experience will not drop. + */ +public class BlockBreakEvent extends BlockExpEvent implements Cancellable { + private final Player player; + private boolean cancel; + private boolean cancelDrops; + + public BlockBreakEvent(final Block theBlock, final Player player) { + super(theBlock, 0); + + this.player = player; + } + + /** + * Gets the Player that is breaking the block involved in this event. + * + * @return The Player that is breaking the block involved in this event + */ + public Player getPlayer() { + return player; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + public boolean isCancelDrops() { + return cancelDrops; + } + + public void setCancelDrops(boolean cancel) { + this.cancelDrops = cancel; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockBurnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockBurnEvent.java new file mode 100644 index 0000000..1592a15 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockBurnEvent.java @@ -0,0 +1,38 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a block is destroyed as a result of being burnt by fire. + *

          + * If a Block Burn event is cancelled, the block will not be destroyed as a + * result of being burnt by fire. + */ +public class BlockBurnEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + + public BlockBurnEvent(final Block block) { + super(block); + this.cancelled = false; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockCanBuildEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockCanBuildEvent.java new file mode 100644 index 0000000..3860f44 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockCanBuildEvent.java @@ -0,0 +1,90 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.Material; +import org.bukkit.event.HandlerList; + +/** + * Called when we try to place a block, to see if we can build it here or not. + *

          + * Note: + *

            + *
          • The Block returned by getBlock() is the block we are trying to place + * on, not the block we are trying to place. + *
          • If you want to figure out what is being placed, use {@link + * #getMaterial()} or {@link #getMaterialId()} instead. + *
          + */ +public class BlockCanBuildEvent extends BlockEvent { + private static final HandlerList handlers = new HandlerList(); + protected boolean buildable; + + /** + * + * @deprecated Magic value + */ + @Deprecated + protected int material; + + /** + * + * @deprecated Magic value + */ + @Deprecated + public BlockCanBuildEvent(final Block block, final int id, final boolean canBuild) { + super(block); + buildable = canBuild; + material = id; + } + + /** + * Gets whether or not the block can be built here. + *

          + * By default, returns Minecraft's answer on whether the block can be + * built here or not. + * + * @return boolean whether or not the block can be built + */ + public boolean isBuildable() { + return buildable; + } + + /** + * Sets whether the block can be built here or not. + * + * @param cancel true if you want to allow the block to be built here + * despite Minecraft's default behaviour + */ + public void setBuildable(boolean cancel) { + this.buildable = cancel; + } + + /** + * Gets the Material that we are trying to place. + * + * @return The Material that we are trying to place + */ + public Material getMaterial() { + return Material.getMaterial(material); + } + + /** + * Gets the Material ID for the Material that we are trying to place. + * + * @return The Material ID for the Material that we are trying to place + * @deprecated Magic value + */ + @Deprecated + public int getMaterialId() { + return material; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockDamageEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockDamageEvent.java new file mode 100644 index 0000000..d80e00e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockDamageEvent.java @@ -0,0 +1,83 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * Called when a block is damaged by a player. + *

          + * If a Block Damage event is cancelled, the block will not be damaged. + */ +public class BlockDamageEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private boolean instaBreak; + private boolean cancel; + private final ItemStack itemstack; + + public BlockDamageEvent(final Player player, final Block block, final ItemStack itemInHand, final boolean instaBreak) { + super(block); + this.instaBreak = instaBreak; + this.player = player; + this.itemstack = itemInHand; + this.cancel = false; + } + + /** + * Gets the player damaging the block involved in this event. + * + * @return The player damaging the block involved in this event + */ + public Player getPlayer() { + return player; + } + + /** + * Gets if the block is set to instantly break when damaged by the player. + * + * @return true if the block should instantly break when damaged by the + * player + */ + public boolean getInstaBreak() { + return instaBreak; + } + + /** + * Sets if the block should instantly break when damaged by the player. + * + * @param bool true if you want the block to instantly break when damaged + * by the player + */ + public void setInstaBreak(boolean bool) { + this.instaBreak = bool; + } + + /** + * Gets the ItemStack for the item currently in the player's hand. + * + * @return The ItemStack for the item currently in the player's hand + */ + public ItemStack getItemInHand() { + return itemstack; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockDispenseEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockDispenseEvent.java new file mode 100644 index 0000000..16ee59b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockDispenseEvent.java @@ -0,0 +1,84 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +/** + * Called when an item is dispensed from a block. + *

          + * If a Block Dispense event is cancelled, the block will not dispense the + * item. + */ +public class BlockDispenseEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled = false; + private ItemStack item; + private Vector velocity; + + public BlockDispenseEvent(final Block block, final ItemStack dispensed, final Vector velocity) { + super(block); + this.item = dispensed; + this.velocity = velocity; + } + + /** + * Gets the item that is being dispensed. Modifying the returned item will + * have no effect, you must use {@link + * #setItem(org.bukkit.inventory.ItemStack)} instead. + * + * @return An ItemStack for the item being dispensed + */ + public ItemStack getItem() { + return item.clone(); + } + + /** + * Sets the item being dispensed. + * + * @param item the item being dispensed + */ + public void setItem(ItemStack item) { + this.item = item; + } + + /** + * Gets the velocity. + *

          + * Note: Modifying the returned Vector will not change the velocity, you + * must use {@link #setVelocity(org.bukkit.util.Vector)} instead. + * + * @return A Vector for the dispensed item's velocity + */ + public Vector getVelocity() { + return velocity.clone(); + } + + /** + * Sets the velocity of the item being dispensed. + * + * @param vel the velocity of the item being dispensed + */ + public void setVelocity(Vector vel) { + velocity = vel; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockDropItemsEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockDropItemsEvent.java new file mode 100644 index 0000000..fb7d18e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockDropItemsEvent.java @@ -0,0 +1,56 @@ +package org.bukkit.event.block; + +import java.util.List; + +import org.bukkit.block.Block; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BlockDropItemsEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private Block block; + private Player player; + private List toDrop; + private boolean cancelled = false; + + public BlockDropItemsEvent(Block block, Player player, List toDrop) { + this.block = block; + this.player = player; + this.toDrop = toDrop; + } + + public Block getBlock() { + return this.block; + } + + public Player getPlayer() { + return this.player; + } + + public List getToDrop() { + return this.toDrop; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockEvent.java new file mode 100644 index 0000000..2405205 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.event.Event; + +/** + * Represents a block related event. + */ +public abstract class BlockEvent extends Event { + protected Block block; + + public BlockEvent(final Block theBlock) { + block = theBlock; + } + + /** + * Gets the block involved in this event. + * + * @return The Block which block is involved in this event + */ + public final Block getBlock() { + return block; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockExpEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockExpEvent.java new file mode 100644 index 0000000..08636a2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockExpEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.event.HandlerList; + +/** + * An event that's called when a block yields experience. + */ +public class BlockExpEvent extends BlockEvent { + private static final HandlerList handlers = new HandlerList(); + private int exp; + + public BlockExpEvent(Block block, int exp) { + super(block); + + this.exp = exp; + } + + /** + * Get the experience dropped by the block after the event has processed + * + * @return The experience to drop + */ + public int getExpToDrop() { + return exp; + } + + /** + * Set the amount of experience dropped by the block after the event has + * processed + * + * @param exp 1 or higher to drop experience, else nothing will drop + */ + public void setExpToDrop(int exp) { + this.exp = exp; + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockFadeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockFadeEvent.java new file mode 100644 index 0000000..673bc5f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockFadeEvent.java @@ -0,0 +1,59 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a block fades, melts or disappears based on world conditions + *

          + * Examples: + *

            + *
          • Snow melting due to being near a light source. + *
          • Ice melting due to being near a light source. + *
          • Fire burning out after time, without destroying fuel block. + *
          + *

          + * If a Block Fade event is cancelled, the block will not fade, melt or + * disappear. + */ +public class BlockFadeEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final BlockState newState; + + public BlockFadeEvent(final Block block, final BlockState newState) { + super(block); + this.newState = newState; + this.cancelled = false; + } + + /** + * Gets the state of the block that will be fading, melting or + * disappearing. + * + * @return The block state of the block that will be fading, melting or + * disappearing + */ + public BlockState getNewState() { + return newState; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockFormEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockFormEvent.java new file mode 100644 index 0000000..df0401f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockFormEvent.java @@ -0,0 +1,39 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a block is formed or spreads based on world conditions. + *

          + * Use {@link BlockSpreadEvent} to catch blocks that actually spread and don't + * just "randomly" form. + *

          + * Examples: + *

            + *
          • Snow forming due to a snow storm. + *
          • Ice forming in a snowy Biome like Taiga or Tundra. + *
          + *

          + * If a Block Form event is cancelled, the block will not be formed. + * + * @see BlockSpreadEvent + */ +public class BlockFormEvent extends BlockGrowEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + public BlockFormEvent(final Block block, final BlockState newState) { + super(block, newState); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockFromToEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockFromToEvent.java new file mode 100644 index 0000000..f976bea --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockFromToEvent.java @@ -0,0 +1,71 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Represents events with a source block and a destination block, currently + * only applies to liquid (lava and water) and teleporting dragon eggs. + *

          + * If a Block From To event is cancelled, the block will not move (the liquid + * will not flow). + */ +public class BlockFromToEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + protected Block to; + protected BlockFace face; + protected boolean cancel; + + public BlockFromToEvent(final Block block, final BlockFace face) { + super(block); + this.face = face; + this.cancel = false; + } + + public BlockFromToEvent(final Block block, final Block toBlock) { + super(block); + this.to = toBlock; + this.face = BlockFace.SELF; + this.cancel = false; + } + + /** + * Gets the BlockFace that the block is moving to. + * + * @return The BlockFace that the block is moving to + */ + public BlockFace getFace() { + return face; + } + + /** + * Convenience method for getting the faced Block. + * + * @return The faced Block + */ + public Block getToBlock() { + if (to == null) { + to = block.getRelative(face); + } + return to; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockGrowEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockGrowEvent.java new file mode 100644 index 0000000..2a959fd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockGrowEvent.java @@ -0,0 +1,56 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a block grows naturally in the world. + *

          + * Examples: + *

            + *
          • Wheat + *
          • Sugar Cane + *
          • Cactus + *
          • Watermelon + *
          • Pumpkin + *
          + *

          + * If a Block Grow event is cancelled, the block will not grow. + */ +public class BlockGrowEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final BlockState newState; + private boolean cancelled = false; + + public BlockGrowEvent(final Block block, final BlockState newState) { + super(block); + this.newState = newState; + } + + /** + * Gets the state of the block where it will form or spread to. + * + * @return The block state for this events block + */ + public BlockState getNewState() { + return newState; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockIgniteEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockIgniteEvent.java new file mode 100644 index 0000000..733a15e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockIgniteEvent.java @@ -0,0 +1,138 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a block is ignited. If you want to catch when a Player places + * fire, you need to use {@link BlockPlaceEvent}. + *

          + * If a Block Ignite event is cancelled, the block will not be ignited. + */ +public class BlockIgniteEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final IgniteCause cause; + private final Entity ignitingEntity; + private final Block ignitingBlock; + private boolean cancel; + + /** + * @deprecated use {@link BlockIgniteEvent#BlockIgniteEvent(Block, + * IgniteCause, Entity)} instead. + */ + @Deprecated + public BlockIgniteEvent(final Block theBlock, final IgniteCause cause, final Player thePlayer) { + this(theBlock, cause, (Entity) thePlayer); + } + + public BlockIgniteEvent(final Block theBlock, final IgniteCause cause, final Entity ignitingEntity) { + this(theBlock, cause, ignitingEntity, null); + } + + public BlockIgniteEvent(final Block theBlock, final IgniteCause cause, final Block ignitingBlock) { + this(theBlock, cause, null, ignitingBlock); + } + + public BlockIgniteEvent(final Block theBlock, final IgniteCause cause, final Entity ignitingEntity, final Block ignitingBlock) { + super(theBlock); + this.cause = cause; + this.ignitingEntity = ignitingEntity; + this.ignitingBlock = ignitingBlock; + this.cancel = false; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the cause of block ignite. + * + * @return An IgniteCause value detailing the cause of block ignition + */ + public IgniteCause getCause() { + return cause; + } + + /** + * Gets the player who ignited this block + * + * @return The Player that placed/ignited the fire block, or null if not ignited by a Player. + */ + public Player getPlayer() { + if (ignitingEntity instanceof Player) { + return (Player) ignitingEntity; + } + + return null; + } + + /** + * Gets the entity who ignited this block + * + * @return The Entity that placed/ignited the fire block, or null if not ignited by a Entity. + */ + public Entity getIgnitingEntity() { + return ignitingEntity; + } + + /** + * Gets the block who ignited this block + * + * @return The Block that placed/ignited the fire block, or null if not ignited by a Block. + */ + public Block getIgnitingBlock() { + return ignitingBlock; + } + + /** + * An enum to specify the cause of the ignite + */ + public enum IgniteCause { + + /** + * Block ignition caused by lava. + */ + LAVA, + /** + * Block ignition caused by a player or dispenser using flint-and-steel. + */ + FLINT_AND_STEEL, + /** + * Block ignition caused by dynamic spreading of fire. + */ + SPREAD, + /** + * Block ignition caused by lightning. + */ + LIGHTNING, + /** + * Block ignition caused by an entity using a fireball. + */ + FIREBALL, + /** + * Block ignition caused by an Ender Crystal. + */ + ENDER_CRYSTAL, + /** + * Block ignition caused by explosion. + */ + EXPLOSION, + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java new file mode 100644 index 0000000..d16e4be --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java @@ -0,0 +1,36 @@ +package org.bukkit.event.block; + +import com.google.common.collect.ImmutableList; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +/** + * Fired when a single block placement action of a player triggers the + * creation of multiple blocks(e.g. placing a bed block). The block returned + * by {@link #getBlockPlaced()} and its related methods is the block where + * the placed block would exist if the placement only affected a single + * block. + */ +public class BlockMultiPlaceEvent extends BlockPlaceEvent { + private final List states; + + public BlockMultiPlaceEvent(List states, Block clicked, ItemStack itemInHand, Player thePlayer, boolean canBuild) { + super(states.get(0).getBlock(), states.get(0), clicked, itemInHand, thePlayer, canBuild); + this.states = ImmutableList.copyOf(states); + } + + /** + * Gets a list of blockstates for all blocks which were replaced by the + * placement of the new blocks. Most of these blocks will just have a + * Material type of AIR. + * + * @return immutable list of replaced BlockStates + */ + public List getReplacedBlockStates() { + return states; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockPhysicsEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPhysicsEvent.java new file mode 100644 index 0000000..e05d1ca --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPhysicsEvent.java @@ -0,0 +1,62 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.Material; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Thrown when a block physics check is called + */ +public class BlockPhysicsEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final int changed; + private boolean cancel = false; + + /** + * + * @deprecated Magic value + */ + @Deprecated + public BlockPhysicsEvent(final Block block, final int changed) { + super(block); + this.changed = changed; + } + + /** + * Gets the type of block that changed, causing this event + * + * @return Changed block's type id + * @deprecated Magic value + */ + @Deprecated + public int getChangedTypeId() { + return changed; + } + + /** + * Gets the type of block that changed, causing this event + * + * @return Changed block's type + */ + public Material getChangedType() { + return Material.getMaterial(changed); + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonEvent.java new file mode 100644 index 0000000..b89006f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonEvent.java @@ -0,0 +1,48 @@ +package org.bukkit.event.block; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.Cancellable; + +/** + * Called when a piston block is triggered + */ +public abstract class BlockPistonEvent extends BlockEvent implements Cancellable { + private boolean cancelled; + private final BlockFace direction; + + public BlockPistonEvent(final Block block, final BlockFace direction) { + super(block); + this.direction = direction; + } + + public boolean isCancelled() { + return this.cancelled; + } + + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + /** + * Returns true if the Piston in the event is sticky. + * + * @return stickiness of the piston + */ + public boolean isSticky() { + return block.getType() == Material.PISTON_STICKY_BASE; + } + + /** + * Return the direction in which the piston will operate. + * + * @return direction of the piston + */ + public BlockFace getDirection() { + // Both are meh! + // return ((PistonBaseMaterial) block.getType().getNewData(block.getData())).getFacing(); + // return ((PistonBaseMaterial) block.getState().getData()).getFacing(); + return direction; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonExtendEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonExtendEvent.java new file mode 100644 index 0000000..1058b8b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonExtendEvent.java @@ -0,0 +1,59 @@ +package org.bukkit.event.block; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.HandlerList; + +/** + * Called when a piston extends + */ +public class BlockPistonExtendEvent extends BlockPistonEvent { + private static final HandlerList handlers = new HandlerList(); + private final int length; + private List blocks; + + public BlockPistonExtendEvent(final Block block, final int length, final BlockFace direction) { + super(block, direction); + + this.length = length; + } + + /** + * Get the amount of blocks which will be moved while extending. + * + * @return the amount of moving blocks + */ + public int getLength() { + return this.length; + } + + /** + * Get an immutable list of the blocks which will be moved by the + * extending. + * + * @return Immutable list of the moved blocks. + */ + public List getBlocks() { + if (blocks == null) { + ArrayList tmp = new ArrayList(); + for (int i = 0; i < this.getLength(); i++) { + tmp.add(block.getRelative(getDirection(), i + 1)); + } + blocks = Collections.unmodifiableList(tmp); + } + return blocks; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonRetractEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonRetractEvent.java new file mode 100644 index 0000000..0190c4c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPistonRetractEvent.java @@ -0,0 +1,35 @@ +package org.bukkit.event.block; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.HandlerList; + +/** + * Called when a piston retracts + */ +public class BlockPistonRetractEvent extends BlockPistonEvent { + private static final HandlerList handlers = new HandlerList(); + public BlockPistonRetractEvent(final Block block, final BlockFace direction) { + super(block, direction); + } + + /** + * Gets the location where the possible moving block might be if the + * retracting piston is sticky. + * + * @return The possible location of the possibly moving block. + */ + public Location getRetractLocation() { + return getBlock().getRelative(getDirection(), 2).getLocation(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java new file mode 100644 index 0000000..6d0ffe8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java @@ -0,0 +1,121 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * Called when a block is placed by a player. + *

          + * If a Block Place event is cancelled, the block will not be placed. + */ +public class BlockPlaceEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + protected boolean cancel; + protected boolean canBuild; + protected Block placedAgainst; + protected BlockState replacedBlockState; + protected ItemStack itemInHand; + protected Player player; + + public BlockPlaceEvent(final Block placedBlock, final BlockState replacedBlockState, final Block placedAgainst, final ItemStack itemInHand, final Player thePlayer, final boolean canBuild) { + super(placedBlock); + this.placedAgainst = placedAgainst; + this.itemInHand = itemInHand; + this.player = thePlayer; + this.replacedBlockState = replacedBlockState; + this.canBuild = canBuild; + cancel = false; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the player who placed the block involved in this event. + * + * @return The Player who placed the block involved in this event + */ + public Player getPlayer() { + return player; + } + + /** + * Clarity method for getting the placed block. Not really needed except + * for reasons of clarity. + * + * @return The Block that was placed + */ + public Block getBlockPlaced() { + return getBlock(); + } + + /** + * Gets the BlockState for the block which was replaced. Material type air + * mostly. + * + * @return The BlockState for the block which was replaced. + */ + public BlockState getBlockReplacedState() { + return this.replacedBlockState; + } + + /** + * Gets the block that this block was placed against + * + * @return Block the block that the new block was placed against + */ + public Block getBlockAgainst() { + return placedAgainst; + } + + /** + * Gets the item in the player's hand when they placed the block. + * + * @return The ItemStack for the item in the player's hand when they + * placed the block + */ + public ItemStack getItemInHand() { + return itemInHand; + } + + /** + * Gets the value whether the player would be allowed to build here. + * Defaults to spawn if the server was going to stop them (such as, the + * player is in Spawn). Note that this is an entirely different check + * than BLOCK_CANBUILD, as this refers to a player, not universe-physics + * rule like cactus on dirt. + * + * @return boolean whether the server would allow a player to build here + */ + public boolean canBuild() { + return this.canBuild; + } + + /** + * Sets the canBuild state of this event. Set to true if you want the + * player to be able to build. + * + * @param canBuild true if you want the player to be able to build + */ + public void setBuild(boolean canBuild) { + this.canBuild = canBuild; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockRedstoneEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockRedstoneEvent.java new file mode 100644 index 0000000..625ec90 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockRedstoneEvent.java @@ -0,0 +1,55 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.event.HandlerList; + +/** + * Called when a redstone current changes + */ +public class BlockRedstoneEvent extends BlockEvent { + private static final HandlerList handlers = new HandlerList(); + private final int oldCurrent; + private int newCurrent; + + public BlockRedstoneEvent(final Block block, final int oldCurrent, final int newCurrent) { + super(block); + this.oldCurrent = oldCurrent; + this.newCurrent = newCurrent; + } + + /** + * Gets the old current of this block + * + * @return The previous current + */ + public int getOldCurrent() { + return oldCurrent; + } + + /** + * Gets the new current of this block + * + * @return The new current + */ + public int getNewCurrent() { + return newCurrent; + } + + /** + * Sets the new current of this block + * + * @param newCurrent The new current to set + */ + public void setNewCurrent(int newCurrent) { + this.newCurrent = newCurrent; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/BlockSpreadEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/BlockSpreadEvent.java new file mode 100644 index 0000000..a1fb363 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/BlockSpreadEvent.java @@ -0,0 +1,49 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.event.HandlerList; + +/** + * Called when a block spreads based on world conditions. + *

          + * Use {@link BlockFormEvent} to catch blocks that "randomly" form instead of + * actually spread. + *

          + * Examples: + *

            + *
          • Mushrooms spreading. + *
          • Fire spreading. + *
          + *

          + * If a Block Spread event is cancelled, the block will not spread. + * + * @see BlockFormEvent + */ +public class BlockSpreadEvent extends BlockFormEvent { + private static final HandlerList handlers = new HandlerList(); + private final Block source; + + public BlockSpreadEvent(final Block block, final Block source, final BlockState newState) { + super(block, newState); + this.source = source; + } + + /** + * Gets the source block involved in this event. + * + * @return the Block for the source block involved in this event. + */ + public Block getSource() { + return source; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/EntityBlockFormEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/EntityBlockFormEvent.java new file mode 100644 index 0000000..45efc32 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/EntityBlockFormEvent.java @@ -0,0 +1,32 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Entity; + +/** + * Called when a block is formed by entities. + *

          + * Examples: + *

            + *
          • Snow formed by a {@link org.bukkit.entity.Snowman}. + *
          + */ +public class EntityBlockFormEvent extends BlockFormEvent { + private final Entity entity; + + public EntityBlockFormEvent(final Entity entity, final Block block, final BlockState blockstate) { + super(block, blockstate); + + this.entity = entity; + } + + /** + * Get the entity that formed the block. + * + * @return Entity involved in event + */ + public Entity getEntity() { + return entity; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/LeavesDecayEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/LeavesDecayEvent.java new file mode 100644 index 0000000..84d8cfd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/LeavesDecayEvent.java @@ -0,0 +1,36 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when leaves are decaying naturally. + *

          + * If a Leaves Decay event is cancelled, the leaves will not decay. + */ +public class LeavesDecayEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + + public LeavesDecayEvent(final Block block) { + super(block); + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/NotePlayEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/NotePlayEvent.java new file mode 100644 index 0000000..d4d4381 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/NotePlayEvent.java @@ -0,0 +1,83 @@ +package org.bukkit.event.block; + +import org.bukkit.Instrument; +import org.bukkit.Note; +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a note block is being played through player interaction or a + * redstone current. + */ +public class NotePlayEvent extends BlockEvent implements Cancellable { + + private static HandlerList handlers = new HandlerList(); + private Instrument instrument; + private Note note; + private boolean cancelled = false; + + public NotePlayEvent(Block block, Instrument instrument, Note note) { + super(block); + this.instrument = instrument; + this.note = note; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * Gets the {@link Instrument} to be used. + * + * @return the Instrument; + */ + public Instrument getInstrument() { + return instrument; + } + + /** + * Gets the {@link Note} to be played. + * + * @return the Note. + */ + public Note getNote() { + return note; + } + + /** + * Overrides the {@link Instrument} to be used. + * + * @param instrument the Instrument. Has no effect if null. + */ + public void setInstrument(Instrument instrument) { + if (instrument != null) { + this.instrument = instrument; + } + + } + + /** + * Overrides the {@link Note} to be played. + * + * @param note the Note. Has no effect if null. + */ + public void setNote(Note note) { + if (note != null) { + this.note = note; + } + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/block/SignChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/block/SignChangeEvent.java new file mode 100644 index 0000000..d1b7908 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/block/SignChangeEvent.java @@ -0,0 +1,84 @@ +package org.bukkit.event.block; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a sign is changed by a player. + *

          + * If a Sign Change event is cancelled, the sign will not be changed. + */ +public class SignChangeEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private final Player player; + private final String[] lines; + + public SignChangeEvent(final Block theBlock, final Player thePlayer, final String[] theLines) { + super(theBlock); + this.player = thePlayer; + this.lines = theLines; + } + + /** + * Gets the player changing the sign involved in this event. + * + * @return the Player involved in this event + */ + public Player getPlayer() { + return player; + } + + /** + * Gets all of the lines of text from the sign involved in this event. + * + * @return the String array for the sign's lines new text + */ + public String[] getLines() { + return lines; + } + + /** + * Gets a single line of text from the sign involved in this event. + * + * @param index index of the line to get + * @return the String containing the line of text associated with the + * provided index + * @throws IndexOutOfBoundsException thrown when the provided index is > 3 + * or < 0 + */ + public String getLine(int index) throws IndexOutOfBoundsException { + return lines[index]; + } + + /** + * Sets a single line for the sign involved in this event + * + * @param index index of the line to set + * @param line text to set + * @throws IndexOutOfBoundsException thrown when the provided index is > 3 + * or < 0 + */ + public void setLine(int index, String line) throws IndexOutOfBoundsException { + lines[index] = line; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/enchantment/EnchantItemEvent.java b/vspigot-api/src/main/java/org/bukkit/event/enchantment/EnchantItemEvent.java new file mode 100644 index 0000000..de28f1d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/enchantment/EnchantItemEvent.java @@ -0,0 +1,121 @@ +package org.bukkit.event.enchantment; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.block.Block; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +/** + * Called when an ItemStack is successfully enchanted (currently at + * enchantment table) + */ +public class EnchantItemEvent extends InventoryEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Block table; + private final ItemStack item; + private int level; + private boolean cancelled; + private final Map enchants; + private final Player enchanter; + private int button; + + public EnchantItemEvent(final Player enchanter, final InventoryView view, final Block table, final ItemStack item, final int level, final Map enchants, final int i) { + super(view); + this.enchanter = enchanter; + this.table = table; + this.item = item; + this.level = level; + this.enchants = new HashMap(enchants); + this.cancelled = false; + this.button = i; + } + + /** + * Gets the player enchanting the item + * + * @return enchanting player + */ + public Player getEnchanter() { + return enchanter; + } + + /** + * Gets the block being used to enchant the item + * + * @return the block used for enchanting + */ + public Block getEnchantBlock() { + return table; + } + + /** + * Gets the item to be enchanted (can be modified) + * + * @return ItemStack of item + */ + public ItemStack getItem() { + return item; + } + + /** + * Get cost in exp levels of the enchantment + * + * @return experience level cost + */ + public int getExpLevelCost() { + return level; + } + + /** + * Set cost in exp levels of the enchantment + * + * @param level - cost in levels + */ + public void setExpLevelCost(int level) { + this.level = level; + } + + /** + * Get map of enchantment (levels, keyed by type) to be added to item + * (modify map returned to change values). Note: Any enchantments not + * allowed for the item will be ignored + * + * @return map of enchantment levels, keyed by enchantment + */ + public Map getEnchantsToAdd() { + return enchants; + } + + /** + * Which button was pressed to initiate the enchanting. + * + * @return The button index (0, 1, or 2). + */ + public int whichButton() { + return button; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/enchantment/PrepareItemEnchantEvent.java b/vspigot-api/src/main/java/org/bukkit/event/enchantment/PrepareItemEnchantEvent.java new file mode 100644 index 0000000..6c0aa9f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/enchantment/PrepareItemEnchantEvent.java @@ -0,0 +1,96 @@ +package org.bukkit.event.enchantment; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +/** + * Called when an ItemStack is inserted in an enchantment table - can be + * called multiple times + */ +public class PrepareItemEnchantEvent extends InventoryEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Block table; + private final ItemStack item; + private final int[] levelsOffered; + private final int bonus; + private boolean cancelled; + private final Player enchanter; + + public PrepareItemEnchantEvent(final Player enchanter, InventoryView view, final Block table, final ItemStack item, final int[] levelsOffered, final int bonus) { + super(view); + this.enchanter = enchanter; + this.table = table; + this.item = item; + this.levelsOffered = levelsOffered; + this.bonus = bonus; + this.cancelled = false; + } + + /** + * Gets the player enchanting the item + * + * @return enchanting player + */ + public Player getEnchanter() { + return enchanter; + } + + /** + * Gets the block being used to enchant the item + * + * @return the block used for enchanting + */ + public Block getEnchantBlock() { + return table; + } + + /** + * Gets the item to be enchanted (can be modified) + * + * @return ItemStack of item + */ + public ItemStack getItem() { + return item; + } + + /** + * Get list of offered exp level costs of the enchantment (modify values + * to change offer) + * + * @return experience level costs offered + */ + public int[] getExpLevelCostsOffered() { + return levelsOffered; + } + + /** + * Get enchantment bonus in effect - corresponds to number of bookshelves + * + * @return enchantment bonus + */ + public int getEnchantmentBonus() { + return bonus; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java new file mode 100644 index 0000000..8883157 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java @@ -0,0 +1,165 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.CreatureType; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; + +/** + * Called when a creature is spawned into a world. + *

          + * If a Creature Spawn event is cancelled, the creature will not spawn. + */ +public class CreatureSpawnEvent extends EntitySpawnEvent { + private final SpawnReason spawnReason; + + public CreatureSpawnEvent(final LivingEntity spawnee, final SpawnReason spawnReason) { + super(spawnee); + this.spawnReason = spawnReason; + } + + @Deprecated + public CreatureSpawnEvent(Entity spawnee, CreatureType type, Location loc, SpawnReason reason) { + super(spawnee); + spawnReason = reason; + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) entity; + } + + /** + * Gets the type of creature being spawned. + * + * @return A CreatureType value detailing the type of creature being + * spawned + * @deprecated In favour of {@link #getEntityType()}. + */ + @Deprecated + public CreatureType getCreatureType() { + return CreatureType.fromEntityType(getEntityType()); + } + + /** + * Gets the reason for why the creature is being spawned. + * + * @return A SpawnReason value detailing the reason for the creature being + * spawned + */ + public SpawnReason getSpawnReason() { + return spawnReason; + } + + /** + * An enum to specify the type of spawning + */ + public enum SpawnReason { + + /** + * When something spawns from natural means + */ + NATURAL, + /** + * When an entity spawns as a jockey of another entity (mostly spider + * jockeys) + */ + JOCKEY, + /** + * When a creature spawns due to chunk generation + */ + CHUNK_GEN, + /** + * When a creature spawns from a spawner + */ + SPAWNER, + /** + * When a creature spawns from an egg + */ + EGG, + /** + * When a creature spawns from a Spawner Egg + */ + SPAWNER_EGG, + /** + * When a creature spawns because of a lightning strike + */ + LIGHTNING, + /** + * When a creature is spawned by a player that is sleeping + * + * @deprecated No longer used + */ + @Deprecated + BED, + /** + * When a snowman is spawned by being built + */ + BUILD_SNOWMAN, + /** + * When an iron golem is spawned by being built + */ + BUILD_IRONGOLEM, + /** + * When a wither boss is spawned by being built + */ + BUILD_WITHER, + /** + * When an iron golem is spawned to defend a village + */ + VILLAGE_DEFENSE, + /** + * When a zombie is spawned to invade a village + */ + VILLAGE_INVASION, + /** + * When an animal breeds to create a child + */ + BREEDING, + /** + * When a slime splits + */ + SLIME_SPLIT, + /** + * When an entity calls for reinforcements + */ + REINFORCEMENTS, + /** + * When a creature is spawned by nether portal + */ + NETHER_PORTAL, + /** + * When a creature is spawned by a dispenser dispensing an egg + */ + DISPENSE_EGG, + /** + * When a zombie infects a villager + */ + INFECTION, + /** + * When a villager is cured from infection + */ + CURED, + /** + * When an ocelot has a baby spawned along with them + */ + OCELOT_BABY, + /** + * When a silverfish spawns from a block + */ + SILVERFISH_BLOCK, + /** + * When an entity spawns as a mount of another entity (mostly chicken + * jockeys) + */ + MOUNT, + /** + * When a creature is spawned by plugins + */ + CUSTOM, + /** + * When an entity is missing a SpawnReason + */ + DEFAULT + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/CreeperPowerEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/CreeperPowerEvent.java new file mode 100644 index 0000000..b103a6a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/CreeperPowerEvent.java @@ -0,0 +1,93 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Creeper; +import org.bukkit.entity.LightningStrike; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a Creeper is struck by lightning. + *

          + * If a Creeper Power event is cancelled, the Creeper will not be powered. + */ +public class CreeperPowerEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final PowerCause cause; + private LightningStrike bolt; + + public CreeperPowerEvent(final Creeper creeper, final LightningStrike bolt, final PowerCause cause) { + this(creeper, cause); + this.bolt = bolt; + } + + public CreeperPowerEvent(final Creeper creeper, final PowerCause cause) { + super(creeper); + this.cause = cause; + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + @Override + public Creeper getEntity() { + return (Creeper) entity; + } + + /** + * Gets the lightning bolt which is striking the Creeper. + * + * @return The Entity for the lightning bolt which is striking the Creeper + */ + public LightningStrike getLightning() { + return bolt; + } + + /** + * Gets the cause of the creeper being (un)powered. + * + * @return A PowerCause value detailing the cause of change in power. + */ + public PowerCause getCause() { + return cause; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * An enum to specify the cause of the change in power + */ + public enum PowerCause { + + /** + * Power change caused by a lightning bolt + *

          + * Powered state: true + */ + LIGHTNING, + /** + * Power change caused by something else (probably a plugin) + *

          + * Powered state: true + */ + SET_ON, + /** + * Power change caused by something else (probably a plugin) + *

          + * Powered state: false + */ + SET_OFF + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityBreakDoorEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityBreakDoorEvent.java new file mode 100644 index 0000000..2cbbc69 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityBreakDoorEvent.java @@ -0,0 +1,22 @@ +package org.bukkit.event.entity; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; + +/** + * Called when an {@link Entity} breaks a door + *

          + * Cancelling the event will cause the event to be delayed + */ +public class EntityBreakDoorEvent extends EntityChangeBlockEvent { + public EntityBreakDoorEvent(final LivingEntity entity, final Block targetBlock) { + super(entity, targetBlock, Material.AIR, (byte) 0); + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) entity; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityChangeBlockEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityChangeBlockEvent.java new file mode 100644 index 0000000..41be9ca --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityChangeBlockEvent.java @@ -0,0 +1,95 @@ +package org.bukkit.event.entity; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when any Entity, excluding players, changes a block. + */ +public class EntityChangeBlockEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Block block; + private boolean cancel; + private final Material to; + private final byte data; + + /** + * + * @param what the LivingEntity causing the change + * @param block the block (before the change) + * @param to the future material being changed to + * @deprecated Provided as a backward compatibility before the data byte + * was provided, and type increased to all entities + */ + @Deprecated + public EntityChangeBlockEvent(final LivingEntity what, final Block block, final Material to) { + this (what, block, to, (byte) 0); + } + + /** + * + * @param what the Entity causing the change + * @param block the block (before the change) + * @param to the future material being changed to + * @param data the future block data + * @deprecated Magic value + */ + @Deprecated + public EntityChangeBlockEvent(final Entity what, final Block block, final Material to, final byte data) { + super(what); + this.block = block; + this.cancel = false; + this.to = to; + this.data = data; + } + + /** + * Gets the block the entity is changing + * + * @return the block that is changing + */ + public Block getBlock() { + return block; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the Material that the block is changing into + * + * @return the material that the block is changing into + */ + public Material getTo() { + return to; + } + + /** + * Gets the data for the block that would be changed into + * + * @return the data for the block that would be changed into + * @deprecated Magic value + */ + @Deprecated + public byte getData() { + return data; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustByBlockEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustByBlockEvent.java new file mode 100644 index 0000000..c84bda9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustByBlockEvent.java @@ -0,0 +1,27 @@ +package org.bukkit.event.entity; + +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; + +/** + * Called when a block causes an entity to combust. + */ +public class EntityCombustByBlockEvent extends EntityCombustEvent { + private final Block combuster; + + public EntityCombustByBlockEvent(final Block combuster, final Entity combustee, final int duration) { + super(combustee, duration); + this.combuster = combuster; + } + + /** + * The combuster can be lava or a block that is on fire. + *

          + * WARNING: block may be null. + * + * @return the Block that set the combustee alight. + */ + public Block getCombuster() { + return combuster; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustByEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustByEntityEvent.java new file mode 100644 index 0000000..639567b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustByEntityEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; + +/** + * Called when an entity causes another entity to combust. + */ +public class EntityCombustByEntityEvent extends EntityCombustEvent { + private final Entity combuster; + + public EntityCombustByEntityEvent(final Entity combuster, final Entity combustee, final int duration) { + super(combustee, duration); + this.combuster = combuster; + } + + /** + * Get the entity that caused the combustion event. + * + * @return the Entity that set the combustee alight. + */ + public Entity getCombuster() { + return combuster; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustEvent.java new file mode 100644 index 0000000..43c4482 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCombustEvent.java @@ -0,0 +1,59 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when an entity combusts. + *

          + * If an Entity Combust event is cancelled, the entity will not combust. + */ +public class EntityCombustEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private int duration; + private boolean cancel; + + public EntityCombustEvent(final Entity combustee, final int duration) { + super(combustee); + this.duration = duration; + this.cancel = false; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * @return the amount of time (in seconds) the combustee should be alight + * for + */ + public int getDuration() { + return duration; + } + + /** + * The number of seconds the combustee should be alight for. + *

          + * This value will only ever increase the combustion time, not decrease + * existing combustion times. + * + * @param duration the time in seconds to be alight for. + */ + public void setDuration(int duration) { + this.duration = duration; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCreatePortalEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCreatePortalEvent.java new file mode 100644 index 0000000..286c206 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityCreatePortalEvent.java @@ -0,0 +1,65 @@ +package org.bukkit.event.entity; + +import java.util.List; +import org.bukkit.PortalType; +import org.bukkit.block.BlockState; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Thrown when a Living Entity creates a portal in a world. + */ +public class EntityCreatePortalEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final List blocks; + private boolean cancelled = false; + private PortalType type = PortalType.CUSTOM; + + public EntityCreatePortalEvent(final LivingEntity what, final List blocks, final PortalType type) { + super(what); + + this.blocks = blocks; + this.type = type; + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) entity; + } + + /** + * Gets a list of all blocks associated with the portal. + * + * @return List of blocks that will be changed. + */ + public List getBlocks() { + return blocks; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * Gets the type of portal that is trying to be created. + * + * @return Type of portal. + */ + public PortalType getPortalType() { + return type; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java new file mode 100644 index 0000000..2ff121e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java @@ -0,0 +1,39 @@ +package org.bukkit.event.entity; + +import java.util.Map; + +import com.google.common.base.Function; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; + +/** + * Called when an entity is damaged by a block + */ +public class EntityDamageByBlockEvent extends EntityDamageEvent { + private final Block damager; + + @Deprecated + public EntityDamageByBlockEvent(final Block damager, final Entity damagee, final DamageCause cause, final int damage) { + this(damager, damagee, cause, (double) damage); + } + + @Deprecated + public EntityDamageByBlockEvent(final Block damager, final Entity damagee, final DamageCause cause, final double damage) { + super(damagee, cause, damage); + this.damager = damager; + } + + public EntityDamageByBlockEvent(final Block damager, final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { + super(damagee, cause, modifiers, modifierFunctions); + this.damager = damager; + } + + /** + * Returns the block that damaged the player. + * + * @return Block that damaged the player + */ + public Block getDamager() { + return damager; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java new file mode 100644 index 0000000..49e74c3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java @@ -0,0 +1,38 @@ +package org.bukkit.event.entity; + +import java.util.Map; + +import com.google.common.base.Function; +import org.bukkit.entity.Entity; + +/** + * Called when an entity is damaged by an entity + */ +public class EntityDamageByEntityEvent extends EntityDamageEvent { + private final Entity damager; + + @Deprecated + public EntityDamageByEntityEvent(final Entity damager, final Entity damagee, final DamageCause cause, final int damage) { + this(damager, damagee, cause, (double) damage); + } + + @Deprecated + public EntityDamageByEntityEvent(final Entity damager, final Entity damagee, final DamageCause cause, final double damage) { + super(damagee, cause, damage); + this.damager = damager; + } + + public EntityDamageByEntityEvent(final Entity damager, final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { + super(damagee, cause, modifiers, modifierFunctions); + this.damager = damager; + } + + /** + * Returns the entity that damaged the defender. + * + * @return Entity that damaged the defender. + */ + public Entity getDamager() { + return damager; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java new file mode 100644 index 0000000..2cc0799 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java @@ -0,0 +1,410 @@ +package org.bukkit.event.entity; + +import java.util.EnumMap; +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.util.NumberConversions; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.ImmutableMap; + +/** + * Stores data for damage events + */ +public class EntityDamageEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private static final DamageModifier[] MODIFIERS = DamageModifier.values(); + private static final Function ZERO = Functions.constant(-0.0); + private final Map modifiers; + private final Map> modifierFunctions; + private final Map originals; + private boolean cancelled; + private final DamageCause cause; + + @Deprecated + public EntityDamageEvent(final Entity damagee, final DamageCause cause, final int damage) { + this(damagee, cause, (double) damage); + } + + @Deprecated + public EntityDamageEvent(final Entity damagee, final DamageCause cause, final double damage) { + this(damagee, cause, new EnumMap(ImmutableMap.of(DamageModifier.BASE, damage)), new EnumMap>(ImmutableMap.of(DamageModifier.BASE, ZERO))); + } + + public EntityDamageEvent(final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { + super(damagee); + Validate.isTrue(modifiers.containsKey(DamageModifier.BASE), "BASE DamageModifier missing"); + Validate.isTrue(!modifiers.containsKey(null), "Cannot have null DamageModifier"); + Validate.noNullElements(modifiers.values(), "Cannot have null modifier values"); + Validate.isTrue(modifiers.keySet().equals(modifierFunctions.keySet()), "Must have a modifier function for each DamageModifier"); + Validate.noNullElements(modifierFunctions.values(), "Cannot have null modifier function"); + this.originals = new EnumMap(modifiers); + this.cause = cause; + this.modifiers = modifiers; + this.modifierFunctions = modifierFunctions; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + /** + * Gets the original damage for the specified modifier, as defined at this + * event's construction. + * + * @param type the modifier + * @throws IllegalArgumentException if type is null + */ + public double getOriginalDamage(DamageModifier type) throws IllegalArgumentException { + final Double damage = originals.get(type); + if (damage != null) { + return damage; + } + if (type == null) { + throw new IllegalArgumentException("Cannot have null DamageModifier"); + } + return 0; + } + + /** + * Sets the damage for the specified modifier. + * + * @param damage the scalar value of the damage's modifier + * @see #getFinalDamage() + * @throws IllegalArgumentException if type is null + * @throws UnsupportedOperationException if the caller does not support + * the particular DamageModifier, or to rephrase, when {@link + * #isApplicable(DamageModifier)} returns false + */ + public void setDamage(DamageModifier type, double damage) throws IllegalArgumentException, UnsupportedOperationException { + if (!modifiers.containsKey(type)) { + throw type == null ? new IllegalArgumentException("Cannot have null DamageModifier") : new UnsupportedOperationException(type + " is not applicable to " + getEntity()); + } + modifiers.put(type, damage); + } + + /** + * Gets the damage change for some modifier + * + * @return The raw amount of damage caused by the event + * @throws IllegalArgumentException if type is null + * @see DamageModifier#BASE + */ + public double getDamage(DamageModifier type) throws IllegalArgumentException { + Validate.notNull(type, "Cannot have null DamageModifier"); + final Double damage = modifiers.get(type); + return damage == null ? 0 : damage; + } + + /** + * This checks to see if a particular modifier is valid for this event's + * caller, such that, {@link #setDamage(DamageModifier, double)} will not + * throw an {@link UnsupportedOperationException}. + *

          + * {@link DamageModifier#BASE} is always applicable. + * + * @param type the modifier + * @return true if the modifier is supported by the caller, false otherwise + * @throws IllegalArgumentException if type is null + */ + public boolean isApplicable(DamageModifier type) throws IllegalArgumentException { + Validate.notNull(type, "Cannot have null DamageModifier"); + return modifiers.containsKey(type); + } + + /** + * Gets the raw amount of damage caused by the event + * + * @return The raw amount of damage caused by the event + * @see DamageModifier#BASE + */ + public double getDamage() { + return getDamage(DamageModifier.BASE); + } + + /** + * Gets the amount of damage caused by the event after all damage + * reduction is applied. + * + * @return the amount of damage caused by the event + */ + public final double getFinalDamage() { + double damage = 0; + for (DamageModifier modifier : MODIFIERS) { + damage += getDamage(modifier); + } + return damage; + } + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public int _INVALID_getDamage() { + return NumberConversions.ceil(getDamage()); + } + + /** + * Sets the raw amount of damage caused by the event. + *

          + * For compatibility this also recalculates the modifiers and scales + * them by the difference between the modifier for the previous damage + * value and the new one. + * + * @param damage The raw amount of damage caused by the event + */ + public void setDamage(double damage) { + // These have to happen in the same order as the server calculates them, keep the enum sorted + double remaining = damage; + double oldRemaining = getDamage(DamageModifier.BASE); + for (DamageModifier modifier : MODIFIERS) { + if (!isApplicable(modifier)) { + continue; + } + + Function modifierFunction = modifierFunctions.get(modifier); + double newVanilla = modifierFunction.apply(remaining); + double oldVanilla = modifierFunction.apply(oldRemaining); + double difference = oldVanilla - newVanilla; + + // Don't allow value to cross zero, assume zero values should be negative + double old = getDamage(modifier); + if (old > 0) { + setDamage(modifier, Math.max(0, old - difference)); + } else { + setDamage(modifier, Math.min(0, old - difference)); + } + remaining += newVanilla; + oldRemaining += oldVanilla; + } + + setDamage(DamageModifier.BASE, damage); + } + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public void _INVALID_setDamage(int damage) { + setDamage(damage); + } + + /** + * Gets the cause of the damage. + * + * @return A DamageCause value detailing the cause of the damage. + */ + public DamageCause getCause() { + return cause; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * An enum to specify the types of modifier + */ + public enum DamageModifier { + /** + * This represents the amount of damage being done, also known as the + * raw {@link EntityDamageEvent#getDamage()}. + */ + BASE, + /** + * This represents the damage reduced by a wearing a helmet when hit + * by a falling block. + */ + HARD_HAT, + /** + * This represents the damage reduction caused by blocking, only present for + * {@link Player Players}. + */ + BLOCKING, + /** + * This represents the damage reduction caused by wearing armor. + */ + ARMOR, + /** + * This represents the damage reduction caused by the Resistance potion effect. + */ + RESISTANCE, + /** + * This represents the damage reduction caused by the combination of: + *

            + *
          • + * Armor enchantments + *
          • + * Witch's potion resistance + *
          • + *
          + */ + MAGIC, + /** + * This represents the damage reduction caused by the absorption potion + * effect. + */ + ABSORPTION, + ; + } + + /** + * An enum to specify the cause of the damage + */ + public enum DamageCause { + + /** + * Damage caused when an entity contacts a block such as a Cactus. + *

          + * Damage: 1 (Cactus) + */ + CONTACT, + /** + * Damage caused when an entity attacks another entity. + *

          + * Damage: variable + */ + ENTITY_ATTACK, + /** + * Damage caused when attacked by a projectile. + *

          + * Damage: variable + */ + PROJECTILE, + /** + * Damage caused by being put in a block + *

          + * Damage: 1 + */ + SUFFOCATION, + /** + * Damage caused when an entity falls a distance greater than 3 blocks + *

          + * Damage: fall height - 3.0 + */ + FALL, + /** + * Damage caused by direct exposure to fire + *

          + * Damage: 1 + */ + FIRE, + /** + * Damage caused due to burns caused by fire + *

          + * Damage: 1 + */ + FIRE_TICK, + /** + * Damage caused due to a snowman melting + *

          + * Damage: 1 + */ + MELTING, + /** + * Damage caused by direct exposure to lava + *

          + * Damage: 4 + */ + LAVA, + /** + * Damage caused by running out of air while in water + *

          + * Damage: 2 + */ + DROWNING, + /** + * Damage caused by being in the area when a block explodes. + *

          + * Damage: variable + */ + BLOCK_EXPLOSION, + /** + * Damage caused by being in the area when an entity, such as a + * Creeper, explodes. + *

          + * Damage: variable + */ + ENTITY_EXPLOSION, + /** + * Damage caused by falling into the void + *

          + * Damage: 4 for players + */ + VOID, + /** + * Damage caused by being struck by lightning + *

          + * Damage: 5 + */ + LIGHTNING, + /** + * Damage caused by committing suicide using the command "/kill" + *

          + * Damage: 1000 + */ + SUICIDE, + /** + * Damage caused by starving due to having an empty hunger bar + *

          + * Damage: 1 + */ + STARVATION, + /** + * Damage caused due to an ongoing poison effect + *

          + * Damage: 1 + */ + POISON, + /** + * Damage caused by being hit by a damage potion or spell + *

          + * Damage: variable + */ + MAGIC, + /** + * Damage caused by Wither potion effect + */ + WITHER, + /** + * Damage caused by being hit by a falling block which deals damage + *

          + * Note: Not every block deals damage + *

          + * Damage: variable + */ + FALLING_BLOCK, + /** + * Damage caused in retaliation to another attack by the Thorns + * enchantment. + *

          + * Damage: 1-4 (Thorns) + */ + THORNS, + /** + * Custom damage. + *

          + * Damage: variable + */ + CUSTOM + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java new file mode 100644 index 0000000..ab9e81f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java @@ -0,0 +1,72 @@ +package org.bukkit.event.entity; + +import java.util.List; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * Thrown whenever a LivingEntity dies + */ +public class EntityDeathEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + private final List drops; + private int dropExp = 0; + + public EntityDeathEvent(final LivingEntity entity, final List drops) { + this(entity, drops, 0); + } + + public EntityDeathEvent(final LivingEntity what, final List drops, final int droppedExp) { + super(what); + this.drops = drops; + this.dropExp = droppedExp; + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) entity; + } + + /** + * Gets how much EXP should be dropped from this death. + *

          + * This does not indicate how much EXP should be taken from the entity in + * question, merely how much should be created after its death. + * + * @return Amount of EXP to drop. + */ + public int getDroppedExp() { + return dropExp; + } + + /** + * Sets how much EXP should be dropped from this death. + *

          + * This does not indicate how much EXP should be taken from the entity in + * question, merely how much should be created after its death. + * + * @param exp Amount of EXP to drop. + */ + public void setDroppedExp(int exp) { + this.dropExp = exp; + } + + /** + * Gets all the items which will drop when the entity dies + * + * @return Items to drop when the entity dies + */ + public List getDrops() { + return drops; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityEvent.java new file mode 100644 index 0000000..c9a4ab3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityEvent.java @@ -0,0 +1,34 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.Event; + +/** + * Represents an Entity-related event + */ +public abstract class EntityEvent extends Event { + protected Entity entity; + + public EntityEvent(final Entity what) { + entity = what; + } + + /** + * Returns the Entity involved in this event + * + * @return Entity who is involved in this event + */ + public Entity getEntity() { + return entity; + } + + /** + * Gets the EntityType of the Entity involved in this event. + * + * @return EntityType of the Entity involved in this event + */ + public EntityType getEntityType() { + return entity.getType(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityExplodeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityExplodeEvent.java new file mode 100644 index 0000000..287035d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityExplodeEvent.java @@ -0,0 +1,85 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +import java.util.List; + +/** + * Called when an entity explodes + */ +public class EntityExplodeEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel; + private final Location location; + private final List blocks; + private float yield; + + public EntityExplodeEvent(final Entity what, final Location location, final List blocks, final float yield) { + super(what); + this.location = location; + this.blocks = blocks; + this.yield = yield; + this.cancel = false; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Returns the list of blocks that would have been removed or were removed + * from the explosion event. + * + * @return All blown-up blocks + */ + public List blockList() { + return blocks; + } + + /** + * Returns the location where the explosion happened. + *

          + * It is not possible to get this value from the Entity as the Entity no + * longer exists in the world. + * + * @return The location of the explosion + */ + public Location getLocation() { + return location; + } + + /** + * Returns the percentage of blocks to drop from this explosion + * + * @return The yield. + */ + public float getYield() { + return yield; + } + + /** + * Sets the percentage of blocks to drop from this explosion + * + * @param yield The new yield percentage + */ + public void setYield(float yield) { + this.yield = yield; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityInteractEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityInteractEvent.java new file mode 100644 index 0000000..1c4e100 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityInteractEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.entity; + +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when an entity interacts with an object + */ +public class EntityInteractEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + protected Block block; + private boolean cancelled; + + public EntityInteractEvent(final Entity entity, final Block block) { + super(entity); + this.block = block; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + /** + * Returns the involved block + * + * @return the block clicked with this item. + */ + public Block getBlock() { + return block; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalEnterEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalEnterEvent.java new file mode 100644 index 0000000..87d57b0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalEnterEvent.java @@ -0,0 +1,36 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.Location; +import org.bukkit.event.HandlerList; + +/** + * Called when an entity comes into contact with a portal + */ +public class EntityPortalEnterEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + private final Location location; + + public EntityPortalEnterEvent(final Entity entity, final Location location) { + super(entity); + this.location = location; + } + + /** + * Gets the portal block the entity is touching + * + * @return The portal block the entity is touching + */ + public Location getLocation() { + return location; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalEvent.java new file mode 100644 index 0000000..835c054 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalEvent.java @@ -0,0 +1,82 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.TravelAgent; +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; + +/** + * Called when a non-player entity is about to teleport because it is in + * contact with a portal. + *

          + * For players see {@link org.bukkit.event.player.PlayerPortalEvent} + */ +public class EntityPortalEvent extends EntityTeleportEvent { + private static final HandlerList handlers = new HandlerList(); + protected boolean useTravelAgent = true; + protected TravelAgent travelAgent; + + public EntityPortalEvent(final Entity entity, final Location from, final Location to, final TravelAgent pta) { + super(entity, from, to); + this.travelAgent = pta; + } + + /** + * Sets whether or not the Travel Agent will be used. + *

          + * If this is set to true, the TravelAgent will try to find a Portal at + * the {@link #getTo()} Location, and will try to create one if there is + * none. + *

          + * If this is set to false, the {@link #getEntity()} will only be + * teleported to the {@link #getTo()} Location. + * + * @param useTravelAgent whether to use the Travel Agent + */ + public void useTravelAgent(boolean useTravelAgent) { + this.useTravelAgent = useTravelAgent; + } + + /** + * Gets whether or not the Travel Agent will be used. + *

          + * If this is set to true, the TravelAgent will try to find a Portal at + * the {@link #getTo()} Location, and will try to create one if there is + * none. + *

          + * If this is set to false, the {@link #getEntity()} will only be + * teleported to the {@link #getTo()} Location. + * + * @return whether to use the Travel Agent + */ + public boolean useTravelAgent() { + return useTravelAgent; + } + + /** + * Gets the Travel Agent used (or not) in this event. + * + * @return the Travel Agent used (or not) in this event + */ + public TravelAgent getPortalTravelAgent() { + return this.travelAgent; + } + + /** + * Sets the Travel Agent used (or not) in this event. + * + * @param travelAgent the Travel Agent used (or not) in this event + */ + public void setPortalTravelAgent(TravelAgent travelAgent) { + this.travelAgent = travelAgent; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalExitEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalExitEvent.java new file mode 100644 index 0000000..682fe59 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityPortalExitEvent.java @@ -0,0 +1,60 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; +import org.bukkit.util.Vector; + +/** + * Called before an entity exits a portal. + *

          + * This event allows you to modify the velocity of the entity after they have + * successfully exited the portal. + */ +public class EntityPortalExitEvent extends EntityTeleportEvent { + private static final HandlerList handlers = new HandlerList(); + private Vector before; + private Vector after; + + public EntityPortalExitEvent(final Entity entity, final Location from, final Location to, final Vector before, final Vector after) { + super(entity, from, to); + this.before = before; + this.after = after; + } + + /** + * Gets a copy of the velocity that the entity has before entering the + * portal. + * + * @return velocity of entity before entering portal + */ + public Vector getBefore() { + return this.before.clone(); + } + + /** + * Gets a copy of the velocity that the entity will have after exiting the + * portal. + * + * @return velocity of entity after exiting portal + */ + public Vector getAfter() { + return this.after.clone(); + } + + /** + * Sets the velocity that the entity will have after exiting the portal. + */ + public void setAfter(Vector after) { + this.after = after.clone(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityRegainHealthEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityRegainHealthEvent.java new file mode 100644 index 0000000..b4291b0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityRegainHealthEvent.java @@ -0,0 +1,137 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.util.NumberConversions; + +/** + * Stores data for health-regain events + */ +public class EntityRegainHealthEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private double amount; + private final RegainReason regainReason; + + @Deprecated + public EntityRegainHealthEvent(final Entity entity, final int amount, final RegainReason regainReason) { + this(entity, (double) amount, regainReason); + } + + public EntityRegainHealthEvent(final Entity entity, final double amount, final RegainReason regainReason) { + super(entity); + this.amount = amount; + this.regainReason = regainReason; + } + + /** + * Gets the amount of regained health + * + * @return The amount of health regained + */ + public double getAmount() { + return amount; + } + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public int _INVALID_getAmount() { + return NumberConversions.ceil(getAmount()); + } + + /** + * Sets the amount of regained health + * + * @param amount the amount of health the entity will regain + */ + public void setAmount(double amount) { + this.amount = amount; + } + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public void _INVALID_setAmount(int amount) { + setAmount(amount); + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + /** + * Gets the reason for why the entity is regaining health + * + * @return A RegainReason detailing the reason for the entity regaining + * health + */ + public RegainReason getRegainReason() { + return regainReason; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * An enum to specify the type of health regaining that is occurring + */ + public enum RegainReason { + + /** + * When a player regains health from regenerating due to Peaceful mode + * (difficulty=0) + */ + REGEN, + /** + * When a player regains health from regenerating due to their hunger + * being satisfied + */ + SATIATED, + /** + * When a player regains health from eating consumables + */ + EATING, + /** + * When an ender dragon regains health from an ender crystal + */ + ENDER_CRYSTAL, + /** + * When a player is healed by a potion or spell + */ + MAGIC, + /** + * When a player is healed over time by a potion or spell + */ + MAGIC_REGEN, + /** + * When a wither is filling its health during spawning + */ + WITHER_SPAWN, + /** + * When an entity is damaged by the Wither potion effect + */ + WITHER, + /** + * Any other reason not covered by the reasons above + */ + CUSTOM + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityShootBowEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityShootBowEvent.java new file mode 100644 index 0000000..f8c91a1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityShootBowEvent.java @@ -0,0 +1,84 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * Called when a LivingEntity shoots a bow firing an arrow + */ +public class EntityShootBowEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final ItemStack bow; + private Entity projectile; + private final float force; + private boolean cancelled; + + public EntityShootBowEvent(final LivingEntity shooter, final ItemStack bow, final Projectile projectile, final float force) { + super(shooter); + this.bow = bow; + this.projectile = projectile; + this.force = force; + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) entity; + } + + /** + * Gets the bow ItemStack used to fire the arrow. + * + * @return the bow involved in this event + */ + public ItemStack getBow() { + return bow; + } + + /** + * Gets the projectile which will be launched by this event + * + * @return the launched projectile + */ + public Entity getProjectile() { + return projectile; + } + + /** + * Replaces the projectile which will be launched + * + * @param projectile the new projectile + */ + public void setProjectile(Entity projectile) { + this.projectile = projectile; + } + + /** + * Gets the force the arrow was launched with + * + * @return bow shooting force, up to 1.0 + */ + public float getForce() { + return force; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java new file mode 100644 index 0000000..5dcf98f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; + +/** + * Called when an entity is spawned into a world. + *

          + * If an Entity Spawn event is cancelled, the entity will not spawn. + */ +public class EntitySpawnEvent extends EntityEvent implements org.bukkit.event.Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + + public EntitySpawnEvent(final Entity spawnee) { + super(spawnee); + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + /** + * Gets the location at which the entity is spawning. + * + * @return The location at which the entity is spawning + */ + public Location getLocation() { + return getEntity().getLocation(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTameEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTameEvent.java new file mode 100644 index 0000000..f105817 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTameEvent.java @@ -0,0 +1,51 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Thrown when a LivingEntity is tamed + */ +public class EntityTameEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final AnimalTamer owner; + + public EntityTameEvent(final LivingEntity entity, final AnimalTamer owner) { + super(entity); + this.owner = owner; + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) entity; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + /** + * Gets the owning AnimalTamer + * + * @return the owning AnimalTamer + */ + public AnimalTamer getOwner() { + return owner; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTargetEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTargetEvent.java new file mode 100644 index 0000000..1f7af4d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTargetEvent.java @@ -0,0 +1,141 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a creature targets or untargets another entity + */ +public class EntityTargetEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private Entity target; + private final TargetReason reason; + + public EntityTargetEvent(final Entity entity, final Entity target, final TargetReason reason) { + super(entity); + this.target = target; + this.reason = reason; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Returns the reason for the targeting + * + * @return The reason + */ + public TargetReason getReason() { + return reason; + } + + /** + * Get the entity that this is targeting. + *

          + * This will be null in the case that the event is called when the mob + * forgets its target. + * + * @return The entity + */ + public Entity getTarget() { + return target; + } + + /** + * Set the entity that you want the mob to target instead. + *

          + * It is possible to be null, null will cause the entity to be + * target-less. + *

          + * This is different from cancelling the event. Cancelling the event will + * cause the entity to keep an original target, while setting to be null + * will cause the entity to be reset. + * + * @param target The entity to target + */ + public void setTarget(Entity target) { + this.target = target; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * An enum to specify the reason for the targeting + */ + public enum TargetReason { + + /** + * When the entity's target has died, and so it no longer targets it + */ + TARGET_DIED, + /** + * When the entity doesn't have a target, so it attacks the nearest + * player + */ + CLOSEST_PLAYER, + /** + * When the target attacks the entity, so entity targets it + */ + TARGET_ATTACKED_ENTITY, + /** + * When the target attacks a fellow pig zombie, so the whole group + * will target him with this reason. + */ + PIG_ZOMBIE_TARGET, + /** + * When the target is forgotten for whatever reason. + *

          + * Currently only occurs in with spiders when there is a high + * brightness. + */ + FORGOT_TARGET, + /** + * When the target attacks the owner of the entity, so the entity + * targets it. + */ + TARGET_ATTACKED_OWNER, + /** + * When the owner of the entity attacks the target attacks, so the + * entity targets it. + */ + OWNER_ATTACKED_TARGET, + /** + * When the entity has no target, so the entity randomly chooses one. + */ + RANDOM_TARGET, + /** + * When an entity selects a target while defending a village. + */ + DEFEND_VILLAGE, + /** + * When the target attacks a nearby entity of the same type, so the entity targets it + */ + TARGET_ATTACKED_NEARBY_ENTITY, + /** + * When a zombie targeting an entity summons reinforcements, so the reinforcements target the same entity + */ + REINFORCEMENT_TARGET, + /** + * When an entity targets another entity after colliding with it. + */ + COLLISION, + /** + * For custom calls to the event. + */ + CUSTOM + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTargetLivingEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTargetLivingEntityEvent.java new file mode 100644 index 0000000..cd9aea1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTargetLivingEntityEvent.java @@ -0,0 +1,34 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; + +/** + * Called when an Entity targets a {@link LivingEntity} and can only target + * LivingEntity's. + */ +public class EntityTargetLivingEntityEvent extends EntityTargetEvent{ + public EntityTargetLivingEntityEvent(final Entity entity, final LivingEntity target, final TargetReason reason) { + super(entity, target, reason); + } + + public LivingEntity getTarget() { + return (LivingEntity) super.getTarget(); + } + + /** + * Set the Entity that you want the mob to target. + *

          + * It is possible to be null, null will cause the entity to be + * target-less. + *

          + * Must be a LivingEntity, or null. + * + * @param target The entity to target + */ + public void setTarget(Entity target) { + if (target == null || target instanceof LivingEntity) { + super.setTarget(target); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTeleportEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTeleportEvent.java new file mode 100644 index 0000000..619f8d4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityTeleportEvent.java @@ -0,0 +1,77 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Thrown when a non-player entity (such as an Enderman) tries to teleport + * from one location to another. + */ +public class EntityTeleportEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel; + private Location from; + private Location to; + + public EntityTeleportEvent(Entity what, Location from, Location to) { + super(what); + this.from = from; + this.to = to; + this.cancel = false; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the location that this entity moved from + * + * @return Location this entity moved from + */ + public Location getFrom() { + return from; + } + + /** + * Sets the location that this entity moved from + * + * @param from New location this entity moved from + */ + public void setFrom(Location from) { + this.from = from; + } + + /** + * Gets the location that this entity moved to + * + * @return Location the entity moved to + */ + public Location getTo() { + return to; + } + + /** + * Sets the location that this entity moved to + * + * @param to New Location this entity moved to + */ + public void setTo(Location to) { + this.to = to; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/EntityUnleashEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityUnleashEvent.java new file mode 100644 index 0000000..da7e46c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/EntityUnleashEvent.java @@ -0,0 +1,52 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; + +/** + * Called immediately prior to an entity being unleashed. + */ +public class EntityUnleashEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + private final UnleashReason reason; + + public EntityUnleashEvent(Entity entity, UnleashReason reason) { + super(entity); + this.reason = reason; + } + + /** + * Returns the reason for the unleashing. + * + * @return The reason + */ + public UnleashReason getReason() { + return reason; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public enum UnleashReason { + /** + * When the entity's leashholder has died or logged out, and so is + * unleashed + */ + HOLDER_GONE, + /** + * When the entity's leashholder attempts to unleash it + */ + PLAYER_UNLEASH, + /** + * When the entity's leashholder is more than 10 blocks away + */ + DISTANCE, + UNKNOWN; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/ExpBottleEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/ExpBottleEvent.java new file mode 100644 index 0000000..4f64424 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/ExpBottleEvent.java @@ -0,0 +1,75 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.ThrownExpBottle; +import org.bukkit.event.HandlerList; + +/** + * Called when a ThrownExpBottle hits and releases experience. + */ +public class ExpBottleEvent extends ProjectileHitEvent { + private static final HandlerList handlers = new HandlerList(); + private int exp; + private boolean showEffect = true; + + public ExpBottleEvent(final ThrownExpBottle bottle, final int exp) { + super(bottle); + this.exp = exp; + } + + @Override + public ThrownExpBottle getEntity() { + return (ThrownExpBottle) entity; + } + + /** + * This method indicates if the particle effect should be shown. + * + * @return true if the effect will be shown, false otherwise + */ + public boolean getShowEffect() { + return this.showEffect; + } + + /** + * This method sets if the particle effect will be shown. + *

          + * This does not change the experience created. + * + * @param showEffect true indicates the effect will be shown, false + * indicates no effect will be shown + */ + public void setShowEffect(final boolean showEffect) { + this.showEffect = showEffect; + } + + /** + * This method retrieves the amount of experience to be created. + *

          + * The number indicates a total amount to be divided into orbs. + * + * @return the total amount of experience to be created + */ + public int getExperience() { + return exp; + } + + /** + * This method sets the amount of experience to be created. + *

          + * The number indicates a total amount to be divided into orbs. + * + * @param exp the total amount of experience to be created + */ + public void setExperience(final int exp) { + this.exp = exp; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/ExplosionPrimeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/ExplosionPrimeEvent.java new file mode 100644 index 0000000..7ca6a55 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/ExplosionPrimeEvent.java @@ -0,0 +1,80 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Explosive; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when an entity has made a decision to explode. + */ +public class ExplosionPrimeEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel; + private float radius; + private boolean fire; + + public ExplosionPrimeEvent(final Entity what, final float radius, final boolean fire) { + super(what); + this.cancel = false; + this.radius = radius; + this.fire = fire; + } + + public ExplosionPrimeEvent(final Explosive explosive) { + this(explosive, explosive.getYield(), explosive.isIncendiary()); + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the radius of the explosion + * + * @return returns the radius of the explosion + */ + public float getRadius() { + return radius; + } + + /** + * Sets the radius of the explosion + * + * @param radius the radius of the explosion + */ + public void setRadius(float radius) { + this.radius = radius; + } + + /** + * Gets whether this explosion will create fire or not + * + * @return true if this explosion will create fire + */ + public boolean getFire() { + return fire; + } + + /** + * Sets whether this explosion will create fire or not + * + * @param fire true if you want this explosion to create fire + */ + public void setFire(boolean fire) { + this.fire = fire; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/FoodLevelChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/FoodLevelChangeEvent.java new file mode 100644 index 0000000..f6e2472 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/FoodLevelChangeEvent.java @@ -0,0 +1,67 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a human entity's food level changes + */ +public class FoodLevelChangeEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private int level; + + public FoodLevelChangeEvent(final HumanEntity what, final int level) { + super(what); + this.level = level; + } + + @Override + public HumanEntity getEntity() { + return (HumanEntity) entity; + } + + /** + * Gets the resultant food level that the entity involved in this event + * should be set to. + *

          + * Where 20 is a full food bar and 0 is an empty one. + * + * @return The resultant food level + */ + public int getFoodLevel() { + return level; + } + + /** + * Sets the resultant food level that the entity involved in this event + * should be set to + * + * @param level the resultant food level that the entity involved in this + * event should be set to + */ + public void setFoodLevel(int level) { + if (level > 20) level = 20; + else if (level < 0) level = 0; + + this.level = level; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/HorseJumpEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/HorseJumpEvent.java new file mode 100644 index 0000000..fad2468 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/HorseJumpEvent.java @@ -0,0 +1,78 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Horse; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a horse jumps. + */ +public class HorseJumpEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private float power; + + public HorseJumpEvent(final Horse horse, final float power) { + super(horse); + this.power = power; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public Horse getEntity() { + return (Horse) entity; + } + + /** + * Gets the power of the jump. + *

          + * Power is a value that defines how much of the horse's jump strength + * should be used for the jump. Power is effectively multiplied times + * the horse's jump strength to determine how high the jump is; 0 + * represents no jump strength while 1 represents full jump strength. + * Setting power to a value above 1 will use additional jump strength + * that the horse does not usually have. + *

          + * Power does not affect how high the horse is capable of jumping, only + * how much of its jumping capability will be used in this jump. To set + * the horse's overall jump strength, see {@link + * Horse#setJumpStrength(double)}. + * + * @return jump strength + */ + public float getPower() { + return power; + } + + /** + * Sets the power of the jump. + *

          + * Jump power can be set to a value above 1.0 which will increase the + * strength of this jump above the horse's actual jump strength. + *

          + * Setting the jump power to 0 will result in the jump animation still + * playing, but the horse not leaving the ground. Only canceling this + * event will result in no jump animation at all. + * + * @param power power of the jump + */ + public void setPower(float power) { + this.power = power; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/ItemDespawnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/ItemDespawnEvent.java new file mode 100644 index 0000000..356e4bd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/ItemDespawnEvent.java @@ -0,0 +1,55 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Item; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * This event is called when a {@link org.bukkit.entity.Item} is removed from + * the world because it has existed for 5 minutes. + *

          + * Cancelling the event results in the item being allowed to exist for 5 more + * minutes. This behavior is not guaranteed and may change in future versions. + */ +public class ItemDespawnEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final Location location; + + public ItemDespawnEvent(final Item despawnee, final Location loc) { + super(despawnee); + location = loc; + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + @Override + public Item getEntity() { + return (Item) entity; + } + + /** + * Gets the location at which the item is despawning. + * + * @return The location at which the item is despawning + */ + public Location getLocation() { + return location; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/ItemSpawnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/ItemSpawnEvent.java new file mode 100644 index 0000000..776f8e7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/ItemSpawnEvent.java @@ -0,0 +1,23 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Item; + +/** + * Called when an item is spawned into a world + */ +public class ItemSpawnEvent extends EntitySpawnEvent { + public ItemSpawnEvent(final Item spawnee) { + super(spawnee); + } + + @Deprecated + public ItemSpawnEvent(final Item spawnee, final Location loc) { + this(spawnee); + } + + @Override + public Item getEntity() { + return (Item) entity; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PigZapEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PigZapEvent.java new file mode 100644 index 0000000..aa80ebf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PigZapEvent.java @@ -0,0 +1,64 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.LightningStrike; +import org.bukkit.entity.Pig; +import org.bukkit.entity.PigZombie; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Stores data for pigs being zapped + */ +public class PigZapEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final PigZombie pigzombie; + private final LightningStrike bolt; + + public PigZapEvent(final Pig pig, final LightningStrike bolt, final PigZombie pigzombie) { + super(pig); + this.bolt = bolt; + this.pigzombie = pigzombie; + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + @Override + public Pig getEntity() { + return (Pig) entity; + } + + /** + * Gets the bolt which is striking the pig. + * + * @return lightning entity + */ + public LightningStrike getLightning() { + return bolt; + } + + /** + * Gets the zombie pig that will replace the pig, provided the event is + * not cancelled first. + * + * @return resulting entity + */ + public PigZombie getPigZombie() { + return pigzombie; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java new file mode 100644 index 0000000..6c9b794 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java @@ -0,0 +1,157 @@ +package org.bukkit.event.entity; + +import java.util.List; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +/** + * Thrown whenever a {@link Player} dies + */ +public class PlayerDeathEvent extends EntityDeathEvent { + private int newExp = 0; + private String deathMessage = ""; + private int newLevel = 0; + private int newTotalExp = 0; + private boolean keepLevel = false; + private boolean keepInventory = false; + + public PlayerDeathEvent(final Player player, final List drops, final int droppedExp, final String deathMessage) { + this(player, drops, droppedExp, 0, deathMessage); + } + + public PlayerDeathEvent(final Player player, final List drops, final int droppedExp, final int newExp, final String deathMessage) { + this(player, drops, droppedExp, newExp, 0, 0, deathMessage); + } + + public PlayerDeathEvent(final Player player, final List drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, final String deathMessage) { + super(player, drops, droppedExp); + this.newExp = newExp; + this.newTotalExp = newTotalExp; + this.newLevel = newLevel; + this.deathMessage = deathMessage; + } + + @Override + public Player getEntity() { + return (Player) entity; + } + + /** + * Set the death message that will appear to everyone on the server. + * + * @param deathMessage Message to appear to other players on the server. + */ + public void setDeathMessage(String deathMessage) { + this.deathMessage = deathMessage; + } + + /** + * Get the death message that will appear to everyone on the server. + * + * @return Message to appear to other players on the server. + */ + public String getDeathMessage() { + return deathMessage; + } + + /** + * Gets how much EXP the Player should have at respawn. + *

          + * This does not indicate how much EXP should be dropped, please see + * {@link #getDroppedExp()} for that. + * + * @return New EXP of the respawned player + */ + public int getNewExp() { + return newExp; + } + + /** + * Sets how much EXP the Player should have at respawn. + *

          + * This does not indicate how much EXP should be dropped, please see + * {@link #setDroppedExp(int)} for that. + * + * @param exp New EXP of the respawned player + */ + public void setNewExp(int exp) { + newExp = exp; + } + + /** + * Gets the Level the Player should have at respawn. + * + * @return New Level of the respawned player + */ + public int getNewLevel() { + return newLevel; + } + + /** + * Sets the Level the Player should have at respawn. + * + * @param level New Level of the respawned player + */ + public void setNewLevel(int level) { + newLevel = level; + } + + /** + * Gets the Total EXP the Player should have at respawn. + * + * @return New Total EXP of the respawned player + */ + public int getNewTotalExp() { + return newTotalExp; + } + + /** + * Sets the Total EXP the Player should have at respawn. + * + * @param totalExp New Total EXP of the respawned player + */ + public void setNewTotalExp(int totalExp) { + newTotalExp = totalExp; + } + + /** + * Gets if the Player should keep all EXP at respawn. + *

          + * This flag overrides other EXP settings + * + * @return True if Player should keep all pre-death exp + */ + public boolean getKeepLevel() { + return keepLevel; + } + + /** + * Sets if the Player should keep all EXP at respawn. + *

          + * This overrides all other EXP settings + * + * @param keepLevel True to keep all current value levels + */ + public void setKeepLevel(boolean keepLevel) { + this.keepLevel = keepLevel; + } + + /** + * Sets if the Player keeps inventory on death. + * + * @param keepInventory True to keep the inventory + */ + public void setKeepInventory(boolean keepInventory) { + this.keepInventory = keepInventory; + } + + /** + * Gets if the Player keeps inventory on death. + * + * @return True if the player keeps inventory on death + */ + public boolean getKeepInventory() { + return keepInventory; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PlayerLeashEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PlayerLeashEntityEvent.java new file mode 100644 index 0000000..74d458a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PlayerLeashEntityEvent.java @@ -0,0 +1,68 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * Called immediately prior to a creature being leashed by a player. + */ +public class PlayerLeashEntityEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Entity leashHolder; + private final Entity entity; + private boolean cancelled = false; + private final Player player; + + public PlayerLeashEntityEvent(Entity what, Entity leashHolder, Player leasher) { + this.leashHolder = leashHolder; + this.entity = what; + this.player = leasher; + } + + /** + * Returns the entity that is holding the leash. + * + * @return The leash holder + */ + public Entity getLeashHolder() { + return leashHolder; + } + + /** + * Returns the entity being leashed. + * + * @return The entity + */ + public Entity getEntity() { + return entity; + } + + /** + * Returns the player involved in this event + * + * @return Player who is involved in this event + */ + public final Player getPlayer() { + return player; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public boolean isCancelled() { + return this.cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectAddEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectAddEvent.java new file mode 100644 index 0000000..998d6ca --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectAddEvent.java @@ -0,0 +1,77 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.potion.PotionEffect; + +/** + * Called when a potion effect is applied to an entity, or an existing effect is extended or upgraded + */ +public class PotionEffectAddEvent extends PotionEffectEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private boolean cancelled; + protected final EffectCause effectCause; + + public PotionEffectAddEvent(LivingEntity entity, PotionEffect effect, EffectCause effectCause) { + super(entity, effect); + this.effectCause = effectCause; + } + + public EffectCause getCause() { + return effectCause; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * The cause of receiving a potion effect + */ + public enum EffectCause { + + /** + * Indicates the effect was caused by the proximity of a potion effect splash + */ + POTION_SPLASH, + /** + * Indicates the effect was caused by the proximity of a beacon + */ + BEACON, + /** + * Indicates the effect was caused by damage from a wither skeleton + */ + WITHER_SKELETON, + /** + * Indicates the effect was caused by damage from a wither skull + */ + WITHER_SKULL, + /** + * Indicates the effect was caused by a plugin + */ + PLUGIN, + /** + * Indicates the effect was caused by an event not covered by + * this enum + */ + UNKNOWN + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectEvent.java new file mode 100644 index 0000000..6510a62 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectEvent.java @@ -0,0 +1,23 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffect; + +public abstract class PotionEffectEvent extends EntityEvent { + + private final PotionEffect effect; + + public PotionEffectEvent(LivingEntity what, PotionEffect effect) { + super(what); + this.effect = effect; + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) super.getEntity(); + } + + public PotionEffect getEffect() { + return effect; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectExpireEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectExpireEvent.java new file mode 100644 index 0000000..e8d3710 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectExpireEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffect; + +/** + * Called when a potion effect on an entity runs out. Cancelling the event extends + * the effect with a practically infinite duration. The new duration can also be set + * explicitly by calling {@link #setDuration}. + * + * Handlers of {@link PotionEffectRemoveEvent} will also receive this event. + */ +public class PotionEffectExpireEvent extends PotionEffectRemoveEvent { + + private int duration = 0; + + public PotionEffectExpireEvent(LivingEntity entity, PotionEffect effect) { + super(entity, effect); + } + + /** + * Get the new duration for the potion effect. This is initially 0. + */ + public int getDuration() { + return duration; + } + + /** + * Set a new duration for the potion effect. Passing 0 to this method un-cancels + * the event, and passing anything above 0 cancels it. + */ + public void setDuration(int duration) { + this.duration = Math.max(0, duration); + } + + @Override + public boolean isCancelled() { + return duration > 0; + } + + @Override + public void setCancelled(boolean cancel) { + this.duration = cancel ? Integer.MAX_VALUE : 0; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectExtendEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectExtendEvent.java new file mode 100644 index 0000000..1292c2f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectExtendEvent.java @@ -0,0 +1,26 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffect; + +/** + * Called when an entity's active potion effect is extended or upgraded. + * + * Handlers of {@link PotionEffectAddEvent} will also receive this event. + */ +public class PotionEffectExtendEvent extends PotionEffectAddEvent { + + private final PotionEffect oldEffect; + + public PotionEffectExtendEvent(LivingEntity entity, PotionEffect effect, PotionEffect oldEffect, EffectCause effectCause) { + super(entity, effect, effectCause); + this.oldEffect = oldEffect; + } + + /** + * Get the state of the potion effect prior to the change + */ + public PotionEffect getOldEffect() { + return oldEffect; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectRemoveEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectRemoveEvent.java new file mode 100644 index 0000000..0622dd0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionEffectRemoveEvent.java @@ -0,0 +1,39 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.potion.PotionEffect; + +/** + * Called when a potion effect is removed from an entity for whatever reason + */ +public class PotionEffectRemoveEvent extends PotionEffectEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private boolean cancelled; + + public PotionEffectRemoveEvent(LivingEntity entity, PotionEffect effect) { + super(entity, effect); + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/PotionSplashEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionSplashEvent.java new file mode 100644 index 0000000..b9840de --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/PotionSplashEvent.java @@ -0,0 +1,94 @@ +package org.bukkit.event.entity; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a splash potion hits an area + */ +public class PotionSplashEvent extends ProjectileHitEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Map affectedEntities; + + public PotionSplashEvent(final ThrownPotion potion, final Map affectedEntities) { + super(potion); + + this.affectedEntities = affectedEntities; + } + + @Override + public ThrownPotion getEntity() { + return (ThrownPotion) entity; + } + + /** + * Gets the potion which caused this event + * + * @return The thrown potion entity + */ + public ThrownPotion getPotion() { + return (ThrownPotion) getEntity(); + } + + /** + * Retrieves a list of all effected entities + * + * @return A fresh copy of the affected entity list + */ + public Collection getAffectedEntities() { + return new ArrayList(affectedEntities.keySet()); + } + + /** + * Gets the intensity of the potion's effects for given entity; This + * depends on the distance to the impact center + * + * @param entity Which entity to get intensity for + * @return intensity relative to maximum effect; 0.0: not affected; 1.0: + * fully hit by potion effects + */ + public double getIntensity(LivingEntity entity) { + Double intensity = affectedEntities.get(entity); + return intensity != null ? intensity : 0.0; + } + + /** + * Overwrites the intensity for a given entity + * + * @param entity For which entity to define a new intensity + * @param intensity relative to maximum effect + */ + public void setIntensity(LivingEntity entity, double intensity) { + Validate.notNull(entity, "You must specify a valid entity."); + if (intensity <= 0.0) { + affectedEntities.remove(entity); + } else { + affectedEntities.put(entity, Math.min(intensity, 1.0)); + } + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/ProjectileHitEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/ProjectileHitEvent.java new file mode 100644 index 0000000..25ae832 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/ProjectileHitEvent.java @@ -0,0 +1,30 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Projectile; +import org.bukkit.event.HandlerList; + +/** + * Called when a projectile hits an object + */ +public class ProjectileHitEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + + public ProjectileHitEvent(final Projectile projectile) { + super(projectile); + } + + @Override + public Projectile getEntity() { + return (Projectile) entity; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/ProjectileLaunchEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/ProjectileLaunchEvent.java new file mode 100644 index 0000000..0c9190c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/ProjectileLaunchEvent.java @@ -0,0 +1,40 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a projectile is launched. + */ +public class ProjectileLaunchEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + + public ProjectileLaunchEvent(Entity what) { + super(what); + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public Projectile getEntity() { + return (Projectile) entity; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/SheepDyeWoolEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/SheepDyeWoolEvent.java new file mode 100644 index 0000000..4c17fea --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/SheepDyeWoolEvent.java @@ -0,0 +1,62 @@ +package org.bukkit.event.entity; + +import org.bukkit.DyeColor; +import org.bukkit.entity.Sheep; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a sheep's wool is dyed + */ +public class SheepDyeWoolEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel; + private DyeColor color; + + public SheepDyeWoolEvent(final Sheep sheep, final DyeColor color) { + super(sheep); + this.cancel = false; + this.color = color; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public Sheep getEntity() { + return (Sheep) entity; + } + + /** + * Gets the DyeColor the sheep is being dyed + * + * @return the DyeColor the sheep is being dyed + */ + public DyeColor getColor() { + return color; + } + + /** + * Sets the DyeColor the sheep is being dyed + * + * @param color the DyeColor the sheep will be dyed + */ + public void setColor(DyeColor color) { + this.color = color; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/SheepRegrowWoolEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/SheepRegrowWoolEvent.java new file mode 100644 index 0000000..e836f7b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/SheepRegrowWoolEvent.java @@ -0,0 +1,41 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Sheep; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a sheep regrows its wool + */ +public class SheepRegrowWoolEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel; + + public SheepRegrowWoolEvent(final Sheep sheep) { + super(sheep); + this.cancel = false; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public Sheep getEntity() { + return (Sheep) entity; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/SlimeSplitEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/SlimeSplitEvent.java new file mode 100644 index 0000000..4b99587 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/SlimeSplitEvent.java @@ -0,0 +1,59 @@ +package org.bukkit.event.entity; + +import org.bukkit.entity.Slime; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a Slime splits into smaller Slimes upon death + */ +public class SlimeSplitEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private int count; + + public SlimeSplitEvent(final Slime slime, final int count) { + super(slime); + this.count = count; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public Slime getEntity() { + return (Slime) entity; + } + + /** + * Gets the amount of smaller slimes to spawn + * + * @return the amount of slimes to spawn + */ + public int getCount() { + return count; + } + + /** + * Sets how many smaller slimes will spawn on the split + * + * @param count the amount of slimes to spawn + */ + public void setCount(int count) { + this.count = count; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/entity/SpawnerSpawnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/entity/SpawnerSpawnEvent.java new file mode 100644 index 0000000..1acb3c4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/entity/SpawnerSpawnEvent.java @@ -0,0 +1,22 @@ +package org.bukkit.event.entity; + +import org.bukkit.block.CreatureSpawner; +import org.bukkit.entity.Entity; + +/** + * Called when an entity is spawned into a world by a spawner. + *

          + * If a Spawner Spawn event is cancelled, the entity will not spawn. + */ +public class SpawnerSpawnEvent extends EntitySpawnEvent { + private final CreatureSpawner spawner; + + public SpawnerSpawnEvent(final Entity spawnee, final CreatureSpawner spawner) { + super(spawnee); + this.spawner = spawner; + } + + public CreatureSpawner getSpawner() { + return spawner; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingBreakByEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingBreakByEntityEvent.java new file mode 100644 index 0000000..80851ed --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingBreakByEntityEvent.java @@ -0,0 +1,25 @@ +package org.bukkit.event.hanging; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Hanging; + +/** + * Triggered when a hanging entity is removed by an entity + */ +public class HangingBreakByEntityEvent extends HangingBreakEvent { + private final Entity remover; + + public HangingBreakByEntityEvent(final Hanging hanging, final Entity remover) { + super(hanging, HangingBreakEvent.RemoveCause.ENTITY); + this.remover = remover; + } + + /** + * Gets the entity that removed the hanging entity + * + * @return the entity that removed the hanging entity + */ + public Entity getRemover() { + return remover; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingBreakEvent.java b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingBreakEvent.java new file mode 100644 index 0000000..87bbdcb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingBreakEvent.java @@ -0,0 +1,71 @@ +package org.bukkit.event.hanging; + +import org.bukkit.entity.Hanging; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Triggered when a hanging entity is removed + */ +public class HangingBreakEvent extends HangingEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final HangingBreakEvent.RemoveCause cause; + + public HangingBreakEvent(final Hanging hanging, final HangingBreakEvent.RemoveCause cause) { + super(hanging); + this.cause = cause; + } + + /** + * Gets the cause for the hanging entity's removal + * + * @return the RemoveCause for the hanging entity's removal + */ + public HangingBreakEvent.RemoveCause getCause() { + return cause; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * An enum to specify the cause of the removal + */ + public enum RemoveCause { + /** + * Removed by an entity + */ + ENTITY, + /** + * Removed by an explosion + */ + EXPLOSION, + /** + * Removed by placing a block on it + */ + OBSTRUCTION, + /** + * Removed by destroying the block behind it, etc + */ + PHYSICS, + /** + * Removed by an uncategorised cause + */ + DEFAULT, + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingEvent.java b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingEvent.java new file mode 100644 index 0000000..b370afe --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.hanging; + +import org.bukkit.entity.Hanging; +import org.bukkit.event.Event; + +/** + * Represents a hanging entity-related event. + */ +public abstract class HangingEvent extends Event { + protected Hanging hanging; + + protected HangingEvent(final Hanging painting) { + this.hanging = painting; + } + + /** + * Gets the hanging entity involved in this event. + * + * @return the hanging entity + */ + public Hanging getEntity() { + return hanging; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingPlaceEvent.java b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingPlaceEvent.java new file mode 100644 index 0000000..b511c55 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/hanging/HangingPlaceEvent.java @@ -0,0 +1,70 @@ +package org.bukkit.event.hanging; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Triggered when a hanging entity is created in the world + */ +public class HangingPlaceEvent extends HangingEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Player player; + private final Block block; + private final BlockFace blockFace; + + public HangingPlaceEvent(final Hanging hanging, final Player player, final Block block, final BlockFace blockFace) { + super(hanging); + this.player = player; + this.block = block; + this.blockFace = blockFace; + } + + /** + * Returns the player placing the hanging entity + * + * @return the player placing the hanging entity + */ + public Player getPlayer() { + return player; + } + + /** + * Returns the block that the hanging entity was placed on + * + * @return the block that the hanging entity was placed on + */ + public Block getBlock() { + return block; + } + + /** + * Returns the face of the block that the hanging entity was placed on + * + * @return the face of the block that the hanging entity was placed on + */ + public BlockFace getBlockFace() { + return blockFace; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/BrewEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/BrewEvent.java new file mode 100644 index 0000000..c2a4229 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/BrewEvent.java @@ -0,0 +1,51 @@ +package org.bukkit.event.inventory; + +import org.bukkit.event.block.*; +import org.bukkit.event.*; +import org.bukkit.inventory.*; +import org.bukkit.block.*; + +public class BrewEvent extends BlockEvent implements Cancellable +{ + private static final HandlerList handlers; + private BrewerInventory contents; + private final ItemStack[] results; + private boolean cancelled; + + public BrewEvent(final Block brewer, final BrewerInventory contents, final ItemStack[] results) { + super(brewer); + this.contents = contents; + this.results = results; + } + + public BrewerInventory getContents() { + return this.contents; + } + + public ItemStack[] getResults() { + return this.results; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(final boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return BrewEvent.handlers; + } + + public static HandlerList getHandlerList() { + return BrewEvent.handlers; + } + + static { + handlers = new HandlerList(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/ClickType.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/ClickType.java new file mode 100644 index 0000000..a7440aa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/ClickType.java @@ -0,0 +1,115 @@ +package org.bukkit.event.inventory; + +/** + * What the client did to trigger this action (not the result). + */ +public enum ClickType { + + /** + * The left (or primary) mouse button. + */ + LEFT, + /** + * Holding shift while pressing the left mouse button. + */ + SHIFT_LEFT, + /** + * The right mouse button. + */ + RIGHT, + /** + * Holding shift while pressing the right mouse button. + */ + SHIFT_RIGHT, + /** + * Clicking the left mouse button on the grey area around the inventory. + */ + WINDOW_BORDER_LEFT, + /** + * Clicking the right mouse button on the grey area around the inventory. + */ + WINDOW_BORDER_RIGHT, + /** + * The middle mouse button, or a "scrollwheel click". + */ + MIDDLE, + /** + * One of the number keys 1-9, correspond to slots on the hotbar. + */ + NUMBER_KEY, + /** + * Pressing the left mouse button twice in quick succession. + */ + DOUBLE_CLICK, + /** + * The "Drop" key (defaults to Q). + */ + DROP, + /** + * Holding Ctrl while pressing the "Drop" key (defaults to Q). + */ + CONTROL_DROP, + /** + * Any action done with the Creative inventory open. + */ + CREATIVE, + /** + * A type of inventory manipulation not yet recognized by Bukkit. + *

          + * This is only for transitional purposes on a new Minecraft update, and + * should never be relied upon. + *

          + * Any ClickType.UNKNOWN is called on a best-effort basis. + */ + UNKNOWN, + ; + + /** + * Gets whether this ClickType represents the pressing of a key on a + * keyboard. + * + * @return true if this ClickType represents the pressing of a key + */ + public boolean isKeyboardClick() { + return (this == ClickType.NUMBER_KEY) || (this == ClickType.DROP) || (this == ClickType.CONTROL_DROP); + } + + /** + * Gets whether this ClickType represents an action that can only be + * performed by a Player in creative mode. + * + * @return true if this action requires Creative mode + */ + public boolean isCreativeAction() { + // Why use middle click? + return (this == ClickType.MIDDLE) || (this == ClickType.CREATIVE); + } + + /** + * Gets whether this ClickType represents a right click. + * + * @return true if this ClickType represents a right click + */ + public boolean isRightClick() { + return (this == ClickType.RIGHT) || (this == ClickType.SHIFT_RIGHT); + } + + /** + * Gets whether this ClickType represents a left click. + * + * @return true if this ClickType represents a left click + */ + public boolean isLeftClick() { + return (this == ClickType.LEFT) || (this == ClickType.SHIFT_LEFT) || (this == ClickType.DOUBLE_CLICK) || (this == ClickType.CREATIVE); + } + + /** + * Gets whether this ClickType indicates that the shift key was pressed + * down when the click was made. + * + * @return true if the action uses Shift. + */ + public boolean isShiftClick() { + return (this == ClickType.SHIFT_LEFT) || (this == ClickType.SHIFT_RIGHT) || (this == ClickType.CONTROL_DROP); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/CraftItemEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/CraftItemEvent.java new file mode 100644 index 0000000..ba3f5e5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/CraftItemEvent.java @@ -0,0 +1,40 @@ +package org.bukkit.event.inventory; + +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.Recipe; + +/** + * Called when the recipe of an Item is completed inside a crafting matrix. + */ +public class CraftItemEvent extends InventoryClickEvent { + private Recipe recipe; + + @Deprecated + public CraftItemEvent(Recipe recipe, InventoryView what, SlotType type, int slot, boolean right, boolean shift) { + this(recipe, what, type, slot, right ? (shift ? ClickType.SHIFT_RIGHT : ClickType.RIGHT) : (shift ? ClickType.SHIFT_LEFT : ClickType.LEFT), InventoryAction.PICKUP_ALL); + } + + public CraftItemEvent(Recipe recipe, InventoryView what, SlotType type, int slot, ClickType click, InventoryAction action) { + super(what, type, slot, click, action); + this.recipe = recipe; + } + + public CraftItemEvent(Recipe recipe, InventoryView what, SlotType type, int slot, ClickType click, InventoryAction action, int key) { + super(what, type, slot, click, action, key); + this.recipe = recipe; + } + + /** + * @return A copy of the current recipe on the crafting matrix. + */ + public Recipe getRecipe() { + return recipe; + } + + @Override + public CraftingInventory getInventory() { + return (CraftingInventory) super.getInventory(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/DragType.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/DragType.java new file mode 100644 index 0000000..72c2bed --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/DragType.java @@ -0,0 +1,17 @@ +package org.bukkit.event.inventory; + +/** + * Represents the effect of a drag that will be applied to an Inventory in an + * InventoryDragEvent. + */ +public enum DragType { + /** + * One item from the cursor is placed in each selected slot. + */ + SINGLE, + /** + * The cursor is split evenly across all selected slots, not to exceed the + * Material's max stack size, with the remainder going to the cursor. + */ + EVEN, +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/EquipmentSetEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/EquipmentSetEvent.java new file mode 100644 index 0000000..befa284 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/EquipmentSetEvent.java @@ -0,0 +1,31 @@ +package org.bukkit.event.inventory; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter @AllArgsConstructor +public class EquipmentSetEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + + private final HumanEntity humanEntity; + private final int slot; + private final ItemStack previousItem; + private final ItemStack newItem; + + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceBurnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceBurnEvent.java new file mode 100644 index 0000000..8ca1ff7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceBurnEvent.java @@ -0,0 +1,99 @@ +package org.bukkit.event.inventory; + +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockEvent; +import org.bukkit.inventory.ItemStack; + +/** + * Called when an ItemStack is successfully burned as fuel in a furnace. + */ +public class FurnaceBurnEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final ItemStack fuel; + private int burnTime; + private boolean cancelled; + private boolean burning; + + public FurnaceBurnEvent(final Block furnace, final ItemStack fuel, final int burnTime) { + super(furnace); + this.fuel = fuel; + this.burnTime = burnTime; + this.cancelled = false; + this.burning = true; + } + + /** + * Gets the block for the furnace involved in this event + * + * @return the block of the furnace + * @deprecated In favour of {@link #getBlock()}. + */ + @Deprecated + public Block getFurnace() { + return getBlock(); + } + + /** + * Gets the fuel ItemStack for this event + * + * @return the fuel ItemStack + */ + public ItemStack getFuel() { + return fuel; + } + + /** + * Gets the burn time for this fuel + * + * @return the burn time for this fuel + */ + public int getBurnTime() { + return burnTime; + } + + /** + * Sets the burn time for this fuel + * + * @param burnTime the burn time for this fuel + */ + public void setBurnTime(int burnTime) { + this.burnTime = burnTime; + } + + /** + * Gets whether the furnace's fuel is burning or not. + * + * @return whether the furnace's fuel is burning or not. + */ + public boolean isBurning() { + return this.burning; + } + + /** + * Sets whether the furnace's fuel is burning or not. + * + * @param burning true if the furnace's fuel is burning + */ + public void setBurning(boolean burning) { + this.burning = burning; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceExtractEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceExtractEvent.java new file mode 100644 index 0000000..b7381fa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceExtractEvent.java @@ -0,0 +1,49 @@ +package org.bukkit.event.inventory; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockExpEvent; + +/** + * This event is called when a player takes items out of the furnace + */ +public class FurnaceExtractEvent extends BlockExpEvent { + private final Player player; + private final Material itemType; + private final int itemAmount; + + public FurnaceExtractEvent(Player player, Block block, Material itemType, int itemAmount, int exp) { + super(block, exp); + this.player = player; + this.itemType = itemType; + this.itemAmount = itemAmount; + } + + /** + * Get the player that triggered the event + * + * @return the relevant player + */ + public Player getPlayer() { + return player; + } + + /** + * Get the Material of the item being retrieved + * + * @return the material of the item + */ + public Material getItemType() { + return itemType; + } + + /** + * Get the item count being retrieved + * + * @return the amount of the item + */ + public int getItemAmount() { + return itemAmount; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceSmeltEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceSmeltEvent.java new file mode 100644 index 0000000..e9d1a54 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/FurnaceSmeltEvent.java @@ -0,0 +1,79 @@ +package org.bukkit.event.inventory; + +import org.bukkit.block.Block; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockEvent; +import org.bukkit.inventory.ItemStack; + +/** + * Called when an ItemStack is successfully smelted in a furnace. + */ +public class FurnaceSmeltEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final ItemStack source; + private ItemStack result; + private boolean cancelled; + + public FurnaceSmeltEvent(final Block furnace, final ItemStack source, final ItemStack result) { + super(furnace); + this.source = source; + this.result = result; + this.cancelled = false; + } + + /** + * Gets the block for the furnace involved in this event + * + * @return the block of the furnace + * @deprecated In favour of {@link #getBlock()}. + */ + @Deprecated + public Block getFurnace() { + return getBlock(); + } + + /** + * Gets the smelted ItemStack for this event + * + * @return smelting source ItemStack + */ + public ItemStack getSource() { + return source; + } + + /** + * Gets the resultant ItemStack for this event + * + * @return smelting result ItemStack + */ + public ItemStack getResult() { + return result; + } + + /** + * Sets the resultant ItemStack for this event + * + * @param result new result ItemStack + */ + public void setResult(ItemStack result) { + this.result = result; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryAction.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryAction.java new file mode 100644 index 0000000..a7bc694 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryAction.java @@ -0,0 +1,91 @@ +package org.bukkit.event.inventory; + +/** + * An estimation of what the result will be. + */ +public enum InventoryAction { + + /** + * Nothing will happen from the click. + *

          + * There may be cases where nothing will happen and this is value is not + * provided, but it is guaranteed that this value is accurate when given. + */ + NOTHING, + /** + * All of the items on the clicked slot are moved to the cursor. + */ + PICKUP_ALL, + /** + * Some of the items on the clicked slot are moved to the cursor. + */ + PICKUP_SOME, + /** + * Half of the items on the clicked slot are moved to the cursor. + */ + PICKUP_HALF, + /** + * One of the items on the clicked slot are moved to the cursor. + */ + PICKUP_ONE, + /** + * All of the items on the cursor are moved to the clicked slot. + */ + PLACE_ALL, + /** + * Some of the items from the cursor are moved to the clicked slot + * (usually up to the max stack size). + */ + PLACE_SOME, + /** + * A single item from the cursor is moved to the clicked slot. + */ + PLACE_ONE, + /** + * The clicked item and the cursor are exchanged. + */ + SWAP_WITH_CURSOR, + /** + * The entire cursor item is dropped. + */ + DROP_ALL_CURSOR, + /** + * One item is dropped from the cursor. + */ + DROP_ONE_CURSOR, + /** + * The entire clicked slot is dropped. + */ + DROP_ALL_SLOT, + /** + * One item is dropped from the clicked slot. + */ + DROP_ONE_SLOT, + /** + * The item is moved to the opposite inventory if a space is found. + */ + MOVE_TO_OTHER_INVENTORY, + /** + * The clicked item is moved to the hotbar, and the item currently there + * is re-added to the player's inventory. + */ + HOTBAR_MOVE_AND_READD, + /** + * The clicked slot and the picked hotbar slot are swapped. + */ + HOTBAR_SWAP, + /** + * A max-size stack of the clicked item is put on the cursor. + */ + CLONE_STACK, + /** + * The inventory is searched for the same material, and they are put on + * the cursor up to {@link org.bukkit.Material#getMaxStackSize()}. + */ + COLLECT_TO_CURSOR, + /** + * An unrecognized ClickType. + */ + UNKNOWN, + ; +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java new file mode 100644 index 0000000..3313d91 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java @@ -0,0 +1,245 @@ +package org.bukkit.event.inventory; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.plugin.Plugin; + +/** + * This event is called when a player clicks a slot in an inventory. + *

          + * Because InventoryClickEvent occurs within a modification of the Inventory, + * not all Inventory related methods are safe to use. + *

          + * The following should never be invoked by an EventHandler for + * InventoryClickEvent using the HumanEntity or InventoryView associated with + * this event: + *

            + *
          • {@link HumanEntity#closeInventory()} + *
          • {@link HumanEntity#openInventory(Inventory)} + *
          • {@link HumanEntity#openWorkbench(Location, boolean)} + *
          • {@link HumanEntity#openEnchanting(Location, boolean)} + *
          • {@link InventoryView#close()} + *
          + * To invoke one of these methods, schedule a task using + * {@link BukkitScheduler#runTask(Plugin, Runnable)}, which will run the task + * on the next tick. Also be aware that this is not an exhaustive list, and + * other methods could potentially create issues as well. + *

          + * Assuming the EntityHuman associated with this event is an instance of a + * Player, manipulating the MaxStackSize or contents of an Inventory will + * require an Invocation of {@link Player#updateInventory()}. + *

          + * Modifications to slots that are modified by the results of this + * InventoryClickEvent can be overwritten. To change these slots, this event + * should be cancelled and all desired changes to the inventory applied. + * Alternatively, scheduling a task using {@link BukkitScheduler#runTask( + * Plugin, Runnable)}, which would execute the task on the next tick, would + * work as well. + */ +public class InventoryClickEvent extends InventoryInteractEvent { + private static final HandlerList handlers = new HandlerList(); + private final ClickType click; + private final InventoryAction action; + private final Inventory clickedInventory; + private SlotType slot_type; + private int whichSlot; + private int rawSlot; + private ItemStack current = null; + private int hotbarKey = -1; + + @Deprecated + public InventoryClickEvent(InventoryView view, SlotType type, int slot, boolean right, boolean shift) { + this(view, type, slot, right ? (shift ? ClickType.SHIFT_RIGHT : ClickType.RIGHT) : (shift ? ClickType.SHIFT_LEFT : ClickType.LEFT), InventoryAction.SWAP_WITH_CURSOR); + } + + public InventoryClickEvent(InventoryView view, SlotType type, int slot, ClickType click, InventoryAction action) { + super(view); + this.slot_type = type; + this.rawSlot = slot; + if (slot < 0) { + this.clickedInventory = null; + } else if (view.getTopInventory() != null && slot < view.getTopInventory().getSize()) { + this.clickedInventory = view.getTopInventory(); + } else { + this.clickedInventory = view.getBottomInventory(); + } + this.whichSlot = view.convertSlot(slot); + this.click = click; + this.action = action; + } + + public InventoryClickEvent(InventoryView view, SlotType type, int slot, ClickType click, InventoryAction action, int key) { + this(view, type, slot, click, action); + this.hotbarKey = key; + } + + /** + * Gets the inventory that was clicked, or null if outside of window + * @return The clicked inventory + */ + public Inventory getClickedInventory() { + return clickedInventory; + } + + /** + * Gets the type of slot that was clicked. + * + * @return the slot type + */ + public SlotType getSlotType() { + return slot_type; + } + + /** + * Gets the current ItemStack on the cursor. + * + * @return the cursor ItemStack + */ + public ItemStack getCursor() { + return getView().getCursor(); + } + + /** + * Gets the ItemStack currently in the clicked slot. + * + * @return the item in the clicked + */ + public ItemStack getCurrentItem() { + if (slot_type == SlotType.OUTSIDE) { + return current; + } + return getView().getItem(rawSlot); + } + + /** + * Gets whether or not the ClickType for this event represents a right + * click. + * + * @return true if the ClickType uses the right mouse button. + * @see ClickType#isRightClick() + */ + public boolean isRightClick() { + return click.isRightClick(); + } + + /** + * Gets whether or not the ClickType for this event represents a left + * click. + * + * @return true if the ClickType uses the left mouse button. + * @see ClickType#isLeftClick() + */ + public boolean isLeftClick() { + return click.isLeftClick(); + } + + /** + * Gets whether the ClickType for this event indicates that the key was + * pressed down when the click was made. + * + * @return true if the ClickType uses Shift or Ctrl. + * @see ClickType#isShiftClick() + */ + public boolean isShiftClick() { + return click.isShiftClick(); + } + + /** + * Sets the item on the cursor. + * + * @param stack the new cursor item + * @deprecated This changes the ItemStack in their hand before any + * calculations are applied to the Inventory, which has a tendency to + * create inconsistencies between the Player and the server, and to + * make unexpected changes in the behavior of the clicked Inventory. + */ + @Deprecated + public void setCursor(ItemStack stack) { + getView().setCursor(stack); + } + + /** + * Sets the ItemStack currently in the clicked slot. + * + * @param stack the item to be placed in the current slot + */ + public void setCurrentItem(ItemStack stack) { + if (slot_type == SlotType.OUTSIDE) { + current = stack; + } else { + getView().setItem(rawSlot, stack); + } + } + + /** + * The slot number that was clicked, ready for passing to + * {@link Inventory#getItem(int)}. Note that there may be two slots with + * the same slot number, since a view links two different inventories. + * + * @return The slot number. + */ + public int getSlot() { + return whichSlot; + } + + /** + * The raw slot number clicked, ready for passing to {@link InventoryView + * #getItem(int)} This slot number is unique for the view. + * + * @return the slot number + */ + public int getRawSlot() { + return rawSlot; + } + + /** + * If the ClickType is NUMBER_KEY, this method will return the index of + * the pressed key (0-8). + * + * @return the number on the key minus 1 (range 0-8); or -1 if not + * a NUMBER_KEY action + */ + public int getHotbarButton() { + return hotbarKey; + } + + /** + * Gets the InventoryAction that triggered this event. + *

          + * This action cannot be changed, and represents what the normal outcome + * of the event will be. To change the behavior of this + * InventoryClickEvent, changes must be manually applied. + * + * @return the InventoryAction that triggered this event. + */ + public InventoryAction getAction() { + return action; + } + + /** + * Gets the ClickType for this event. + *

          + * This is insulated against changes to the inventory by other plugins. + * + * @return the type of inventory click + */ + public ClickType getClick() { + return click; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java new file mode 100644 index 0000000..19889b2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java @@ -0,0 +1,35 @@ + +package org.bukkit.event.inventory; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.InventoryView; + +/** + * Represents a player related inventory event + */ +public class InventoryCloseEvent extends InventoryEvent { + private static final HandlerList handlers = new HandlerList(); + + public InventoryCloseEvent(InventoryView transaction) { + super(transaction); + } + + /** + * Returns the player involved in this event + * + * @return Player who is involved in this event + */ + public final HumanEntity getPlayer() { + return transaction.getPlayer(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryCreativeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryCreativeEvent.java new file mode 100644 index 0000000..da7dffc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryCreativeEvent.java @@ -0,0 +1,27 @@ +package org.bukkit.event.inventory; + +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +/** + * This event is called when a player in creative mode puts down or picks up + * an item in their inventory / hotbar and when they drop items from their + * Inventory while in creative mode. + */ +public class InventoryCreativeEvent extends InventoryClickEvent { + private ItemStack item; + + public InventoryCreativeEvent(InventoryView what, SlotType type, int slot, ItemStack newItem) { + super(what, type, slot, ClickType.CREATIVE, InventoryAction.PLACE_ALL); + this.item = newItem; + } + + public ItemStack getCursor() { + return item; + } + + public void setCursor(ItemStack item) { + this.item = item; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryDragEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryDragEvent.java new file mode 100644 index 0000000..e7e54a7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryDragEvent.java @@ -0,0 +1,164 @@ +package org.bukkit.event.inventory; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; + +import com.google.common.collect.ImmutableSet; + +/** + * This event is called when the player drags an item in their cursor across + * the inventory. The ItemStack is distributed across the slots the + * HumanEntity dragged over. The method of distribution is described by the + * DragType returned by {@link #getType()}. + *

          + * Canceling this event will result in none of the changes described in + * {@link #getNewItems()} being applied to the Inventory. + *

          + * Because InventoryDragEvent occurs within a modification of the Inventory, + * not all Inventory related methods are safe to use. + *

          + * The following should never be invoked by an EventHandler for + * InventoryDragEvent using the HumanEntity or InventoryView associated with + * this event. + *

            + *
          • {@link HumanEntity#closeInventory()} + *
          • {@link HumanEntity#openInventory(Inventory)} + *
          • {@link HumanEntity#openWorkbench(Location, boolean)} + *
          • {@link HumanEntity#openEnchanting(Location, boolean)} + *
          • {@link InventoryView#close()} + *
          + * To invoke one of these methods, schedule a task using + * {@link BukkitScheduler#runTask(Plugin, Runnable)}, which will run the task + * on the next tick. Also be aware that this is not an exhaustive list, and + * other methods could potentially create issues as well. + *

          + * Assuming the EntityHuman associated with this event is an instance of a + * Player, manipulating the MaxStackSize or contents of an Inventory will + * require an Invocation of {@link Player#updateInventory()}. + *

          + * Any modifications to slots that are modified by the results of this + * InventoryDragEvent will be overwritten. To change these slots, this event + * should be cancelled and the changes applied. Alternatively, scheduling a + * task using {@link BukkitScheduler#runTask(Plugin, Runnable)}, which would + * execute the task on the next tick, would work as well. + */ +public class InventoryDragEvent extends InventoryInteractEvent { + private static final HandlerList handlers = new HandlerList(); + private final DragType type; + private final Map addedItems; + private final Set containerSlots; + private final ItemStack oldCursor; + private ItemStack newCursor; + + public InventoryDragEvent(InventoryView what, ItemStack newCursor, ItemStack oldCursor, boolean right, Map slots) { + super(what); + + Validate.notNull(oldCursor); + Validate.notNull(slots); + + type = right ? DragType.SINGLE : DragType.EVEN; + this.newCursor = newCursor; + this.oldCursor = oldCursor; + this.addedItems = slots; + ImmutableSet.Builder b = ImmutableSet.builder(); + for (Integer slot : slots.keySet()) { + b.add(what.convertSlot(slot)); + } + this.containerSlots = b.build(); + } + + /** + * Gets all items to be added to the inventory in this drag. + * + * @return map from raw slot id to new ItemStack + */ + public Map getNewItems() { + return Collections.unmodifiableMap(addedItems); + } + + /** + * Gets the raw slot ids to be changed in this drag. + * + * @return list of raw slot ids, suitable for getView().getItem(int) + */ + public Set getRawSlots() { + return addedItems.keySet(); + } + + /** + * Gets the slots to be changed in this drag. + * + * @return list of converted slot ids, suitable for {@link + * org.bukkit.inventory.Inventory#getItem(int)}. + */ + public Set getInventorySlots() { + return containerSlots; + } + + /** + * Gets the result cursor after the drag is done. The returned value is + * mutable. + * + * @return the result cursor + */ + public ItemStack getCursor() { + return newCursor; + } + + /** + * Sets the result cursor after the drag is done. + *

          + * Changing this item stack changes the cursor item. Note that changing + * the affected "dragged" slots does not change this ItemStack, nor does + * changing this ItemStack affect the "dragged" slots. + * + * @param newCursor the new cursor ItemStack + */ + public void setCursor(ItemStack newCursor) { + this.newCursor = newCursor; + } + + /** + * Gets an ItemStack representing the cursor prior to any modifications + * as a result of this drag. + * + * @return the original cursor + */ + public ItemStack getOldCursor() { + return oldCursor.clone(); + } + + /** + * Gets the DragType that describes the behavior of ItemStacks placed + * after this InventoryDragEvent. + *

          + * The ItemStacks and the raw slots that they're being applied to can be + * found using {@link #getNewItems()}. + * + * @return the DragType of this InventoryDragEvent + */ + public DragType getType() { + return type; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryEvent.java new file mode 100644 index 0000000..973c392 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryEvent.java @@ -0,0 +1,59 @@ + +package org.bukkit.event.inventory; + +import java.util.List; + +import org.bukkit.event.HandlerList; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.Event; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; + +/** + * Represents a player related inventory event + */ +public class InventoryEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + protected InventoryView transaction; + + public InventoryEvent(InventoryView transaction) { + this.transaction = transaction; + } + + /** + * Gets the primary Inventory involved in this transaction + * + * @return The upper inventory. + */ + public Inventory getInventory() { + return transaction.getTopInventory(); + } + + /** + * Gets the list of players viewing the primary (upper) inventory involved + * in this event + * + * @return A list of people viewing. + */ + public List getViewers() { + return transaction.getTopInventory().getViewers(); + } + + /** + * Gets the view object itself + * + * @return InventoryView + */ + public InventoryView getView() { + return transaction; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryInteractEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryInteractEvent.java new file mode 100644 index 0000000..8624f8d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryInteractEvent.java @@ -0,0 +1,78 @@ +package org.bukkit.event.inventory; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event.Result; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +/** + * An abstract base class for events that describe an interaction between a + * HumanEntity and the contents of an Inventory. + */ +public abstract class InventoryInteractEvent extends InventoryEvent implements Cancellable { + private Result result = Result.DEFAULT; + + public InventoryInteractEvent(InventoryView transaction) { + super(transaction); + } + + /** + * Gets the player who performed the click. + * + * @return The clicking player. + */ + public HumanEntity getWhoClicked() { + return getView().getPlayer(); + } + + /** + * Sets the result of this event. This will change whether or not this + * event is considered cancelled. + * + * @see #isCancelled() + * @param newResult the new {@link Result} for this event + */ + public void setResult(Result newResult) { + result = newResult; + } + + /** + * Gets the {@link Result} of this event. The Result describes the + * behavior that will be applied to the inventory in relation to this + * event. + * + * @return the Result of this event. + */ + public Result getResult() { + return result; + } + + /** + * Gets whether or not this event is cancelled. This is based off of the + * Result value returned by {@link #getResult()}. Result.ALLOW and + * Result.DEFAULT will result in a returned value of false, but + * Result.DENY will result in a returned value of true. + *

          + * {@inheritDoc} + * + * @return whether the event is cancelled + */ + public boolean isCancelled() { + return getResult() == Result.DENY; + } + + /** + * Proxy method to {@link #setResult(Event.Result)} for the Cancellable + * interface. {@link #setResult(Event.Result)} is preferred, as it allows + * you to specify the Result beyond Result.DENY and Result.ALLOW. + *

          + * {@inheritDoc} + * + * @param toCancel result becomes DENY if true, ALLOW if false + */ + public void setCancelled(boolean toCancel) { + setResult(toCancel ? Result.DENY : Result.ALLOW); + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryMoveItemEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryMoveItemEvent.java new file mode 100644 index 0000000..06ec99a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryMoveItemEvent.java @@ -0,0 +1,108 @@ +package org.bukkit.event.inventory; + +import org.apache.commons.lang.Validate; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +/** + * Called when some entity or block (e.g. hopper) tries to move items directly + * from one inventory to another. + *

          + * When this event is called, the initiator may already have removed the item + * from the source inventory and is ready to move it into the destination + * inventory. + *

          + * If this event is cancelled, the items will be returned to the source + * inventory, if needed. + *

          + * If this event is not cancelled, the initiator will try to put the ItemStack + * into the destination inventory. If this is not possible and the ItemStack + * has not been modified, the source inventory slot will be restored to its + * former state. Otherwise any additional items will be discarded. + */ +public class InventoryMoveItemEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Inventory sourceInventory; + private final Inventory destinationInventory; + private ItemStack itemStack; + private final boolean didSourceInitiate; + + public InventoryMoveItemEvent(final Inventory sourceInventory, final ItemStack itemStack, final Inventory destinationInventory, final boolean didSourceInitiate) { + Validate.notNull(itemStack, "ItemStack cannot be null"); + this.sourceInventory = sourceInventory; + this.itemStack = itemStack; + this.destinationInventory = destinationInventory; + this.didSourceInitiate = didSourceInitiate; + } + + /** + * Gets the Inventory that the ItemStack is being taken from + * + * @return Inventory that the ItemStack is being taken from + */ + public Inventory getSource() { + return sourceInventory; + } + + /** + * Gets the ItemStack being moved; if modified, the original item will not + * be removed from the source inventory. + * + * @return ItemStack + */ + public ItemStack getItem() { + return itemStack.clone(); + } + + /** + * Sets the ItemStack being moved; if this is different from the original + * ItemStack, the original item will not be removed from the source + * inventory. + * + * @param itemStack The ItemStack + */ + public void setItem(ItemStack itemStack) { + Validate.notNull(itemStack, "ItemStack cannot be null. Cancel the event if you want nothing to be transferred."); + this.itemStack = itemStack.clone(); + } + + /** + * Gets the Inventory that the ItemStack is being put into + * + * @return Inventory that the ItemStack is being put into + */ + public Inventory getDestination() { + return destinationInventory; + } + + /** + * Gets the Inventory that initiated the transfer. This will always be + * either the destination or source Inventory. + * + * @return Inventory that initiated the transfer + */ + public Inventory getInitiator() { + return didSourceInitiate ? sourceInventory : destinationInventory; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java new file mode 100644 index 0000000..c3570aa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java @@ -0,0 +1,63 @@ +package org.bukkit.event.inventory; + +import org.bukkit.inventory.InventoryView; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Represents a player related inventory event + */ +public class InventoryOpenEvent extends InventoryEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + + public InventoryOpenEvent(InventoryView transaction) { + super(transaction); + this.cancelled = false; + } + + /** + * Returns the player involved in this event + * + * @return Player who is involved in this event + */ + public final HumanEntity getPlayer() { + return transaction.getPlayer(); + } + + /** + * Gets the cancellation state of this event. A cancelled event will not + * be executed in the server, but will still pass to other plugins. + *

          + * If an inventory open event is cancelled, the inventory screen will not + * show. + * + * @return true if this event is cancelled + */ + public boolean isCancelled() { + return cancelled; + } + + /** + * Sets the cancellation state of this event. A cancelled event will not + * be executed in the server, but will still pass to other plugins. + *

          + * If an inventory open event is cancelled, the inventory screen will not + * show. + * + * @param cancel true if you wish to cancel this event + */ + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryPickupItemEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryPickupItemEvent.java new file mode 100644 index 0000000..af6ad5b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryPickupItemEvent.java @@ -0,0 +1,58 @@ +package org.bukkit.event.inventory; + +import org.bukkit.entity.Item; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.Inventory; + +/** + * Called when a hopper or hopper minecart picks up a dropped item. + */ +public class InventoryPickupItemEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Inventory inventory; + private final Item item; + + public InventoryPickupItemEvent(final Inventory inventory, final Item item) { + super(); + this.inventory = inventory; + this.item = item; + } + + /** + * Gets the Inventory that picked up the item + * + * @return Inventory + */ + public Inventory getInventory() { + return inventory; + } + + /** + * Gets the Item entity that was picked up + * + * @return Item + */ + public Item getItem() { + return item; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryType.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryType.java new file mode 100644 index 0000000..b83580a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/InventoryType.java @@ -0,0 +1,124 @@ +package org.bukkit.event.inventory; + +public enum InventoryType { + + /** + * A chest inventory, with 0, 9, 18, 27, 36, 45, or 54 slots of type + * CONTAINER. + */ + CHEST(27,"Chest"), + /** + * A dispenser inventory, with 9 slots of type CONTAINER. + */ + DISPENSER(9,"Dispenser"), + /** + * A dropper inventory, with 9 slots of type CONTAINER. + */ + DROPPER(9, "Dropper"), + /** + * A furnace inventory, with a RESULT slot, a CRAFTING slot, and a FUEL + * slot. + */ + FURNACE(3,"Furnace"), + /** + * A workbench inventory, with 9 CRAFTING slots and a RESULT slot. + */ + WORKBENCH(10,"Crafting"), + /** + * A player's crafting inventory, with 4 CRAFTING slots and a RESULT slot. + * Also implies that the 4 ARMOR slots are accessible. + */ + CRAFTING(5,"Crafting"), + /** + * An enchantment table inventory, with one CRAFTING slot and three + * enchanting buttons. + */ + ENCHANTING(1,"Enchanting"), + /** + * A brewing stand inventory, with one FUEL slot and three CRAFTING slots. + */ + BREWING(4,"Brewing"), + /** + * A player's inventory, with 9 QUICKBAR slots, 27 CONTAINER slots, and 4 + * ARMOR slots. The ARMOUR slots may not be visible to the player, though. + */ + PLAYER(36,"Player"), + /** + * The creative mode inventory, with only 9 QUICKBAR slots and nothing + * else. (The actual creative interface with the items is client-side and + * cannot be altered by the server.) + */ + CREATIVE(9,"Creative"), + /** + * The merchant inventory, with 2 TRADE-IN slots, and 1 RESULT slot. + */ + MERCHANT(3,"Villager"), + /** + * The ender chest inventory, with 27 slots. + */ + ENDER_CHEST(27,"Ender Chest"), + /** + * An anvil inventory, with 2 CRAFTING slots and 1 RESULT slot + */ + ANVIL(3, "Repairing"), + /** + * A beacon inventory, with 1 CRAFTING slot + */ + BEACON(1, "container.beacon"), + /** + * A hopper inventory, with 5 slots of type CONTAINER. + */ + HOPPER(5, "Item Hopper"), + ; + + private final int size; + private final String title; + + private InventoryType(int defaultSize, String defaultTitle) { + size = defaultSize; + title = defaultTitle; + } + + public int getDefaultSize() { + return size; + } + + public String getDefaultTitle() { + return title; + } + + public enum SlotType { + /** + * A result slot in a furnace or crafting inventory. + */ + RESULT, + /** + * A slot in the crafting matrix, or the input slot in a furnace + * inventory, the potion slot in the brewing stand, or the enchanting + * slot. + */ + CRAFTING, + /** + * An armour slot in the player's inventory. + */ + ARMOR, + /** + * A regular slot in the container or the player's inventory; anything + * not covered by the other enum values. + */ + CONTAINER, + /** + * A slot in the bottom row or quickbar. + */ + QUICKBAR, + /** + * A pseudo-slot representing the area outside the inventory window. + */ + OUTSIDE, + /** + * The fuel slot in a furnace inventory, or the ingredient slot in a + * brewing stand inventory. + */ + FUEL; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/PrepareAnvilRepairEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/PrepareAnvilRepairEvent.java new file mode 100644 index 0000000..15aa243 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/PrepareAnvilRepairEvent.java @@ -0,0 +1,77 @@ +package org.bukkit.event.inventory; + +import org.bukkit.block.Block; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +/** + * @since 11/20/2017 + */ +public class PrepareAnvilRepairEvent extends InventoryEvent implements Cancellable { + + private static final HandlerList handlers; + + static { + handlers = new HandlerList(); + } + + private final ItemStack first; + private final ItemStack second; + private final HumanEntity repairer; + private final Block anvil; + private boolean cancelled; + private ItemStack result; + + public PrepareAnvilRepairEvent(final HumanEntity repairer, final InventoryView view, final Block anvil, + final ItemStack first, final ItemStack second, final ItemStack result) { + super(view); + this.first = first; + this.second = second; + this.anvil = anvil; + this.result = result; + this.repairer = repairer; + } + + public static HandlerList getHandlerList() { + return PrepareAnvilRepairEvent.handlers; + } + + public ItemStack getFirst() { + return this.first; + } + + public ItemStack getSecond() { + return this.second; + } + + public HumanEntity getRepairer() { + return this.repairer; + } + + public Block getAnvil() { + return this.anvil; + } + + public ItemStack getResult() { + return this.result; + } + + public void setResult(final ItemStack result) { + this.result = result; + } + + public boolean isCancelled() { + return this.cancelled; + } + + public void setCancelled(final boolean cancel) { + this.cancelled = cancel; + } + + public HandlerList getHandlers() { + return PrepareAnvilRepairEvent.handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/inventory/PrepareItemCraftEvent.java b/vspigot-api/src/main/java/org/bukkit/event/inventory/PrepareItemCraftEvent.java new file mode 100644 index 0000000..5731190 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/inventory/PrepareItemCraftEvent.java @@ -0,0 +1,56 @@ +package org.bukkit.event.inventory; + +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.Recipe; + +public class PrepareItemCraftEvent extends InventoryEvent { + private static final HandlerList handlers = new HandlerList(); + private boolean repair; + private CraftingInventory matrix; + + public PrepareItemCraftEvent(CraftingInventory what, InventoryView view, boolean isRepair) { + super(view); + this.matrix = what; + this.repair = isRepair; + } + + /** + * Get the recipe that has been formed. If this event was triggered by a + * tool repair, this will be a temporary shapeless recipe representing the + * repair. + * + * @return The recipe being crafted. + */ + public Recipe getRecipe() { + return matrix.getRecipe(); + } + + /** + * @return The crafting inventory on which the recipe was formed. + */ + @Override + public CraftingInventory getInventory() { + return matrix; + } + + /** + * Check if this event was triggered by a tool repair operation rather + * than a crafting recipe. + * + * @return True if this is a repair. + */ + public boolean isRepair() { + return repair; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingBreakByEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingBreakByEntityEvent.java new file mode 100644 index 0000000..1dc4987 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingBreakByEntityEvent.java @@ -0,0 +1,31 @@ +package org.bukkit.event.painting; + +import org.bukkit.Warning; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Painting; + +/** + * Triggered when a painting is removed by an entity + * + * @deprecated Use {@link org.bukkit.event.hanging.HangingBreakByEntityEvent} + * instead. + */ +@Deprecated +@Warning(reason="This event has been replaced by HangingBreakByEntityEvent") +public class PaintingBreakByEntityEvent extends PaintingBreakEvent { + private final Entity remover; + + public PaintingBreakByEntityEvent(final Painting painting, final Entity remover) { + super(painting, RemoveCause.ENTITY); + this.remover = remover; + } + + /** + * Gets the entity that removed the painting + * + * @return the entity that removed the painting. + */ + public Entity getRemover() { + return remover; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingBreakEvent.java b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingBreakEvent.java new file mode 100644 index 0000000..3e27c69 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingBreakEvent.java @@ -0,0 +1,76 @@ +package org.bukkit.event.painting; + +import org.bukkit.Warning; +import org.bukkit.entity.Painting; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Triggered when a painting is removed + * + * @deprecated Use {@link org.bukkit.event.hanging.HangingBreakEvent} instead. + */ +@Deprecated +@Warning(reason="This event has been replaced by HangingBreakEvent") +public class PaintingBreakEvent extends PaintingEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final RemoveCause cause; + + public PaintingBreakEvent(final Painting painting, final RemoveCause cause) { + super(painting); + this.cause = cause; + } + + /** + * Gets the cause for the painting's removal + * + * @return the RemoveCause for the painting's removal + */ + public RemoveCause getCause() { + return cause; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * An enum to specify the cause of the removal + */ + public enum RemoveCause { + /** + * Removed by an entity + */ + ENTITY, + /** + * Removed by fire + */ + FIRE, + /** + * Removed by placing a block on it + */ + OBSTRUCTION, + /** + * Removed by water flowing over it + */ + WATER, + /** + * Removed by destroying the block behind it, etc + */ + PHYSICS, + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingEvent.java b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingEvent.java new file mode 100644 index 0000000..3a51348 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingEvent.java @@ -0,0 +1,29 @@ +package org.bukkit.event.painting; + +import org.bukkit.Warning; +import org.bukkit.entity.Painting; +import org.bukkit.event.Event; + +/** + * Represents a painting-related event. + * + * @deprecated Use {@link org.bukkit.event.hanging.HangingEvent} instead. + */ +@Deprecated +@Warning(reason="This event has been replaced by HangingEvent") +public abstract class PaintingEvent extends Event { + protected Painting painting; + + protected PaintingEvent(final Painting painting) { + this.painting = painting; + } + + /** + * Gets the painting involved in this event. + * + * @return the painting + */ + public Painting getPainting() { + return painting; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingPlaceEvent.java b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingPlaceEvent.java new file mode 100644 index 0000000..3250b29 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/painting/PaintingPlaceEvent.java @@ -0,0 +1,76 @@ +package org.bukkit.event.painting; + +import org.bukkit.Warning; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Painting; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Triggered when a painting is created in the world + * + * @deprecated Use {@link org.bukkit.event.hanging.HangingPlaceEvent} instead. + */ +@Deprecated +@Warning(reason="This event has been replaced by HangingPlaceEvent") +public class PaintingPlaceEvent extends PaintingEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Player player; + private final Block block; + private final BlockFace blockFace; + + public PaintingPlaceEvent(final Painting painting, final Player player, final Block block, final BlockFace blockFace) { + super(painting); + this.player = player; + this.block = block; + this.blockFace = blockFace; + } + + /** + * Returns the player placing the painting + * + * @return Entity returns the player placing the painting + */ + public Player getPlayer() { + return player; + } + + /** + * Returns the block that the painting was placed on + * + * @return Block returns the block painting placed on + */ + public Block getBlock() { + return block; + } + + /** + * Returns the face of the block that the painting was placed on + * + * @return BlockFace returns the face of the block the painting was placed + * on + */ + public BlockFace getBlockFace() { + return blockFace; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java new file mode 100644 index 0000000..a796292 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java @@ -0,0 +1,140 @@ +package org.bukkit.event.player; + +import java.util.IllegalFormatException; +import java.util.Set; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * This event will sometimes fire synchronously, depending on how it was + * triggered. + *

          + * The constructor provides a boolean to indicate if the event was fired + * synchronously or asynchronously. When asynchronous, this event can be + * called from any thread, sans the main thread, and has limited access to the + * API. + *

          + * If a player is the direct cause of this event by an incoming packet, this + * event will be asynchronous. If a plugin triggers this event by compelling a + * player to chat, this event will be synchronous. + *

          + * Care should be taken to check {@link #isAsynchronous()} and treat the event + * appropriately. + */ +public class AsyncPlayerChatEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private String message; + private String format = "<%1$s> %2$s"; + private final Set recipients; + + /** + * + * @param async This changes the event to a synchronous state. + * @param who the chat sender + * @param message the message sent + * @param players the players to receive the message. This may be a lazy + * or unmodifiable collection. + */ + public AsyncPlayerChatEvent(final boolean async, final Player who, final String message, final Set players) { + super(who, async); + this.message = message; + recipients = players; + } + + /** + * Gets the message that the player is attempting to send. This message + * will be used with {@link #getFormat()}. + * + * @return Message the player is attempting to send + */ + public String getMessage() { + return message; + } + + /** + * Sets the message that the player will send. This message will be used + * with {@link #getFormat()}. + * + * @param message New message that the player will send + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * Gets the format to use to display this chat message. + *

          + * When this event finishes execution, the first format parameter is the + * {@link Player#getDisplayName()} and the second parameter is {@link + * #getMessage()} + * + * @return {@link String#format(String, Object...)} compatible format + * string + */ + public String getFormat() { + return format; + } + + /** + * Sets the format to use to display this chat message. + *

          + * When this event finishes execution, the first format parameter is the + * {@link Player#getDisplayName()} and the second parameter is {@link + * #getMessage()} + * + * @param format {@link String#format(String, Object...)} compatible + * format string + * @throws IllegalFormatException if the underlying API throws the + * exception + * @throws NullPointerException if format is null + * @see String#format(String, Object...) + */ + public void setFormat(final String format) throws IllegalFormatException, NullPointerException { + // Oh for a better way to do this! + try { + String.format(format, player, message); + } catch (RuntimeException ex) { + ex.fillInStackTrace(); + throw ex; + } + + this.format = format; + } + + /** + * Gets a set of recipients that this chat message will be displayed to. + *

          + * The set returned is not guaranteed to be mutable and may auto-populate + * on access. Any listener accessing the returned set should be aware that + * it may reduce performance for a lazy set implementation. + *

          + * Listeners should be aware that modifying the list may throw {@link + * UnsupportedOperationException} if the event caller provides an + * unmodifiable set. + * + * @return All Players who will see this chat message + */ + public Set getRecipients() { + return recipients; + } + + public boolean isCancelled() { + return cancel ; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java new file mode 100644 index 0000000..1d57188 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java @@ -0,0 +1,201 @@ +package org.bukkit.event.player; + +import java.net.InetAddress; +import java.util.UUID; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * Stores details for players attempting to log in. + *

          + * This event is asynchronous, and not run using main thread. + */ +public class AsyncPlayerPreLoginEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + private Result result; + private String message; + private final String name; + private final InetAddress ipAddress; + private final UUID uniqueId; + + @Deprecated + public AsyncPlayerPreLoginEvent(final String name, final InetAddress ipAddress) { + this(name, ipAddress, null); + } + + public AsyncPlayerPreLoginEvent(final String name, final InetAddress ipAddress, final UUID uniqueId) { + super(true); + this.result = Result.ALLOWED; + this.message = ""; + this.name = name; + this.ipAddress = ipAddress; + this.uniqueId = uniqueId; + } + + /** + * Gets the current result of the login, as an enum + * + * @return Current Result of the login + */ + public Result getLoginResult() { + return result; + } + + /** + * Gets the current result of the login, as an enum + * + * @return Current Result of the login + * @deprecated This method uses a deprecated enum from {@link + * PlayerPreLoginEvent} + * @see #getLoginResult() + */ + @Deprecated + public PlayerPreLoginEvent.Result getResult() { + return result == null ? null : result.old(); + } + + /** + * Sets the new result of the login, as an enum + * + * @param result New result to set + */ + public void setLoginResult(final Result result) { + this.result = result; + } + + /** + * Sets the new result of the login, as an enum + * + * @param result New result to set + * @deprecated This method uses a deprecated enum from {@link + * PlayerPreLoginEvent} + * @see #setLoginResult(Result) + */ + @Deprecated + public void setResult(final PlayerPreLoginEvent.Result result) { + this.result = result == null ? null : Result.valueOf(result.name()); + } + + /** + * Gets the current kick message that will be used if getResult() != + * Result.ALLOWED + * + * @return Current kick message + */ + public String getKickMessage() { + return message; + } + + /** + * Sets the kick message to display if getResult() != Result.ALLOWED + * + * @param message New kick message + */ + public void setKickMessage(final String message) { + this.message = message; + } + + /** + * Allows the player to log in + */ + public void allow() { + result = Result.ALLOWED; + message = ""; + } + + /** + * Disallows the player from logging in, with the given reason + * + * @param result New result for disallowing the player + * @param message Kick message to display to the user + */ + public void disallow(final Result result, final String message) { + this.result = result; + this.message = message; + } + + /** + * Disallows the player from logging in, with the given reason + * + * @param result New result for disallowing the player + * @param message Kick message to display to the user + * @deprecated This method uses a deprecated enum from {@link + * PlayerPreLoginEvent} + * @see #disallow(Result, String) + */ + @Deprecated + public void disallow(final PlayerPreLoginEvent.Result result, final String message) { + this.result = result == null ? null : Result.valueOf(result.name()); + this.message = message; + } + + /** + * Gets the player's name. + * + * @return the player's name + */ + public String getName() { + return name; + } + + /** + * Gets the player IP address. + * + * @return The IP address + */ + public InetAddress getAddress() { + return ipAddress; + } + + /** + * Gets the player's unique ID. + * + * @return The unique ID + */ + public UUID getUniqueId() { + return uniqueId; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * Basic kick reasons for communicating to plugins + */ + public enum Result { + + /** + * The player is allowed to log in + */ + ALLOWED, + /** + * The player is not allowed to log in, due to the server being full + */ + KICK_FULL, + /** + * The player is not allowed to log in, due to them being banned + */ + KICK_BANNED, + /** + * The player is not allowed to log in, due to them not being on the + * white list + */ + KICK_WHITELIST, + /** + * The player is not allowed to log in, for reasons undefined + */ + KICK_OTHER; + + @Deprecated + private PlayerPreLoginEvent.Result old() { + return PlayerPreLoginEvent.Result.valueOf(name()); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/GuardianEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/GuardianEvent.java new file mode 100644 index 0000000..f463e16 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/GuardianEvent.java @@ -0,0 +1,104 @@ +package org.bukkit.event.player; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +import java.util.HashMap; +import java.util.Map; + +import lombok.Getter; + +public class GuardianEvent extends PlayerEvent { + + @Getter private static HandlerList handlerList = new HandlerList(); + + @Getter private final Cheat cheat; + @Getter private final String module; + + @Getter private final DisplayLevel level; + + @Getter private final String message; + @Getter private final Location location; + + @Getter private Map data = new HashMap(); + + public GuardianEvent(Player player, Cheat cheat, String module, DisplayLevel level, String message) { + super(player); + + this.cheat = cheat; + this.level = level; + this.module = module; + this.message = message; + this.location = player.getLocation(); + } + + // Use this constructor if you want to override the location of the alert + public GuardianEvent(Player player, Cheat cheat, String module, DisplayLevel level, String message, Location location) { + super(player); + + this.cheat = cheat; + this.level = level; + this.module = module; + this.message = message; + this.location = location; + } + + @Override + public HandlerList getHandlers() { + return handlerList; + } + + public GuardianEvent addData(String key, Object value) { + data.put(key, value); + return this; + } + + public int getInt(String key) { + return (Integer) data.get(key); + } + + public String getString(String key) { + return (String) data.get(key); + } + + public double getDouble(String key) { + return (Double) data.get(key); + } + + public enum Cheat { + + SPEED_HACKS, + FLY_HACKS, + AUTO_CLICKER, + KILL_AURA, + HOVER, + CRITICALS, + NO_FALL, + TIMER, + PHASE, + FAST_USE, + REGENERATION, + CLIENT_MODIFICATIONS, + REACH, + + GENERAL, // Used for wrongly formed packets and other stuff + + DEBUG // Used for debugging only + + } + + public enum DisplayLevel { + + LOW, + MEDIUM, + HIGH, + HIGHEST; + + public boolean willDisplay(Player player) { + return player.hasPermission("guardian.display." + name().toLowerCase()); + } + + } + +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAchievementAwardedEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAchievementAwardedEvent.java new file mode 100644 index 0000000..e33fade --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAchievementAwardedEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.player; + +import org.bukkit.Achievement; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a player earns an achievement. + */ +public class PlayerAchievementAwardedEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Achievement achievement; + private boolean isCancelled = false; + + public PlayerAchievementAwardedEvent(Player player, Achievement achievement) { + super(player); + this.achievement = achievement; + } + + /** + * Gets the Achievement being awarded. + * + * @return the achievement being awarded + */ + public Achievement getAchievement() { + return achievement; + } + + public boolean isCancelled() { + return isCancelled; + } + + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAnimationEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAnimationEvent.java new file mode 100644 index 0000000..cabe77d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAnimationEvent.java @@ -0,0 +1,52 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Represents a player animation event + */ +public class PlayerAnimationEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final PlayerAnimationType animationType; + private boolean isCancelled = false; + + /** + * Construct a new PlayerAnimation event + * + * @param player The player instance + */ + public PlayerAnimationEvent(final Player player) { + super(player); + + // Only supported animation type for now: + animationType = PlayerAnimationType.ARM_SWING; + } + + /** + * Get the type of this animation event + * + * @return the animation type + */ + public PlayerAnimationType getAnimationType() { + return animationType; + } + + public boolean isCancelled() { + return this.isCancelled; + } + + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAnimationType.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAnimationType.java new file mode 100644 index 0000000..ea4bf26 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAnimationType.java @@ -0,0 +1,8 @@ +package org.bukkit.event.player; + +/** + * Different types of player animations + */ +public enum PlayerAnimationType { + ARM_SWING +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAttackEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAttackEvent.java new file mode 100644 index 0000000..79a8d56 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerAttackEvent.java @@ -0,0 +1,26 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +import lombok.Getter; + +public class PlayerAttackEvent extends PlayerEvent { + + @Getter private static HandlerList handlerList = new HandlerList(); + + @Getter private final Entity target; + + public PlayerAttackEvent(Player player, Entity target) { + super(player); + + this.target = target; + } + + @Override + public HandlerList getHandlers() { + return handlerList; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBedEnterEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBedEnterEvent.java new file mode 100644 index 0000000..09f1a66 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBedEnterEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.player; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * This event is fired when the player is almost about to enter the bed. + */ +public class PlayerBedEnterEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private final Block bed; + + public PlayerBedEnterEvent(final Player who, final Block bed) { + super(who); + this.bed = bed; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Returns the bed block involved in this event. + * + * @return the bed block involved in this event + */ + public Block getBed() { + return bed; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBedLeaveEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBedLeaveEvent.java new file mode 100644 index 0000000..628ab0b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBedLeaveEvent.java @@ -0,0 +1,36 @@ +package org.bukkit.event.player; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * This event is fired when the player is leaving a bed. + */ +public class PlayerBedLeaveEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final Block bed; + + public PlayerBedLeaveEvent(final Player who, final Block bed) { + super(who); + this.bed = bed; + } + + /** + * Returns the bed block involved in this event. + * + * @return the bed block involved in this event + */ + public Block getBed() { + return bed; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketEmptyEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketEmptyEvent.java new file mode 100644 index 0000000..8fb121a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketEmptyEvent.java @@ -0,0 +1,28 @@ +package org.bukkit.event.player; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * Called when a player empties a bucket + */ +public class PlayerBucketEmptyEvent extends PlayerBucketEvent { + private static final HandlerList handlers = new HandlerList(); + + public PlayerBucketEmptyEvent(final Player who, final Block blockClicked, final BlockFace blockFace, final Material bucket, final ItemStack itemInHand) { + super(who, blockClicked, blockFace, bucket, itemInHand); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketEvent.java new file mode 100644 index 0000000..d32c55e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketEvent.java @@ -0,0 +1,80 @@ +package org.bukkit.event.player; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.inventory.ItemStack; + +/** + * Called when a player interacts with a Bucket + */ +public abstract class PlayerBucketEvent extends PlayerEvent implements Cancellable { + private ItemStack itemStack; + private boolean cancelled = false; + private final Block blockClicked; + private final BlockFace blockFace; + private final Material bucket; + + public PlayerBucketEvent(final Player who, final Block blockClicked, final BlockFace blockFace, final Material bucket, final ItemStack itemInHand) { + super(who); + this.blockClicked = blockClicked; + this.blockFace = blockFace; + this.itemStack = itemInHand; + this.bucket = bucket; + } + + /** + * Returns the bucket used in this event + * + * @return the used bucket + */ + public Material getBucket() { + return bucket; + } + + /** + * Get the resulting item in hand after the bucket event + * + * @return Itemstack hold in hand after the event. + */ + public ItemStack getItemStack() { + return itemStack; + } + + /** + * Set the item in hand after the event + * + * @param itemStack the new held itemstack after the bucket event. + */ + public void setItemStack(ItemStack itemStack) { + this.itemStack = itemStack; + } + + /** + * Return the block clicked + * + * @return the blicked block + */ + public Block getBlockClicked() { + return blockClicked; + } + + /** + * Get the face on the clicked block + * + * @return the clicked face + */ + public BlockFace getBlockFace() { + return blockFace; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketFillEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketFillEvent.java new file mode 100644 index 0000000..94e042a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerBucketFillEvent.java @@ -0,0 +1,28 @@ +package org.bukkit.event.player; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * Called when a player fills a bucket + */ +public class PlayerBucketFillEvent extends PlayerBucketEvent { + private static final HandlerList handlers = new HandlerList(); + + public PlayerBucketFillEvent(final Player who, final Block blockClicked, final BlockFace blockFace, final Material bucket, final ItemStack itemInHand) { + super(who, blockClicked, blockFace, bucket, itemInHand); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChangedWorldEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChangedWorldEvent.java new file mode 100644 index 0000000..76c9c20 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChangedWorldEvent.java @@ -0,0 +1,36 @@ +package org.bukkit.event.player; + +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a player switches to another world. + */ +public class PlayerChangedWorldEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final World from; + + public PlayerChangedWorldEvent(final Player player, final World from) { + super(player); + this.from = from; + } + + /** + * Gets the world the player is switching from. + * + * @return player's previous world + */ + public World getFrom() { + return from; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChannelEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChannelEvent.java new file mode 100644 index 0000000..054efbc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChannelEvent.java @@ -0,0 +1,31 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * This event is called after a player registers or unregisters a new plugin + * channel. + */ +public abstract class PlayerChannelEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final String channel; + + public PlayerChannelEvent(final Player player, final String channel) { + super(player); + this.channel = channel; + } + + public final String getChannel() { + return channel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChatEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChatEvent.java new file mode 100644 index 0000000..1fb5cd7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChatEvent.java @@ -0,0 +1,125 @@ +package org.bukkit.event.player; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.Warning; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Holds information for player chat and commands + * + * @deprecated This event will fire from the main thread and allows the use of + * all of the Bukkit API, unlike the {@link AsyncPlayerChatEvent}. + *

          + * Listening to this event forces chat to wait for the main thread which + * causes delays for chat. {@link AsyncPlayerChatEvent} is the encouraged + * alternative for thread safe implementations. + */ +@Deprecated +@Warning(reason="Listening to this event forces chat to wait for the main thread, delaying chat messages.") +public class PlayerChatEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private String message; + private String format; + private final Set recipients; + + public PlayerChatEvent(final Player player, final String message) { + super(player); + this.message = message; + this.format = "<%1$s> %2$s"; + this.recipients = new HashSet(player.getServer().getOnlinePlayers()); + } + + public PlayerChatEvent(final Player player, final String message, final String format, final Set recipients) { + super(player); + this.message = message; + this.format = format; + this.recipients = recipients; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the message that the player is attempting to send + * + * @return Message the player is attempting to send + */ + public String getMessage() { + return message; + } + + /** + * Sets the message that the player will send + * + * @param message New message that the player will send + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * Sets the player that this message will display as, or command will be + * executed as + * + * @param player New player which this event will execute as + */ + public void setPlayer(final Player player) { + Validate.notNull(player, "Player cannot be null"); + this.player = player; + } + + /** + * Gets the format to use to display this chat message + * + * @return String.Format compatible format string + */ + public String getFormat() { + return format; + } + + /** + * Sets the format to use to display this chat message + * + * @param format String.Format compatible format string + */ + public void setFormat(final String format) { + // Oh for a better way to do this! + try { + String.format(format, player, message); + } catch (RuntimeException ex) { + ex.fillInStackTrace(); + throw ex; + } + + this.format = format; + } + + /** + * Gets a set of recipients that this chat message will be displayed to + * + * @return All Players who will see this chat message + */ + public Set getRecipients() { + return recipients; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChatTabCompleteEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChatTabCompleteEvent.java new file mode 100644 index 0000000..7241a9b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerChatTabCompleteEvent.java @@ -0,0 +1,70 @@ +package org.bukkit.event.player; + +import java.util.Collection; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a player attempts to tab-complete a chat message. + */ +public class PlayerChatTabCompleteEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final String message; + private final String lastToken; + private final Collection completions; + + public PlayerChatTabCompleteEvent(final Player who, final String message, final Collection completions) { + super(who); + Validate.notNull(message, "Message cannot be null"); + Validate.notNull(completions, "Completions cannot be null"); + this.message = message; + int i = message.lastIndexOf(' '); + if (i < 0) { + this.lastToken = message; + } else { + this.lastToken = message.substring(i + 1); + } + this.completions = completions; + } + + /** + * Gets the chat message being tab-completed. + * + * @return the chat message + */ + public String getChatMessage() { + return message; + } + + /** + * Gets the last 'token' of the message being tab-completed. + *

          + * The token is the substring starting with the character after the last + * space in the message. + * + * @return The last token for the chat message + */ + public String getLastToken() { + return lastToken; + } + + /** + * This is the collection of completions for this event. + * + * @return the current completions + */ + public Collection getTabCompletions() { + return completions; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerCommandPreprocessEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerCommandPreprocessEvent.java new file mode 100644 index 0000000..1ec8173 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerCommandPreprocessEvent.java @@ -0,0 +1,172 @@ +package org.bukkit.event.player; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * This event is called whenever a player runs a command (by placing a slash + * at the start of their message). It is called early in the command handling + * process, and modifications in this event (via {@link #setMessage(String)}) + * will be shown in the behavior. + *

          + * Many plugins will have no use for this event, and you should + * attempt to avoid using it if it is not necessary. + *

          + * Some examples of valid uses for this event are: + *

            + *
          • Logging executed commands to a separate file + *
          • Variable substitution. For example, replacing + * ${nearbyPlayer} with the name of the nearest other + * player, or simulating the @a and @p + * decorators used by Command Blocks in plugins that do not handle it. + *
          • Conditionally blocking commands belonging to other plugins. For + * example, blocking the use of the /home command in a + * combat arena. + *
          • Per-sender command aliases. For example, after a player runs the + * command /calias cr gamemode creative, the next time they + * run /cr, it gets replaced into + * /gamemode creative. (Global command aliases should be + * done by registering the alias.) + *
          + *

          + * Examples of incorrect uses are: + *

            + *
          • Using this event to run command logic + *
          + *

          + * If the event is cancelled, processing of the command will halt. + *

          + * The state of whether or not there is a slash (/) at the + * beginning of the message should be preserved. If a slash is added or + * removed, unexpected behavior may result. + */ +public class PlayerCommandPreprocessEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private String message; + private String format = "<%1$s> %2$s"; + private final Set recipients; + + public PlayerCommandPreprocessEvent(final Player player, final String message) { + super(player); + this.recipients = new HashSet(player.getServer().getOnlinePlayers()); + this.message = message; + } + + public PlayerCommandPreprocessEvent(final Player player, final String message, final Set recipients) { + super(player); + this.recipients = recipients; + this.message = message; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the command that the player is attempting to send. + *

          + * All commands begin with a special character; implementations do not + * consider the first character when executing the content. + * + * @return Message the player is attempting to send + */ + public String getMessage() { + return message; + } + + /** + * Sets the command that the player will send. + *

          + * All commands begin with a special character; implementations do not + * consider the first character when executing the content. + * + * @param command New message that the player will send + * @throws IllegalArgumentException if command is null or empty + */ + public void setMessage(String command) throws IllegalArgumentException { + Validate.notNull(command, "Command cannot be null"); + Validate.notEmpty(command, "Command cannot be empty"); + this.message = command; + } + + /** + * Sets the player that this command will be executed as. + * + * @param player New player which this event will execute as + * @throws IllegalArgumentException if the player provided is null + */ + public void setPlayer(final Player player) throws IllegalArgumentException { + Validate.notNull(player, "Player cannot be null"); + this.player = player; + } + + /** + * Gets the format to use to display this chat message + * + * @deprecated This method is provided for backward compatibility with no + * guarantee to the use of the format. + * @return String.Format compatible format string + */ + @Deprecated + public String getFormat() { + return format; + } + + /** + * Sets the format to use to display this chat message + * + * @deprecated This method is provided for backward compatibility with no + * guarantee to the effect of modifying the format. + * @param format String.Format compatible format string + */ + @Deprecated + public void setFormat(final String format) { + // Oh for a better way to do this! + try { + String.format(format, player, message); + } catch (RuntimeException ex) { + ex.fillInStackTrace(); + throw ex; + } + + this.format = format; + } + + /** + * Gets a set of recipients that this chat message will be displayed to. + *

          + * The set returned is not guaranteed to be mutable and may auto-populate + * on access. Any listener accessing the returned set should be aware that + * it may reduce performance for a lazy set implementation. Listeners + * should be aware that modifying the list may throw {@link + * UnsupportedOperationException} if the event caller provides an + * unmodifiable set. + * + * @deprecated This method is provided for backward compatibility with no + * guarantee to the effect of viewing or modifying the set. + * @return All Players who will see this chat message + */ + @Deprecated + public Set getRecipients() { + return recipients; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerDropItemEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerDropItemEvent.java new file mode 100644 index 0000000..5b41b65 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerDropItemEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Thrown when a player drops an item from their inventory + */ +public class PlayerDropItemEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Item drop; + private boolean cancel = false; + + public PlayerDropItemEvent(final Player player, final Item drop) { + super(player); + this.drop = drop; + } + + /** + * Gets the ItemDrop created by the player + * + * @return ItemDrop created by the player + */ + public Item getItemDrop() { + return drop; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEditBookEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEditBookEvent.java new file mode 100644 index 0000000..ea7ecef --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEditBookEvent.java @@ -0,0 +1,124 @@ +package org.bukkit.event.player; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.meta.BookMeta; + +/** + * Called when a player edits or signs a book and quill item. If the event is + * cancelled, no changes are made to the BookMeta + */ +public class PlayerEditBookEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + private final BookMeta previousBookMeta; + private final int slot; + private BookMeta newBookMeta; + private boolean isSigning; + private boolean cancel; + + public PlayerEditBookEvent(Player who, int slot, BookMeta previousBookMeta, BookMeta newBookMeta, boolean isSigning) { + super(who); + + Validate.isTrue(slot >= 0 && slot <=8, "Slot must be in range 0-8 inclusive"); + Validate.notNull(previousBookMeta, "Previous book meta must not be null"); + Validate.notNull(newBookMeta, "New book meta must not be null"); + + Bukkit.getItemFactory().equals(previousBookMeta, newBookMeta); + + this.previousBookMeta = previousBookMeta; + this.newBookMeta = newBookMeta; + this.slot = slot; + this.isSigning = isSigning; + this.cancel = false; + } + + /** + * Gets the book meta currently on the book. + *

          + * Note: this is a copy of the book meta. You cannot use this object to + * change the existing book meta. + * + * @return the book meta currently on the book + */ + public BookMeta getPreviousBookMeta() { + return previousBookMeta.clone(); + } + + /** + * Gets the book meta that the player is attempting to add to the book. + *

          + * Note: this is a copy of the proposed new book meta. Use {@link + * #setNewBookMeta(BookMeta)} to change what will actually be added to the + * book. + * + * @return the book meta that the player is attempting to add + */ + public BookMeta getNewBookMeta() { + return newBookMeta.clone(); + } + + /** + * Gets the inventory slot number for the book item that triggered this + * event. + *

          + * This is a slot number on the player's hotbar in the range 0-8. + * + * @return the inventory slot number that the book item occupies + */ + public int getSlot() { + return slot; + } + + /** + * Sets the book meta that will actually be added to the book. + * + * @param newBookMeta new book meta + * @throws IllegalArgumentException if the new book meta is null + */ + public void setNewBookMeta(BookMeta newBookMeta) throws IllegalArgumentException { + Validate.notNull(newBookMeta, "New book meta must not be null"); + Bukkit.getItemFactory().equals(newBookMeta, null); + this.newBookMeta = newBookMeta.clone(); + } + + /** + * Gets whether or not the book is being signed. If a book is signed the + * Material changes from BOOK_AND_QUILL to WRITTEN_BOOK. + * + * @return true if the book is being signed + */ + public boolean isSigning() { + return isSigning; + } + + /** + * Sets whether or not the book is being signed. If a book is signed the + * Material changes from BOOK_AND_QUILL to WRITTEN_BOOK. + * + * @param signing whether or not the book is being signed. + */ + public void setSigning(boolean signing) { + isSigning = signing; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEggThrowEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEggThrowEvent.java new file mode 100644 index 0000000..896347e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEggThrowEvent.java @@ -0,0 +1,137 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.CreatureType; +import org.bukkit.entity.Egg; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a player throws an egg and it might hatch + */ +public class PlayerEggThrowEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final Egg egg; + private boolean hatching; + private EntityType hatchType; + private byte numHatches; + + public PlayerEggThrowEvent(final Player player, final Egg egg, final boolean hatching, final byte numHatches, final EntityType hatchingType) { + super(player); + this.egg = egg; + this.hatching = hatching; + this.numHatches = numHatches; + this.hatchType = hatchingType; + } + + @Deprecated + public PlayerEggThrowEvent(Player player, Egg egg, boolean hatching, byte numHatches, CreatureType hatchingType) { + this(player, egg, hatching, numHatches, hatchingType.toEntityType()); + } + + /** + * Gets the egg involved in this event. + * + * @return the egg involved in this event + */ + public Egg getEgg() { + return egg; + } + + /** + * Gets whether the egg is hatching or not. Will be what the server + * would've done without interaction. + * + * @return boolean Whether the egg is going to hatch or not + */ + public boolean isHatching() { + return hatching; + } + + /** + * Sets whether the egg will hatch or not. + * + * @param hatching true if you want the egg to hatch, false if you want it + * not to + */ + public void setHatching(boolean hatching) { + this.hatching = hatching; + } + + /** + * Get the type of the mob being hatched (EntityType.CHICKEN by default) + * + * @return The type of the mob being hatched by the egg + * @deprecated In favour of {@link #getHatchingType()}. + */ + @Deprecated + public CreatureType getHatchType() { + return CreatureType.fromEntityType(hatchType); + } + + /** + * Get the type of the mob being hatched (EntityType.CHICKEN by default) + * + * @return The type of the mob being hatched by the egg + */ + public EntityType getHatchingType() { + return hatchType; + } + + /** + * Change the type of mob being hatched by the egg + * + * @param hatchType The type of the mob being hatched by the egg + * @deprecated In favour of {@link #setHatchingType(EntityType)}. + */ + @Deprecated + public void setHatchType(CreatureType hatchType) { + this.hatchType = hatchType.toEntityType(); + } + + /** + * Change the type of mob being hatched by the egg + * + * @param hatchType The type of the mob being hatched by the egg + */ + public void setHatchingType(EntityType hatchType) { + if(!hatchType.isSpawnable()) throw new IllegalArgumentException("Can't spawn that entity type from an egg!"); + this.hatchType = hatchType; + } + + /** + * Get the number of mob hatches from the egg. By default the number will + * be the number the server would've done + *

            + *
          • 7/8 chance of being 0 + *
          • 31/256 ~= 1/8 chance to be 1 + *
          • 1/256 chance to be 4 + *
          + * + * @return The number of mobs going to be hatched by the egg + */ + public byte getNumHatches() { + return numHatches; + } + + /** + * Change the number of mobs coming out of the hatched egg + *

          + * The boolean hatching will override this number. Ie. If hatching = + * false, this number will not matter + * + * @param numHatches The number of mobs coming out of the egg + */ + public void setNumHatches(byte numHatches) { + this.numHatches = numHatches; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEvent.java new file mode 100644 index 0000000..0d4833f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerEvent.java @@ -0,0 +1,30 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; + +/** + * Represents a player related event + */ +public abstract class PlayerEvent extends Event { + protected Player player; + + public PlayerEvent(final Player who) { + player = who; + } + + PlayerEvent(final Player who, boolean async) { + super(async); + player = who; + + } + + /** + * Returns the player involved in this event + * + * @return Player who is involved in this event + */ + public final Player getPlayer() { + return player; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerExpChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerExpChangeEvent.java new file mode 100644 index 0000000..f37491d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerExpChangeEvent.java @@ -0,0 +1,44 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a players experience changes naturally + */ +public class PlayerExpChangeEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private int exp; + + public PlayerExpChangeEvent(final Player player, final int expAmount) { + super(player); + exp = expAmount; + } + + /** + * Get the amount of experience the player will receive + * + * @return The amount of experience + */ + public int getAmount() { + return exp; + } + + /** + * Set the amount of experience the player will receive + * + * @param amount The amount of experience to set + */ + public void setAmount(int amount) { + exp = amount; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerFishEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerFishEvent.java new file mode 100644 index 0000000..7416a0a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerFishEvent.java @@ -0,0 +1,138 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Fish; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; + +/** + * Thrown when a player is fishing + */ +public class PlayerFishEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Entity entity; + private boolean cancel = false; + private int exp; + private final State state; + private final Fish hookEntity; + + /** + * @deprecated replaced by {@link #PlayerFishEvent(Player, Entity, Fish, + * State)} to include the {@link Fish} hook entity. + * @param player + * @param entity + * @param state + */ + @Deprecated + public PlayerFishEvent(final Player player, final Entity entity, final State state) { + this(player, entity, null, state); + } + + public PlayerFishEvent(final Player player, final Entity entity, final Fish hookEntity, final State state) { + super(player); + this.entity = entity; + this.hookEntity = hookEntity; + this.state = state; + } + + /** + * Gets the entity caught by the player. + *

          + * If player has fished successfully, the result may be cast to {@link + * Item}. + * + * @return Entity caught by the player, Entity if fishing, and null if + * bobber has gotten stuck in the ground or nothing has been caught + */ + public Entity getCaught() { + return entity; + } + + /** + * Gets the fishing hook. + * + * @return Fish the entity representing the fishing hook/bobber. + */ + public Fish getHook() { + return hookEntity; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the amount of experience received when fishing. + *

          + * Note: This value has no default effect unless the event state is {@link + * State#CAUGHT_FISH}. + * + * @return the amount of experience to drop + */ + public int getExpToDrop() { + return exp; + } + + /** + * Sets the amount of experience received when fishing. + *

          + * Note: This value has no default effect unless the event state is {@link + * State#CAUGHT_FISH}. + * + * @param amount the amount of experience to drop + */ + public void setExpToDrop(int amount) { + exp = amount; + } + + /** + * Gets the state of the fishing + * + * @return A State detailing the state of the fishing + */ + public State getState() { + return state; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * An enum to specify the state of the fishing + */ + public enum State { + + /** + * When a player is fishing, ie casting the line out. + */ + FISHING, + /** + * When a player has successfully caught a fish and is reeling it in. + */ + CAUGHT_FISH, + /** + * When a player has successfully caught an entity + */ + CAUGHT_ENTITY, + /** + * When a bobber is stuck in the ground + */ + IN_GROUND, + /** + * When a player fails to catch anything while fishing usually due to + * poor aiming or timing + */ + FAILED_ATTEMPT, + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerGameModeChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerGameModeChangeEvent.java new file mode 100644 index 0000000..8c9afa8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerGameModeChangeEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.player; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when the GameMode of the player is changed. + */ +public class PlayerGameModeChangeEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final GameMode newGameMode; + + public PlayerGameModeChangeEvent(final Player player, final GameMode newGameMode) { + super(player); + this.newGameMode = newGameMode; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * Gets the GameMode the player is switched to. + * + * @return player's new GameMode + */ + public GameMode getNewGameMode() { + return newGameMode; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerHealthChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerHealthChangeEvent.java new file mode 100644 index 0000000..0ca0c8b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerHealthChangeEvent.java @@ -0,0 +1,37 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +public class PlayerHealthChangeEvent extends PlayerEvent { + + private static final HandlerList handlers = new HandlerList(); + + private final double previousHealth; + private final double newHealth; + + public PlayerHealthChangeEvent(final Player player, final double previousHealth, final double newHealth) { + super(player); + + this.previousHealth = previousHealth; + this.newHealth = newHealth; + } + + public double getPreviousHealth() { + return previousHealth; + } + + public double getNewHealth() { + return newHealth; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInteractEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInteractEntityEvent.java new file mode 100644 index 0000000..935211d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInteractEntityEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Represents an event that is called when a player right clicks an entity. + */ +public class PlayerInteractEntityEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + protected Entity clickedEntity; + boolean cancelled = false; + + public PlayerInteractEntityEvent(final Player who, final Entity clickedEntity) { + super(who); + this.clickedEntity = clickedEntity; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * Gets the entity that was rightclicked by the player. + * + * @return entity right clicked by player + */ + public Entity getRightClicked() { + return this.clickedEntity; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java new file mode 100644 index 0000000..59567d9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java @@ -0,0 +1,187 @@ +package org.bukkit.event.player; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.block.Action; + +/** + * Called when a player interacts with an object or air. + */ +public class PlayerInteractEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + protected ItemStack item; + protected Action action; + protected Block blockClicked; + protected BlockFace blockFace; + private Result useClickedBlock; + private Result useItemInHand; + + public PlayerInteractEvent(final Player who, final Action action, final ItemStack item, final Block clickedBlock, final BlockFace clickedFace) { + super(who); + this.action = action; + this.item = item; + this.blockClicked = clickedBlock; + this.blockFace = clickedFace; + + useItemInHand = Result.DEFAULT; + useClickedBlock = clickedBlock == null ? Result.DENY : Result.ALLOW; + } + + /** + * Returns the action type + * + * @return Action returns the type of interaction + */ + public Action getAction() { + return action; + } + + /** + * Gets the cancellation state of this event. Set to true if you want to + * prevent buckets from placing water and so forth + * + * @return boolean cancellation state + */ + public boolean isCancelled() { + return useInteractedBlock() == Result.DENY; + } + + /** + * Sets the cancellation state of this event. A canceled event will not be + * executed in the server, but will still pass to other plugins + *

          + * Canceling this event will prevent use of food (player won't lose the + * food item), prevent bows/snowballs/eggs from firing, etc. (player won't + * lose the ammo) + * + * @param cancel true if you wish to cancel this event + */ + public void setCancelled(boolean cancel) { + setUseInteractedBlock(cancel ? Result.DENY : useInteractedBlock() == Result.DENY ? Result.DEFAULT : useInteractedBlock()); + setUseItemInHand(cancel ? Result.DENY : useItemInHand() == Result.DENY ? Result.DEFAULT : useItemInHand()); + } + + /** + * Returns the item in hand represented by this event + * + * @return ItemStack the item used + */ + public ItemStack getItem() { + return this.item; + } + + /** + * Convenience method. Returns the material of the item represented by + * this event + * + * @return Material the material of the item used + */ + public Material getMaterial() { + if (!hasItem()) { + return Material.AIR; + } + + return item.getType(); + } + + /** + * Check if this event involved a block + * + * @return boolean true if it did + */ + public boolean hasBlock() { + return this.blockClicked != null; + } + + /** + * Check if this event involved an item + * + * @return boolean true if it did + */ + public boolean hasItem() { + return this.item != null; + } + + /** + * Convenience method to inform the user whether this was a block + * placement event. + * + * @return boolean true if the item in hand was a block + */ + public boolean isBlockInHand() { + if (!hasItem()) { + return false; + } + + return item.getType().isBlock(); + } + + /** + * Returns the clicked block + * + * @return Block returns the block clicked with this item. + */ + public Block getClickedBlock() { + return blockClicked; + } + + /** + * Returns the face of the block that was clicked + * + * @return BlockFace returns the face of the block that was clicked + */ + public BlockFace getBlockFace() { + return blockFace; + } + + /** + * This controls the action to take with the block (if any) that was + * clicked on. This event gets processed for all blocks, but most don't + * have a default action + * + * @return the action to take with the interacted block + */ + public Result useInteractedBlock() { + return useClickedBlock; + } + + /** + * @param useInteractedBlock the action to take with the interacted block + */ + public void setUseInteractedBlock(Result useInteractedBlock) { + this.useClickedBlock = useInteractedBlock; + } + + /** + * This controls the action to take with the item the player is holding. + * This includes both blocks and items (such as flint and steel or + * records). When this is set to default, it will be allowed if no action + * is taken on the interacted block. + * + * @return the action to take with the item in hand + */ + public Result useItemInHand() { + return useItemInHand; + } + + /** + * @param useItemInHand the action to take with the item in hand + */ + public void setUseItemInHand(Result useItemInHand) { + this.useItemInHand = useItemInHand; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInventoryEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInventoryEvent.java new file mode 100644 index 0000000..2ec69d7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerInventoryEvent.java @@ -0,0 +1,44 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.inventory.Inventory; + +/** + * Represents a player related inventory event; note that this event never + * actually did anything + * + * @deprecated Use {@link InventoryClickEvent} or {@link InventoryOpenEvent} + * instead, or one of the other inventory events in {@link + * org.bukkit.event.inventory}. + */ +@Deprecated +public class PlayerInventoryEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + protected Inventory inventory; + + public PlayerInventoryEvent(final Player player, final Inventory inventory) { + super(player); + this.inventory = inventory; + } + + /** + * Gets the Inventory involved in this event + * + * @return Inventory + */ + public Inventory getInventory() { + return inventory; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemBreakEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemBreakEvent.java new file mode 100644 index 0000000..176cd91 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemBreakEvent.java @@ -0,0 +1,39 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * Fired when a player's item breaks (such as a shovel or flint and steel). + *

          + * The item that's breaking will exist in the inventory with a stack size of + * 0. After the event, the item's durability will be reset to 0. + */ +public class PlayerItemBreakEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final ItemStack brokenItem; + + public PlayerItemBreakEvent(final Player player, final ItemStack brokenItem) { + super(player); + this.brokenItem = brokenItem; + } + + /** + * Gets the item that broke + * + * @return The broken item + */ + public ItemStack getBrokenItem() { + return brokenItem; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java new file mode 100644 index 0000000..8ab76b1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java @@ -0,0 +1,74 @@ +package org.bukkit.event.player; + +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * This event will fire when a player is finishing consuming an item (food, + * potion, milk bucket). + *
          + * If the ItemStack is modified the server will use the effects of the new + * item and not remove the original one from the player's inventory. + *
          + * If the event is cancelled the effect will not be applied and the item will + * not be removed from the player's inventory. + */ +public class PlayerItemConsumeEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean isCancelled = false; + private ItemStack item; + + /** + * @param player the player consuming + * @param item the ItemStack being consumed + */ + public PlayerItemConsumeEvent(final Player player, final ItemStack item) { + super(player); + + this.item = item; + } + + /** + * Gets the item that is being consumed. Modifying the returned item will + * have no effect, you must use {@link + * #setItem(org.bukkit.inventory.ItemStack)} instead. + * + * @return an ItemStack for the item being consumed + */ + public ItemStack getItem() { + return item.clone(); + } + + /** + * Set the item being consumed + * + * @param item the item being consumed + */ + public void setItem(ItemStack item) { + if (item == null) { + this.item = new ItemStack(Material.AIR); + } else { + this.item = item; + } + } + + public boolean isCancelled() { + return this.isCancelled; + } + + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemDamageEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemDamageEvent.java new file mode 100644 index 0000000..38a72ab --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemDamageEvent.java @@ -0,0 +1,54 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +public class PlayerItemDamageEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private final ItemStack item; + private int damage; + private boolean cancelled = false; + + public PlayerItemDamageEvent(Player player, ItemStack what, int damage) { + super(player); + this.item = what; + this.damage = damage; + } + + public ItemStack getItem() { + return item; + } + + /** + * Gets the amount of durability damage this item will be taking. + * + * @return durability change + */ + public int getDamage() { + return damage; + } + + public void setDamage(int damage) { + this.damage = damage; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemHeldEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemHeldEvent.java new file mode 100644 index 0000000..f0d055a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerItemHeldEvent.java @@ -0,0 +1,56 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Fired when a player changes their currently held item + */ +public class PlayerItemHeldEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private final int previous; + private final int current; + + public PlayerItemHeldEvent(final Player player, final int previous, final int current) { + super(player); + this.previous = previous; + this.current = current; + } + + /** + * Gets the previous held slot index + * + * @return Previous slot index + */ + public int getPreviousSlot() { + return previous; + } + + /** + * Gets the new held slot index + * + * @return New slot index + */ + public int getNewSlot() { + return current; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java new file mode 100644 index 0000000..e7481f9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java @@ -0,0 +1,44 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a player joins a server + */ +public class PlayerJoinEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private String joinMessage; + + public PlayerJoinEvent(final Player playerJoined, final String joinMessage) { + super(playerJoined); + this.joinMessage = joinMessage; + } + + /** + * Gets the join message to send to all online players + * + * @return string join message + */ + public String getJoinMessage() { + return joinMessage; + } + + /** + * Sets the join message to send to all online players + * + * @param joinMessage join message + */ + public void setJoinMessage(String joinMessage) { + this.joinMessage = joinMessage; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerKickEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerKickEvent.java new file mode 100644 index 0000000..39e81b6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerKickEvent.java @@ -0,0 +1,75 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a player gets kicked from the server + */ +public class PlayerKickEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private String leaveMessage; + private String kickReason; + private Boolean cancel; + + public PlayerKickEvent(final Player playerKicked, final String kickReason, final String leaveMessage) { + super(playerKicked); + this.kickReason = kickReason; + this.leaveMessage = leaveMessage; + this.cancel = false; + } + + /** + * Gets the reason why the player is getting kicked + * + * @return string kick reason + */ + public String getReason() { + return kickReason; + } + + /** + * Gets the leave message send to all online players + * + * @return string kick reason + */ + public String getLeaveMessage() { + return leaveMessage; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Sets the reason why the player is getting kicked + * + * @param kickReason kick reason + */ + public void setReason(String kickReason) { + this.kickReason = kickReason; + } + + /** + * Sets the leave message send to all online players + * + * @param leaveMessage leave message + */ + public void setLeaveMessage(String leaveMessage) { + this.leaveMessage = leaveMessage; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerLevelChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerLevelChangeEvent.java new file mode 100644 index 0000000..730a776 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerLevelChangeEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a players level changes + */ +public class PlayerLevelChangeEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final int oldLevel; + private final int newLevel; + + public PlayerLevelChangeEvent(final Player player, final int oldLevel, final int newLevel) { + super(player); + this.oldLevel = oldLevel; + this.newLevel = newLevel; + } + + /** + * Gets the old level of the player + * + * @return The old level of the player + */ + public int getOldLevel() { + return oldLevel; + } + + /** + * Gets the new level of the player + * + * @return The new (current) level of the player + */ + public int getNewLevel() { + return newLevel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java new file mode 100644 index 0000000..081e994 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java @@ -0,0 +1,207 @@ +package org.bukkit.event.player; + +import java.net.InetAddress; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Stores details for players attempting to log in + */ +public class PlayerLoginEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final InetAddress address; + private final String hostname; + private Result result = Result.ALLOWED; + private String message = ""; + private final InetAddress realAddress; // Spigot + + /** + * @deprecated Address should be provided in other constructor + */ + @Deprecated + public PlayerLoginEvent(final Player player) { + this(player, "", null); + } + + /** + * @deprecated Address should be provided in other constructor + */ + @Deprecated + public PlayerLoginEvent(final Player player, final String hostname) { + this(player, hostname, null); + } + + /** + * This constructor defaults message to an empty string, and result to + * ALLOWED + * + * @param player The {@link Player} for this event + * @param hostname The hostname that was used to connect to the server + * @param address The address the player used to connect, provided for + * timing issues + */ + public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address, final InetAddress realAddress) { // Spigot + super(player); + this.hostname = hostname; + this.address = address; + // Spigot start + this.realAddress = realAddress; + } + + public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address) { + this(player, hostname, address, address); + // Spigot end + } + + /** + * @deprecated Address and hostname should be provided in other + * constructor + */ + @Deprecated + public PlayerLoginEvent(final Player player, final Result result, final String message) { + this(player, "", null, result, message, null); // Spigot + } + + /** + * This constructor pre-configures the event with a result and message + * + * @param player The {@link Player} for this event + * @param hostname The hostname that was used to connect to the server + * @param address The address the player used to connect, provided for + * timing issues + * @param result The result status for this event + * @param message The message to be displayed if result denies login + */ + public PlayerLoginEvent(final Player player, String hostname, final InetAddress address, final Result result, final String message, final InetAddress realAddress) { // Spigot + this(player, hostname, address, realAddress); // Spigot + this.result = result; + this.message = message; + } + + // Spigot start + /** + * Gets the connection address of this player, regardless of whether it has been spoofed or not. + * + * @return the player's connection address + */ + public InetAddress getRealAddress() { + return realAddress; + } + // Spigot end + + /** + * Gets the current result of the login, as an enum + * + * @return Current Result of the login + */ + public Result getResult() { + return result; + } + + /** + * Sets the new result of the login, as an enum + * + * @param result New result to set + */ + public void setResult(final Result result) { + this.result = result; + } + + /** + * Gets the current kick message that will be used if getResult() != + * Result.ALLOWED + * + * @return Current kick message + */ + public String getKickMessage() { + return message; + } + + /** + * Sets the kick message to display if getResult() != Result.ALLOWED + * + * @param message New kick message + */ + public void setKickMessage(final String message) { + this.message = message; + } + + /** + * Gets the hostname that the player used to connect to the server, or + * blank if unknown + * + * @return The hostname + */ + public String getHostname() { + return hostname; + } + + /** + * Allows the player to log in + */ + public void allow() { + result = Result.ALLOWED; + message = ""; + } + + /** + * Disallows the player from logging in, with the given reason + * + * @param result New result for disallowing the player + * @param message Kick message to display to the user + */ + public void disallow(final Result result, final String message) { + this.result = result; + this.message = message; + } + + /** + * Gets the {@link InetAddress} for the Player associated with this event. + * This method is provided as a workaround for player.getAddress() + * returning null during PlayerLoginEvent. + * + * @return The address for this player. For legacy compatibility, this may + * be null. + */ + public InetAddress getAddress() { + return address; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * Basic kick reasons for communicating to plugins + */ + public enum Result { + + /** + * The player is allowed to log in + */ + ALLOWED, + /** + * The player is not allowed to log in, due to the server being full + */ + KICK_FULL, + /** + * The player is not allowed to log in, due to them being banned + */ + KICK_BANNED, + /** + * The player is not allowed to log in, due to them not being on the + * white list + */ + KICK_WHITELIST, + /** + * The player is not allowed to log in, for reasons undefined + */ + KICK_OTHER + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java new file mode 100644 index 0000000..fa3b340 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java @@ -0,0 +1,95 @@ +package org.bukkit.event.player; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Holds information for player movement events + */ +public class PlayerMoveEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private Location from; + private Location to; + + public PlayerMoveEvent(final Player player, final Location from, final Location to) { + super(player); + this.from = from; + this.to = to; + } + + /** + * Gets the cancellation state of this event. A cancelled event will not + * be executed in the server, but will still pass to other plugins + *

          + * If a move or teleport event is cancelled, the player will be moved or + * teleported back to the Location as defined by getFrom(). This will not + * fire an event + * + * @return true if this event is cancelled + */ + public boolean isCancelled() { + return cancel; + } + + /** + * Sets the cancellation state of this event. A cancelled event will not + * be executed in the server, but will still pass to other plugins + *

          + * If a move or teleport event is cancelled, the player will be moved or + * teleported back to the Location as defined by getFrom(). This will not + * fire an event + * + * @param cancel true if you wish to cancel this event + */ + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the location this player moved from + * + * @return Location the player moved from + */ + public Location getFrom() { + return from; + } + + /** + * Sets the location to mark as where the player moved from + * + * @param from New location to mark as the players previous location + */ + public void setFrom(Location from) { + this.from = from; + } + + /** + * Gets the location this player moved to + * + * @return Location the player moved to + */ + public Location getTo() { + return to; + } + + /** + * Sets the location that this player will move to + * + * @param to New Location this player will move to + */ + public void setTo(Location to) { + this.to = to; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPearlRefundEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPearlRefundEvent.java new file mode 100644 index 0000000..7491f12 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPearlRefundEvent.java @@ -0,0 +1,21 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +public class PlayerPearlRefundEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + + public PlayerPearlRefundEvent(final Player player) { + super(player); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPickupItemEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPickupItemEvent.java new file mode 100644 index 0000000..dfba816 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPickupItemEvent.java @@ -0,0 +1,57 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Thrown when a player picks an item up from the ground + */ +public class PlayerPickupItemEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Item item; + private boolean cancel = false; + private final int remaining; + + public PlayerPickupItemEvent(final Player player, final Item item, final int remaining) { + super(player); + this.item = item; + this.remaining = remaining; + } + + /** + * Gets the Item picked up by the player. + * + * @return Item + */ + public Item getItem() { + return item; + } + + /** + * Gets the amount remaining on the ground, if any + * + * @return amount remaining on the ground + */ + public int getRemaining() { + return remaining; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPortalEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPortalEvent.java new file mode 100644 index 0000000..93752f7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPortalEvent.java @@ -0,0 +1,87 @@ +package org.bukkit.event.player; + +import org.bukkit.Location; +import org.bukkit.TravelAgent; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a player is about to teleport because it is in contact with a + * portal. + *

          + * For other entities see {@link org.bukkit.event.entity.EntityPortalEvent} + */ +public class PlayerPortalEvent extends PlayerTeleportEvent { + private static final HandlerList handlers = new HandlerList(); + protected boolean useTravelAgent = true; + protected TravelAgent travelAgent; + + public PlayerPortalEvent(final Player player, final Location from, final Location to, final TravelAgent pta) { + super(player, from, to); + this.travelAgent = pta; + } + + public PlayerPortalEvent(Player player, Location from, Location to, TravelAgent pta, TeleportCause cause) { + super(player, from, to, cause); + this.travelAgent = pta; + } + + /** + * Sets whether or not the Travel Agent will be used. + *

          + * If this is set to true, the TravelAgent will try to find a Portal at + * the {@link #getTo()} Location, and will try to create one if there is + * none. + *

          + * If this is set to false, the {@link #getPlayer()} will only be + * teleported to the {@link #getTo()} Location. + * + * @param useTravelAgent whether to use the Travel Agent + */ + public void useTravelAgent(boolean useTravelAgent) { + this.useTravelAgent = useTravelAgent; + } + + /** + * Gets whether or not the Travel Agent will be used. + *

          + * If this is set to true, the TravelAgent will try to find a Portal at + * the {@link #getTo()} Location, and will try to create one if there is + * none. + *

          + * If this is set to false, the {@link #getPlayer()}} will only be + * teleported to the {@link #getTo()} Location. + * + * @return whether to use the Travel Agent + */ + public boolean useTravelAgent() { + return useTravelAgent && travelAgent != null; + } + + /** + * Gets the Travel Agent used (or not) in this event. + * + * @return the Travel Agent used (or not) in this event + */ + public TravelAgent getPortalTravelAgent() { + return this.travelAgent; + } + + /** + * Sets the Travel Agent used (or not) in this event. + * + * @param travelAgent the Travel Agent used (or not) in this event + */ + public void setPortalTravelAgent(TravelAgent travelAgent) { + this.travelAgent = travelAgent; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java new file mode 100644 index 0000000..e8553f0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java @@ -0,0 +1,159 @@ +package org.bukkit.event.player; + +import java.net.InetAddress; +import java.util.UUID; + +import org.bukkit.Warning; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * Stores details for players attempting to log in + * + * @deprecated This event causes synchronization from the login thread; {@link + * AsyncPlayerPreLoginEvent} is preferred to keep the secondary threads + * asynchronous. + */ +@Deprecated +@Warning(reason="This event causes a login thread to synchronize with the main thread") +public class PlayerPreLoginEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + private Result result; + private String message; + private final String name; + private final InetAddress ipAddress; + private final UUID uniqueId; + + @Deprecated + public PlayerPreLoginEvent(final String name, final InetAddress ipAddress) { + this(name, ipAddress, null); + } + + public PlayerPreLoginEvent(final String name, final InetAddress ipAddress, final UUID uniqueId) { + this.result = Result.ALLOWED; + this.message = ""; + this.name = name; + this.ipAddress = ipAddress; + this.uniqueId = uniqueId; + } + + /** + * Gets the current result of the login, as an enum + * + * @return Current Result of the login + */ + public Result getResult() { + return result; + } + + /** + * Sets the new result of the login, as an enum + * + * @param result New result to set + */ + public void setResult(final Result result) { + this.result = result; + } + + /** + * Gets the current kick message that will be used if getResult() != + * Result.ALLOWED + * + * @return Current kick message + */ + public String getKickMessage() { + return message; + } + + /** + * Sets the kick message to display if getResult() != Result.ALLOWED + * + * @param message New kick message + */ + public void setKickMessage(final String message) { + this.message = message; + } + + /** + * Allows the player to log in + */ + public void allow() { + result = Result.ALLOWED; + message = ""; + } + + /** + * Disallows the player from logging in, with the given reason + * + * @param result New result for disallowing the player + * @param message Kick message to display to the user + */ + public void disallow(final Result result, final String message) { + this.result = result; + this.message = message; + } + + /** + * Gets the player's name. + * + * @return the player's name + */ + public String getName() { + return name; + } + + /** + * Gets the player IP address. + * + * @return The IP address + */ + public InetAddress getAddress() { + return ipAddress; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + /** + * Gets the player's unique ID. + * + * @return The unique ID + */ + public UUID getUniqueId() { + return uniqueId; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * Basic kick reasons for communicating to plugins + */ + public enum Result { + + /** + * The player is allowed to log in + */ + ALLOWED, + /** + * The player is not allowed to log in, due to the server being full + */ + KICK_FULL, + /** + * The player is not allowed to log in, due to them being banned + */ + KICK_BANNED, + /** + * The player is not allowed to log in, due to them not being on the + * white list + */ + KICK_WHITELIST, + /** + * The player is not allowed to log in, for reasons undefined + */ + KICK_OTHER + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java new file mode 100644 index 0000000..5c8dc1b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java @@ -0,0 +1,44 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a player leaves a server + */ +public class PlayerQuitEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private String quitMessage; + + public PlayerQuitEvent(final Player who, final String quitMessage) { + super(who); + this.quitMessage = quitMessage; + } + + /** + * Gets the quit message to send to all online players + * + * @return string quit message + */ + public String getQuitMessage() { + return quitMessage; + } + + /** + * Sets the quit message to send to all online players + * + * @param quitMessage quit message + */ + public void setQuitMessage(String quitMessage) { + this.quitMessage = quitMessage; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerRegisterChannelEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerRegisterChannelEvent.java new file mode 100644 index 0000000..442ac7f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerRegisterChannelEvent.java @@ -0,0 +1,13 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; + +/** + * This is called immediately after a player registers for a plugin channel. + */ +public class PlayerRegisterChannelEvent extends PlayerChannelEvent { + + public PlayerRegisterChannelEvent(final Player player, final String channel) { + super(player, channel); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerRespawnEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerRespawnEvent.java new file mode 100644 index 0000000..35900dd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerRespawnEvent.java @@ -0,0 +1,60 @@ +package org.bukkit.event.player; + +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Called when a player respawns. + */ +public class PlayerRespawnEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private Location respawnLocation; + private final boolean isBedSpawn; + + public PlayerRespawnEvent(final Player respawnPlayer, final Location respawnLocation, final boolean isBedSpawn) { + super(respawnPlayer); + this.respawnLocation = respawnLocation; + this.isBedSpawn = isBedSpawn; + } + + /** + * Gets the current respawn location + * + * @return Location current respawn location + */ + public Location getRespawnLocation() { + return this.respawnLocation; + } + + /** + * Sets the new respawn location + * + * @param respawnLocation new location for the respawn + */ + public void setRespawnLocation(Location respawnLocation) { + Validate.notNull(respawnLocation, "Respawn location can not be null"); + Validate.notNull(respawnLocation.getWorld(), "Respawn world can not be null"); + + this.respawnLocation = respawnLocation; + } + + /** + * Gets whether the respawn location is the player's bed. + * + * @return true if the respawn location is the player's bed. + */ + public boolean isBedSpawn() { + return this.isBedSpawn; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerShearEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerShearEntityEvent.java new file mode 100644 index 0000000..38afb3c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerShearEntityEvent.java @@ -0,0 +1,48 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a player shears an entity + */ +public class PlayerShearEntityEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel; + private final Entity what; + + public PlayerShearEntityEvent(final Player who, final Entity what) { + super(who); + this.cancel = false; + this.what = what; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the entity the player is shearing + * + * @return the entity the player is shearing + */ + public Entity getEntity() { + return what; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java new file mode 100644 index 0000000..3b64d70 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java @@ -0,0 +1,116 @@ +package org.bukkit.event.player; + +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a player statistic is incremented. + *

          + * This event is not called for {@link org.bukkit.Statistic#PLAY_ONE_TICK} or + * movement based statistics. + * + */ +public class PlayerStatisticIncrementEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + protected final Statistic statistic; + private final int initialValue; + private final int newValue; + private boolean isCancelled = false; + private final EntityType entityType; + private final Material material; + + public PlayerStatisticIncrementEvent(Player player, Statistic statistic, int initialValue, int newValue) { + super (player); + this.statistic = statistic; + this.initialValue = initialValue; + this.newValue = newValue; + this.entityType = null; + this.material = null; + } + + public PlayerStatisticIncrementEvent(Player player, Statistic statistic, int initialValue, int newValue, EntityType entityType) { + super (player); + this.statistic = statistic; + this.initialValue = initialValue; + this.newValue = newValue; + this.entityType = entityType; + this.material = null; + } + + public PlayerStatisticIncrementEvent(Player player, Statistic statistic, int initialValue, int newValue, Material material) { + super (player); + this.statistic = statistic; + this.initialValue = initialValue; + this.newValue = newValue; + this.entityType = null; + this.material = material; + } + + /** + * Gets the statistic that is being incremented. + * + * @return the incremented statistic + */ + public Statistic getStatistic() { + return statistic; + } + + /** + * Gets the previous value of the statistic. + * + * @return the previous value of the statistic + */ + public int getPreviousValue() { + return initialValue; + } + + /** + * Gets the new value of the statistic. + * + * @return the new value of the statistic + */ + public int getNewValue() { + return newValue; + } + + /** + * Gets the EntityType if {@link #getStatistic() getStatistic()} is an + * entity statistic otherwise returns null. + * + * @return the EntityType of the statistic + */ + public EntityType getEntityType() { + return entityType; + } + + /** + * Gets the Material if {@link #getStatistic() getStatistic()} is a block + * or item statistic otherwise returns null. + * + * @return the Material of the statistic + */ + public Material getMaterial() { + return material; + } + + public boolean isCancelled() { + return isCancelled; + } + + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerTeleportEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerTeleportEvent.java new file mode 100644 index 0000000..7e2e128 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerTeleportEvent.java @@ -0,0 +1,73 @@ +package org.bukkit.event.player; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Holds information for player teleport events + */ +public class PlayerTeleportEvent extends PlayerMoveEvent { + private static final HandlerList handlers = new HandlerList(); + private TeleportCause cause = TeleportCause.UNKNOWN; + + public PlayerTeleportEvent(final Player player, final Location from, final Location to) { + super(player, from, to); + } + + public PlayerTeleportEvent(final Player player, final Location from, final Location to, final TeleportCause cause) { + this(player, from, to); + + this.cause = cause; + } + + /** + * Gets the cause of this teleportation event + * + * @return Cause of the event + */ + public TeleportCause getCause() { + return cause; + } + + public enum TeleportCause { + /** + * Indicates the teleporation was caused by a player throwing an Ender + * Pearl + */ + ENDER_PEARL, + /** + * Indicates the teleportation was caused by a player executing a + * command + */ + COMMAND, + /** + * Indicates the teleportation was caused by a plugin + */ + PLUGIN, + /** + * Indicates the teleportation was caused by a player entering a + * Nether portal + */ + NETHER_PORTAL, + /** + * Indicates the teleportation was caused by a player entering an End + * portal + */ + END_PORTAL, + /** + * Indicates the teleportation was caused by an event not covered by + * this enum + */ + UNKNOWN; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleFlightEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleFlightEvent.java new file mode 100644 index 0000000..1c5ec37 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleFlightEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a player toggles their flying state + */ +public class PlayerToggleFlightEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final boolean isFlying; + private boolean cancel = false; + + public PlayerToggleFlightEvent(final Player player, final boolean isFlying) { + super(player); + this.isFlying = isFlying; + } + + /** + * Returns whether the player is trying to start or stop flying. + * + * @return flying state + */ + public boolean isFlying() { + return isFlying; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleSneakEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleSneakEvent.java new file mode 100644 index 0000000..667acad --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleSneakEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a player toggles their sneaking state + */ +public class PlayerToggleSneakEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final boolean isSneaking; + private boolean cancel = false; + + public PlayerToggleSneakEvent(final Player player, final boolean isSneaking) { + super(player); + this.isSneaking = isSneaking; + } + + /** + * Returns whether the player is now sneaking or not. + * + * @return sneaking state + */ + public boolean isSneaking() { + return isSneaking; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleSprintEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleSprintEvent.java new file mode 100644 index 0000000..cf065e1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerToggleSprintEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a player toggles their sprinting state + */ +public class PlayerToggleSprintEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final boolean isSprinting; + private boolean cancel = false; + + public PlayerToggleSprintEvent(final Player player, final boolean isSprinting) { + super(player); + this.isSprinting = isSprinting; + } + + /** + * Gets whether the player is now sprinting or not. + * + * @return sprinting state + */ + public boolean isSprinting() { + return isSprinting; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java new file mode 100644 index 0000000..f6aebef --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java @@ -0,0 +1,36 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.entity.EntityUnleashEvent; + +/** + * Called prior to an entity being unleashed due to a player's action. + */ +public class PlayerUnleashEntityEvent extends EntityUnleashEvent implements Cancellable { + private final Player player; + private boolean cancelled = false; + + public PlayerUnleashEntityEvent(Entity entity, Player player) { + super(entity, UnleashReason.PLAYER_UNLEASH); + this.player = player; + } + + /** + * Returns the player who is unleashing the entity. + * + * @return The player + */ + public Player getPlayer() { + return player; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerUnregisterChannelEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerUnregisterChannelEvent.java new file mode 100644 index 0000000..11c77e3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerUnregisterChannelEvent.java @@ -0,0 +1,13 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; + +/** + * This is called immediately after a player unregisters for a plugin channel. + */ +public class PlayerUnregisterChannelEvent extends PlayerChannelEvent { + + public PlayerUnregisterChannelEvent(final Player player, final String channel) { + super(player, channel); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/player/PlayerVelocityEvent.java b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerVelocityEvent.java new file mode 100644 index 0000000..69d2fce --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/player/PlayerVelocityEvent.java @@ -0,0 +1,55 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.util.Vector; + +/** + * Called when the velocity of a player changes. + */ +public class PlayerVelocityEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private Vector velocity; + + public PlayerVelocityEvent(final Player player, final Vector velocity) { + super(player); + this.velocity = velocity; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the velocity vector that will be sent to the player + * + * @return Vector the player will get + */ + public Vector getVelocity() { + return velocity; + } + + /** + * Sets the velocity vector that will be sent to the player + * + * @param velocity The velocity vector that will be sent to the player + */ + public void setVelocity(Vector velocity) { + this.velocity = velocity; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/MapInitializeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/MapInitializeEvent.java new file mode 100644 index 0000000..8834489 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/MapInitializeEvent.java @@ -0,0 +1,34 @@ +package org.bukkit.event.server; + +import org.bukkit.event.HandlerList; +import org.bukkit.map.MapView; + +/** + * Called when a map is initialized. + */ +public class MapInitializeEvent extends ServerEvent { + private static final HandlerList handlers = new HandlerList(); + private final MapView mapView; + + public MapInitializeEvent(final MapView mapView) { + this.mapView = mapView; + } + + /** + * Gets the map initialized in this event. + * + * @return Map for this event + */ + public MapView getMap() { + return mapView; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/PluginDisableEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/PluginDisableEvent.java new file mode 100644 index 0000000..932c4fd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/PluginDisableEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.server; + +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; + +/** + * Called when a plugin is disabled. + */ +public class PluginDisableEvent extends PluginEvent { + private static final HandlerList handlers = new HandlerList(); + + public PluginDisableEvent(final Plugin plugin) { + super(plugin); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/PluginEnableEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/PluginEnableEvent.java new file mode 100644 index 0000000..865316d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/PluginEnableEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.server; + +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; + +/** + * Called when a plugin is enabled. + */ +public class PluginEnableEvent extends PluginEvent { + private static final HandlerList handlers = new HandlerList(); + + public PluginEnableEvent(final Plugin plugin) { + super(plugin); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/PluginEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/PluginEvent.java new file mode 100644 index 0000000..1ad656d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/PluginEvent.java @@ -0,0 +1,23 @@ +package org.bukkit.event.server; + +import org.bukkit.plugin.Plugin; + +/** + * Used for plugin enable and disable events + */ +public abstract class PluginEvent extends ServerEvent { + private final Plugin plugin; + + public PluginEvent(final Plugin plugin) { + this.plugin = plugin; + } + + /** + * Gets the plugin involved in this event + * + * @return Plugin for this event + */ + public Plugin getPlugin() { + return plugin; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/RemoteServerCommandEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/RemoteServerCommandEvent.java new file mode 100644 index 0000000..2a49237 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/RemoteServerCommandEvent.java @@ -0,0 +1,25 @@ +package org.bukkit.event.server; + +import org.bukkit.command.CommandSender; +import org.bukkit.event.HandlerList; + +/** + * This event is called when a command is recieved over RCON. See the javadocs + * of {@link ServerCommandEvent} for more information. + */ +public class RemoteServerCommandEvent extends ServerCommandEvent { + private static final HandlerList handlers = new HandlerList(); + + public RemoteServerCommandEvent(final CommandSender sender, final String command) { + super(sender, command); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/ServerCommandEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/ServerCommandEvent.java new file mode 100644 index 0000000..8a5972a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/ServerCommandEvent.java @@ -0,0 +1,86 @@ +package org.bukkit.event.server; + +import org.bukkit.command.CommandSender; +import org.bukkit.event.HandlerList; + +/** + * This event is called when a command is run from the server console. It is + * called early in the command handling process, and modifications in this + * event (via {@link #setCommand(String)}) will be shown in the behavior. + *

          + * Many plugins will have no use for this event, and you should + * attempt to avoid using it if it is not necessary. + *

          + * Some examples of valid uses for this event are: + *

            + *
          • Logging executed commands to a separate file + *
          • Variable substitution. For example, replacing ${ip:Steve} + * with the connection IP of the player named Steve, or simulating the + * @a and @p decorators used by Command Blocks + * for plugins that do not handle it. + *
          • Conditionally blocking commands belonging to other plugins. + *
          • Per-sender command aliases. For example, after the console runs the + * command /calias cr gamemode creative, the next time they + * run /cr, it gets replaced into + * /gamemode creative. (Global command aliases should be + * done by registering the alias.) + *
          + *

          + * Examples of incorrect uses are: + *

            + *
          • Using this event to run command logic + *
          + *

          + * If the event is cancelled, processing of the command will halt. + *

          + * The state of whether or not there is a slash (/) at the + * beginning of the message should be preserved. If a slash is added or + * removed, unexpected behavior may result. + */ +public class ServerCommandEvent extends ServerEvent { + private static final HandlerList handlers = new HandlerList(); + private String command; + private final CommandSender sender; + + public ServerCommandEvent(final CommandSender sender, final String command) { + this.command = command; + this.sender = sender; + } + + /** + * Gets the command that the user is attempting to execute from the + * console + * + * @return Command the user is attempting to execute + */ + public String getCommand() { + return command; + } + + /** + * Sets the command that the server will execute + * + * @param message New message that the server will execute + */ + public void setCommand(String message) { + this.command = message; + } + + /** + * Get the command sender. + * + * @return The sender + */ + public CommandSender getSender() { + return sender; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/ServerEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/ServerEvent.java new file mode 100644 index 0000000..eb00d6a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/ServerEvent.java @@ -0,0 +1,9 @@ +package org.bukkit.event.server; + +import org.bukkit.event.Event; + +/** + * Miscellaneous server events + */ +public abstract class ServerEvent extends Event { +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/ServerListPingEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/ServerListPingEvent.java new file mode 100644 index 0000000..c61afdf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/ServerListPingEvent.java @@ -0,0 +1,142 @@ +package org.bukkit.event.server; + +import java.net.InetAddress; +import java.util.Iterator; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.util.CachedServerIcon; + +/** + * Called when a server list ping is coming in. Displayed players can be + * checked and removed by {@link #iterator() iterating} over this event. + */ +public class ServerListPingEvent extends ServerEvent implements Iterable { + private static final int MAGIC_PLAYER_COUNT = Integer.MIN_VALUE; + private static final HandlerList handlers = new HandlerList(); + private final InetAddress address; + private String motd; + private final int numPlayers; + private int maxPlayers; + + public ServerListPingEvent(final InetAddress address, final String motd, final int numPlayers, final int maxPlayers) { + Validate.isTrue(numPlayers >= 0, "Cannot have negative number of players online", numPlayers); + this.address = address; + this.motd = motd; + this.numPlayers = numPlayers; + this.maxPlayers = maxPlayers; + } + + /** + * This constructor is intended for implementations that provide the + * {@link #iterator()} method, thus provided the {@link #getNumPlayers()} + * count. + */ + protected ServerListPingEvent(final InetAddress address, final String motd, final int maxPlayers) { + this.numPlayers = MAGIC_PLAYER_COUNT; + this.address = address; + this.motd = motd; + this.maxPlayers = maxPlayers; + } + + /** + * Get the address the ping is coming from. + * + * @return the address + */ + public InetAddress getAddress() { + return address; + } + + /** + * Get the message of the day message. + * + * @return the message of the day + */ + public String getMotd() { + return motd; + } + + /** + * Change the message of the day message. + * + * @param motd the message of the day + */ + public void setMotd(String motd) { + this.motd = motd; + } + + /** + * Get the number of players sent. + * + * @return the number of players + */ + public int getNumPlayers() { + int numPlayers = this.numPlayers; + if (numPlayers == MAGIC_PLAYER_COUNT) { + numPlayers = 0; + for (@SuppressWarnings("unused") final Player player : this) { + numPlayers++; + } + } + return numPlayers; + } + + /** + * Get the maximum number of players sent. + * + * @return the maximum number of players + */ + public int getMaxPlayers() { + return maxPlayers; + } + + /** + * Set the maximum number of players sent. + * + * @param maxPlayers the maximum number of player + */ + public void setMaxPlayers(int maxPlayers) { + this.maxPlayers = maxPlayers; + } + + /** + * Sets the server-icon sent to the client. + * + * @param icon the icon to send to the client + * @throws IllegalArgumentException if the {@link CachedServerIcon} is not + * created by the caller of this event; null may be accepted for some + * implementations + * @throws UnsupportedOperationException if the caller of this event does + * not support setting the server icon + */ + public void setServerIcon(CachedServerIcon icon) throws IllegalArgumentException, UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * {@inheritDoc} + *

          + * Calling the {@link Iterator#remove()} method will force that particular + * player to not be displayed on the player list, decrease the size + * returned by {@link #getNumPlayers()}, and will not be returned again by + * any new iterator. + * + * @throws UnsupportedOperationException if the caller of this event does + * not support removing players + */ + @Override + public Iterator iterator() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/ServiceEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/ServiceEvent.java new file mode 100644 index 0000000..69bf872 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/ServiceEvent.java @@ -0,0 +1,19 @@ +package org.bukkit.event.server; + +import org.bukkit.plugin.RegisteredServiceProvider; + +/** + * An event relating to a registered service. This is called in a {@link + * org.bukkit.plugin.ServicesManager} + */ +public abstract class ServiceEvent extends ServerEvent { + private final RegisteredServiceProvider provider; + + public ServiceEvent(final RegisteredServiceProvider provider) { + this.provider = provider; + } + + public RegisteredServiceProvider getProvider() { + return provider; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/ServiceRegisterEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/ServiceRegisterEvent.java new file mode 100644 index 0000000..7dfadde --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/ServiceRegisterEvent.java @@ -0,0 +1,27 @@ +package org.bukkit.event.server; + +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.RegisteredServiceProvider; + +/** + * This event is called when a service is registered. + *

          + * Warning: The order in which register and unregister events are called + * should not be relied upon. + */ +public class ServiceRegisterEvent extends ServiceEvent { + private static final HandlerList handlers = new HandlerList(); + + public ServiceRegisterEvent(RegisteredServiceProvider registeredProvider) { + super(registeredProvider); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/server/ServiceUnregisterEvent.java b/vspigot-api/src/main/java/org/bukkit/event/server/ServiceUnregisterEvent.java new file mode 100644 index 0000000..db61d23 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/server/ServiceUnregisterEvent.java @@ -0,0 +1,27 @@ +package org.bukkit.event.server; + +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.RegisteredServiceProvider; + +/** + * This event is called when a service is unregistered. + *

          + * Warning: The order in which register and unregister events are called + * should not be relied upon. + */ +public class ServiceUnregisterEvent extends ServiceEvent { + private static final HandlerList handlers = new HandlerList(); + + public ServiceUnregisterEvent(RegisteredServiceProvider serviceProvider) { + super(serviceProvider); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleBlockCollisionEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleBlockCollisionEvent.java new file mode 100644 index 0000000..b643b57 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleBlockCollisionEvent.java @@ -0,0 +1,36 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.block.Block; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.HandlerList; + +/** + * Raised when a vehicle collides with a block. + */ +public class VehicleBlockCollisionEvent extends VehicleCollisionEvent { + private static final HandlerList handlers = new HandlerList(); + private final Block block; + + public VehicleBlockCollisionEvent(final Vehicle vehicle, final Block block) { + super(vehicle); + this.block = block; + } + + /** + * Gets the block the vehicle collided with + * + * @return the block the vehicle collided with + */ + public Block getBlock() { + return block; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleCollisionEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleCollisionEvent.java new file mode 100644 index 0000000..9dd0579 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleCollisionEvent.java @@ -0,0 +1,12 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Vehicle; + +/** + * Raised when a vehicle collides. + */ +public abstract class VehicleCollisionEvent extends VehicleEvent { + public VehicleCollisionEvent(final Vehicle vehicle) { + super(vehicle); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleCreateEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleCreateEvent.java new file mode 100644 index 0000000..22eda72 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleCreateEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Vehicle; +import org.bukkit.event.HandlerList; + +/** + * Raised when a vehicle is created. + */ +public class VehicleCreateEvent extends VehicleEvent { + private static final HandlerList handlers = new HandlerList(); + + public VehicleCreateEvent(final Vehicle vehicle) { + super(vehicle); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleDamageEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleDamageEvent.java new file mode 100644 index 0000000..304ee2c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleDamageEvent.java @@ -0,0 +1,92 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.util.NumberConversions; + +/** + * Raised when a vehicle receives damage. + */ +public class VehicleDamageEvent extends VehicleEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Entity attacker; + private double damage; + private boolean cancelled; + + @Deprecated + public VehicleDamageEvent(final Vehicle vehicle, final Entity attacker, final int damage) { + this(vehicle, attacker, (double) damage); + } + + public VehicleDamageEvent(final Vehicle vehicle, final Entity attacker, final double damage) { + super(vehicle); + this.attacker = attacker; + this.damage = damage; + } + + /** + * Gets the Entity that is attacking the vehicle + * + * @return the Entity that is attacking the vehicle + */ + public Entity getAttacker() { + return attacker; + } + + /** + * Gets the damage done to the vehicle + * + * @return the damage done to the vehicle + */ + public double getDamage() { + return damage; + } + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public int _INVALID_getDamage() { + return NumberConversions.ceil(getDamage()); + } + + /** + * Sets the damage done to the vehicle + * + * @param damage The damage + */ + public void setDamage(double damage) { + this.damage = damage; + } + + /** + * This method exists for legacy reasons to provide backwards + * compatibility. It will not exist at runtime and should not be used + * under any circumstances. + */ + @Deprecated + public void _INVALID_setDamage(int damage) { + setDamage(damage); + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleDestroyEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleDestroyEvent.java new file mode 100644 index 0000000..f1176fd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleDestroyEvent.java @@ -0,0 +1,48 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Raised when a vehicle is destroyed, which could be caused by either a + * player or the environment. This is not raised if the boat is simply + * 'removed' due to other means. + */ +public class VehicleDestroyEvent extends VehicleEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Entity attacker; + private boolean cancelled; + + public VehicleDestroyEvent(final Vehicle vehicle, final Entity attacker) { + super(vehicle); + this.attacker = attacker; + } + + /** + * Gets the Entity that has destroyed the vehicle, potentially null + * + * @return the Entity that has destroyed the vehicle, potentially null + */ + public Entity getAttacker() { + return attacker; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEnterEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEnterEvent.java new file mode 100644 index 0000000..85c9b21 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEnterEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Raised when an entity enters a vehicle. + */ +public class VehicleEnterEvent extends VehicleEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Entity entered; + + public VehicleEnterEvent(final Vehicle vehicle, final Entity entered) { + super(vehicle); + this.entered = entered; + } + + /** + * Gets the Entity that entered the vehicle. + * + * @return the Entity that entered the vehicle + */ + public Entity getEntered() { + return entered; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEntityCollisionEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEntityCollisionEvent.java new file mode 100644 index 0000000..4d4d0e2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEntityCollisionEvent.java @@ -0,0 +1,59 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Raised when a vehicle collides with an entity. + */ +public class VehicleEntityCollisionEvent extends VehicleCollisionEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Entity entity; + private boolean cancelled = false; + private boolean cancelledPickup = false; + private boolean cancelledCollision = false; + + public VehicleEntityCollisionEvent(final Vehicle vehicle, final Entity entity) { + super(vehicle); + this.entity = entity; + } + + public Entity getEntity() { + return entity; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + public boolean isPickupCancelled() { + return cancelledPickup; + } + + public void setPickupCancelled(boolean cancel) { + cancelledPickup = cancel; + } + + public boolean isCollisionCancelled() { + return cancelledCollision; + } + + public void setCollisionCancelled(boolean cancel) { + cancelledCollision = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEvent.java new file mode 100644 index 0000000..b8255c0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Vehicle; +import org.bukkit.event.Event; + +/** + * Represents a vehicle-related event. + */ +public abstract class VehicleEvent extends Event { + protected Vehicle vehicle; + + public VehicleEvent(final Vehicle vehicle) { + this.vehicle = vehicle; + } + + /** + * Get the vehicle. + * + * @return the vehicle + */ + public final Vehicle getVehicle() { + return vehicle; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleExitEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleExitEvent.java new file mode 100644 index 0000000..364451b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleExitEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Raised when a living entity exits a vehicle. + */ +public class VehicleExitEvent extends VehicleEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final LivingEntity exited; + + public VehicleExitEvent(final Vehicle vehicle, final LivingEntity exited) { + super(vehicle); + this.exited = exited; + } + + /** + * Get the living entity that exited the vehicle. + * + * @return The entity. + */ + public LivingEntity getExited() { + return exited; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleMoveEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleMoveEvent.java new file mode 100644 index 0000000..9a13e29 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleMoveEvent.java @@ -0,0 +1,49 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.Location; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.HandlerList; + +/** + * Raised when a vehicle moves. + */ +public class VehicleMoveEvent extends VehicleEvent { + private static final HandlerList handlers = new HandlerList(); + private final Location from; + private final Location to; + + public VehicleMoveEvent(final Vehicle vehicle, final Location from, final Location to) { + super(vehicle); + + this.from = from; + this.to = to; + } + + /** + * Get the previous position. + * + * @return Old position. + */ + public Location getFrom() { + return from; + } + + /** + * Get the next position. + * + * @return New position. + */ + public Location getTo() { + return to; + } + + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleUpdateEvent.java b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleUpdateEvent.java new file mode 100644 index 0000000..eebfdb1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/vehicle/VehicleUpdateEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.vehicle; + +import org.bukkit.entity.Vehicle; +import org.bukkit.event.HandlerList; + +/** + * Called when a vehicle updates + */ +public class VehicleUpdateEvent extends VehicleEvent { + private static final HandlerList handlers = new HandlerList(); + + public VehicleUpdateEvent(final Vehicle vehicle) { + super(vehicle); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/weather/LightningStrikeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/weather/LightningStrikeEvent.java new file mode 100644 index 0000000..66fd763 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/weather/LightningStrikeEvent.java @@ -0,0 +1,46 @@ +package org.bukkit.event.weather; + +import org.bukkit.World; +import org.bukkit.entity.LightningStrike; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Stores data for lightning striking + */ +public class LightningStrikeEvent extends WeatherEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final LightningStrike bolt; + + public LightningStrikeEvent(final World world, final LightningStrike bolt) { + super(world); + this.bolt = bolt; + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + /** + * Gets the bolt which is striking the earth. + * + * @return lightning entity + */ + public LightningStrike getLightning() { + return bolt; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/weather/ThunderChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/weather/ThunderChangeEvent.java new file mode 100644 index 0000000..5e3716e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/weather/ThunderChangeEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.weather; + +import org.bukkit.World; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Stores data for thunder state changing in a world + */ +public class ThunderChangeEvent extends WeatherEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final boolean to; + + public ThunderChangeEvent(final World world, final boolean to) { + super(world); + this.to = to; + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + /** + * Gets the state of thunder that the world is being set to + * + * @return true if the weather is being set to thundering, false otherwise + */ + public boolean toThunderState() { + return to; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/weather/WeatherChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/weather/WeatherChangeEvent.java new file mode 100644 index 0000000..5d1234e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/weather/WeatherChangeEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.weather; + +import org.bukkit.World; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Stores data for weather changing in a world + */ +public class WeatherChangeEvent extends WeatherEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final boolean to; + + public WeatherChangeEvent(final World world, final boolean to) { + super(world); + this.to = to; + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + /** + * Gets the state of weather that the world is being set to + * + * @return true if the weather is being set to raining, false otherwise + */ + public boolean toWeatherState() { + return to; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/weather/WeatherEvent.java b/vspigot-api/src/main/java/org/bukkit/event/weather/WeatherEvent.java new file mode 100644 index 0000000..0cae9bc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/weather/WeatherEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.weather; + +import org.bukkit.World; +import org.bukkit.event.Event; + +/** + * Represents a Weather-related event + */ +public abstract class WeatherEvent extends Event { + protected World world; + + public WeatherEvent(final World where) { + world = where; + } + + /** + * Returns the World where this event is occurring + * + * @return World this event is occurring in + */ + public final World getWorld() { + return world; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/ChunkEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkEvent.java new file mode 100644 index 0000000..4710d40 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.world; + +import org.bukkit.Chunk; + +/** + * Represents a Chunk related event + */ +public abstract class ChunkEvent extends WorldEvent { + protected Chunk chunk; + + protected ChunkEvent(final Chunk chunk) { + super(chunk.getWorld()); + this.chunk = chunk; + } + + /** + * Gets the chunk being loaded/unloaded + * + * @return Chunk that triggered this event + */ + public Chunk getChunk() { + return chunk; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/ChunkLoadEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkLoadEvent.java new file mode 100644 index 0000000..a45b1cd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkLoadEvent.java @@ -0,0 +1,37 @@ +package org.bukkit.event.world; + +import org.bukkit.Chunk; +import org.bukkit.event.HandlerList; + +/** + * Called when a chunk is loaded + */ +public class ChunkLoadEvent extends ChunkEvent { + private static final HandlerList handlers = new HandlerList(); + private final boolean newChunk; + + public ChunkLoadEvent(final Chunk chunk, final boolean newChunk) { + super(chunk); + this.newChunk = newChunk; + } + + /** + * Gets if this chunk was newly created or not. + *

          + * Note that if this chunk is new, it will not be populated at this time. + * + * @return true if the chunk is new, otherwise false + */ + public boolean isNewChunk() { + return newChunk; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/ChunkPopulateEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkPopulateEvent.java new file mode 100644 index 0000000..705d955 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkPopulateEvent.java @@ -0,0 +1,28 @@ +package org.bukkit.event.world; + +import org.bukkit.Chunk; +import org.bukkit.event.HandlerList; +import org.bukkit.generator.BlockPopulator; + +/** + * Thrown when a new chunk has finished being populated. + *

          + * If your intent is to populate the chunk using this event, please see {@link + * BlockPopulator} + */ +public class ChunkPopulateEvent extends ChunkEvent { + private static final HandlerList handlers = new HandlerList(); + + public ChunkPopulateEvent(final Chunk chunk) { + super(chunk); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/ChunkUnloadEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkUnloadEvent.java new file mode 100644 index 0000000..f59d091 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/ChunkUnloadEvent.java @@ -0,0 +1,34 @@ +package org.bukkit.event.world; + +import org.bukkit.Chunk; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a chunk is unloaded + */ +public class ChunkUnloadEvent extends ChunkEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + + public ChunkUnloadEvent(final Chunk chunk) { + super(chunk); + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/PortalCreateEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/PortalCreateEvent.java new file mode 100644 index 0000000..d83d7a9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/PortalCreateEvent.java @@ -0,0 +1,77 @@ +package org.bukkit.event.world; + +import org.bukkit.block.Block; +import org.bukkit.World; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Called when a portal is created + */ +public class PortalCreateEvent extends WorldEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private final ArrayList blocks = new ArrayList(); + private CreateReason reason = CreateReason.FIRE; + + public PortalCreateEvent(final Collection blocks, final World world, CreateReason reason) { + super(world); + + this.blocks.addAll(blocks); + this.reason = reason; + } + + /** + * Gets an array list of all the blocks associated with the created portal + * + * @return array list of all the blocks associated with the created portal + */ + public ArrayList getBlocks() { + return this.blocks; + } + + public boolean isCancelled() { + return cancel; + } + + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + /** + * Gets the reason for the portal's creation + * + * @return CreateReason for the portal's creation + */ + public CreateReason getReason() { + return reason; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * An enum to specify the various reasons for a portal's creation + */ + public enum CreateReason { + /** + * When a portal is created 'traditionally' due to a portal frame + * being set on fire. + */ + FIRE, + /** + * When a portal is created as a destination for an existing portal + * when using the custom PortalTravelAgent + */ + OBC_DESTINATION + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/SpawnChangeEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/SpawnChangeEvent.java new file mode 100644 index 0000000..e99c3c0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/SpawnChangeEvent.java @@ -0,0 +1,37 @@ +package org.bukkit.event.world; + +import org.bukkit.World; +import org.bukkit.Location; +import org.bukkit.event.HandlerList; + +/** + * An event that is called when a world's spawn changes. The world's previous + * spawn location is included. + */ +public class SpawnChangeEvent extends WorldEvent { + private static final HandlerList handlers = new HandlerList(); + private final Location previousLocation; + + public SpawnChangeEvent(final World world, final Location previousLocation) { + super(world); + this.previousLocation = previousLocation; + } + + /** + * Gets the previous spawn location + * + * @return Location that used to be spawn + */ + public Location getPreviousLocation() { + return previousLocation; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/StructureGrowEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/StructureGrowEvent.java new file mode 100644 index 0000000..d1c9822 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/StructureGrowEvent.java @@ -0,0 +1,96 @@ +package org.bukkit.event.world; + +import java.util.List; +import org.bukkit.Location; +import org.bukkit.TreeType; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Event that is called when an organic structure attempts to grow (Sapling -> + * Tree), (Mushroom -> Huge Mushroom), naturally or using bonemeal. + */ +public class StructureGrowEvent extends WorldEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled = false; + private final Location location; + private final TreeType species; + private final boolean bonemeal; + private final Player player; + private final List blocks; + + public StructureGrowEvent(final Location location, final TreeType species, final boolean bonemeal, final Player player, final List blocks) { + super(location.getWorld()); + this.location = location; + this.species = species; + this.bonemeal = bonemeal; + this.player = player; + this.blocks = blocks; + } + + /** + * Gets the location of the structure. + * + * @return Location of the structure + */ + public Location getLocation() { + return location; + } + + /** + * Gets the species type (birch, normal, pine, red mushroom, brown + * mushroom) + * + * @return Structure species + */ + public TreeType getSpecies() { + return species; + } + + /** + * Checks if structure was grown using bonemeal. + * + * @return True if the structure was grown using bonemeal. + */ + public boolean isFromBonemeal() { + return bonemeal; + } + + /** + * Gets the player that created the structure. + * + * @return Player that created the structure, null if was not created + * manually + */ + public Player getPlayer() { + return player; + } + + /** + * Gets an ArrayList of all blocks associated with the structure. + * + * @return ArrayList of all blocks associated with the structure. + */ + public List getBlocks() { + return blocks; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/WorldEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/WorldEvent.java new file mode 100644 index 0000000..bd89b81 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/WorldEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.world; + +import org.bukkit.World; +import org.bukkit.event.Event; + +/** + * Represents events within a world + */ +public abstract class WorldEvent extends Event { + private final World world; + + public WorldEvent(final World world) { + this.world = world; + } + + /** + * Gets the world primarily involved with this event + * + * @return World which caused this event + */ + public World getWorld() { + return world; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/WorldInitEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/WorldInitEvent.java new file mode 100644 index 0000000..6bf13e0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/WorldInitEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.world; + +import org.bukkit.World; +import org.bukkit.event.HandlerList; + +/** + * Called when a World is initializing + */ +public class WorldInitEvent extends WorldEvent { + private static final HandlerList handlers = new HandlerList(); + + public WorldInitEvent(final World world) { + super(world); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/WorldLoadEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/WorldLoadEvent.java new file mode 100644 index 0000000..c5545aa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/WorldLoadEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.world; + +import org.bukkit.World; +import org.bukkit.event.HandlerList; + +/** + * Called when a World is loaded + */ +public class WorldLoadEvent extends WorldEvent { + private static final HandlerList handlers = new HandlerList(); + + public WorldLoadEvent(final World world) { + super(world); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/WorldSaveEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/WorldSaveEvent.java new file mode 100644 index 0000000..d46b413 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/WorldSaveEvent.java @@ -0,0 +1,24 @@ +package org.bukkit.event.world; + +import org.bukkit.World; +import org.bukkit.event.HandlerList; + +/** + * Called when a World is saved. + */ +public class WorldSaveEvent extends WorldEvent { + private static final HandlerList handlers = new HandlerList(); + + public WorldSaveEvent(final World world) { + super(world); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/event/world/WorldUnloadEvent.java b/vspigot-api/src/main/java/org/bukkit/event/world/WorldUnloadEvent.java new file mode 100644 index 0000000..110544b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/event/world/WorldUnloadEvent.java @@ -0,0 +1,34 @@ +package org.bukkit.event.world; + +import org.bukkit.World; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +/** + * Called when a World is unloaded + */ +public class WorldUnloadEvent extends WorldEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean isCancelled; + + public WorldUnloadEvent(final World world) { + super(world); + } + + public boolean isCancelled() { + return this.isCancelled; + } + + public void setCancelled(boolean cancel) { + this.isCancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/generator/BlockPopulator.java b/vspigot-api/src/main/java/org/bukkit/generator/BlockPopulator.java new file mode 100644 index 0000000..6a70bdb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/generator/BlockPopulator.java @@ -0,0 +1,29 @@ +package org.bukkit.generator; + +import java.util.Random; +import org.bukkit.Chunk; +import org.bukkit.World; + +/** + * A block populator is responsible for generating a small area of blocks. + *

          + * For example, generating glowstone inside the nether or generating dungeons + * full of treasure + */ +public abstract class BlockPopulator { + + /** + * Populates an area of blocks at or around the given chunk. + *

          + * The chunks on each side of the specified chunk must already exist; that + * is, there must be one north, east, south and west of the specified + * chunk. The "corner" chunks may not exist, in which scenario the + * populator should record any changes required for those chunks and + * perform the changes when they are ready. + * + * @param world The world to generate in + * @param random The random generator to use + * @param source The chunk to generate for + */ + public abstract void populate(World world, Random random, Chunk source); +} diff --git a/vspigot-api/src/main/java/org/bukkit/generator/ChunkGenerator.java b/vspigot-api/src/main/java/org/bukkit/generator/ChunkGenerator.java new file mode 100644 index 0000000..8e08bdc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/generator/ChunkGenerator.java @@ -0,0 +1,268 @@ +package org.bukkit.generator; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; + +/** + * A chunk generator is responsible for the initial shaping of an entire + * chunk. For example, the nether chunk generator should shape netherrack and + * soulsand + */ +public abstract class ChunkGenerator { + + /** + * Interface to biome data for chunk to be generated: initialized with + * default values for world type and seed. + *

          + * Custom generator is free to access and tailor values during + * generateBlockSections() or generateExtBlockSections(). + */ + public interface BiomeGrid { + + /** + * Get biome at x, z within chunk being generated + * + * @param x - 0-15 + * @param z - 0-15 + * @return Biome value + */ + Biome getBiome(int x, int z); + + /** + * Set biome at x, z within chunk being generated + * + * @param x - 0-15 + * @param z - 0-15 + * @param bio - Biome value + */ + void setBiome(int x, int z, Biome bio); + } + @Deprecated + /** + * Shapes the chunk for the given coordinates. + *

          + * This method should return a byte[32768] in the following format: + *

          +     * for (int x = 0; x < 16; x++) {
          +     *     for (int z = 0; z < 16; z++) {
          +     *         for (int y = 0; y < 128; y++) {
          +     *             // result[(x * 16 + z) * 128 + y] = ??;
          +     *         }
          +     *     }
          +     * }
          +     * 
          + *

          + * Note that this method should never attempt to get the Chunk at + * the passed coordinates, as doing so may cause an infinite loop + *

          + * Note this deprecated method will only be called when both + * generateExtBlockSections() and generateBlockSections() are + * unimplemented and return null. + * + * @param world The world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @return byte[] containing the types for each block created by this + * generator + */ + public byte[] generate(World world, Random random, int x, int z) { + throw new UnsupportedOperationException("Custom generator is missing required methods: generate(), generateBlockSections() and generateExtBlockSections()"); + } + + /** + * Shapes the chunk for the given coordinates, with extended block IDs + * supported (0-4095). + *

          + * As of 1.2, chunks are represented by a vertical array of chunk + * sections, each of which is 16 x 16 x 16 blocks. If a section is empty + * (all zero), the section does not need to be supplied, reducing memory + * usage. + *

          + * This method must return a short[][] array in the following format: + *

          +     *     short[][] result = new short[world-height / 16][];
          +     * 
          + * Each section (sectionID = (Y>>4)) that has blocks needs to be allocated + * space for the 4096 blocks in that section: + *
          +     *     result[sectionID] = new short[4096];
          +     * 
          + * while sections that are not populated can be left null. + *

          + * Setting a block at X, Y, Z within the chunk can be done with the + * following mapping function: + *

          +     *    void setBlock(short[][] result, int x, int y, int z, short blkid) {
          +     *        if (result[y >> 4] == null) {
          +     *            result[y >> 4] = new short[4096];
          +     *        }
          +     *        result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = blkid;
          +     *    }
          +     * 
          + * while reading a block ID can be done with the following mapping + * function: + *
          +     *    short getBlock(short[][] result, int x, int y, int z) {
          +     *        if (result[y >> 4] == null) {
          +     *            return (short)0;
          +     *        }
          +     *        return result[y >> 4][((y & 0xF) << 8) | (z << 4) | x];
          +     *    }
          +     * 
          + * while sections that are not populated can be left null. + *

          + * Setting a block at X, Y, Z within the chunk can be done with the + * following mapping function: + *

          +     *    void setBlock(short[][] result, int x, int y, int z, short blkid) {
          +     *        if (result[y >> 4) == null) {
          +     *            result[y >> 4] = new short[4096];
          +     *        }
          +     *        result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = blkid;
          +     *    }
          +     * 
          + * while reading a block ID can be done with the following mapping + * function: + *
          +     *    short getBlock(short[][] result, int x, int y, int z) {
          +     *        if (result[y >> 4) == null) {
          +     *            return (short)0;
          +     *        }
          +     *        return result[y >> 4][((y & 0xF) << 8) | (z << 4) | x];
          +     *    }
          +     * 
          + *

          + * Note that this method should never attempt to get the Chunk at + * the passed coordinates, as doing so may cause an infinite loop + *

          + * Note generators that do not return block IDs above 255 should not + * implement this method, or should have it return null (which will result + * in the generateBlockSections() method being called). + * + * @param world The world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @param biomes Proposed biome values for chunk - can be updated by + * generator + * @return short[][] containing the types for each block created by this + * generator + * @deprecated Magic value + */ + @Deprecated + public short[][] generateExtBlockSections(World world, Random random, int x, int z, BiomeGrid biomes) { + return null; // Default - returns null, which drives call to generateBlockSections() + } + + /** + * Shapes the chunk for the given coordinates. + *

          + * As of 1.2, chunks are represented by a vertical array of chunk + * sections, each of which is 16 x 16 x 16 blocks. If a section is empty + * (all zero), the section does not need to be supplied, reducing memory + * usage. + *

          + * This method must return a byte[][] array in the following format: + *

          +     *     byte[][] result = new byte[world-height / 16][];
          +     * 
          + * Each section (sectionID = (Y>>4)) that has blocks needs to be allocated + * space for the 4096 blocks in that section: + *
          +     *     result[sectionID] = new byte[4096];
          +     * 
          + * while sections that are not populated can be left null. + *

          + * Setting a block at X, Y, Z within the chunk can be done with the + * following mapping function: + *

          +     *    void setBlock(byte[][] result, int x, int y, int z, byte blkid) {
          +     *        if (result[y >> 4) == null) {
          +     *            result[y >> 4] = new byte[4096];
          +     *        }
          +     *        result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = blkid;
          +     *    }
          +     * 
          + * while reading a block ID can be done with the following mapping + * function: + *
          +     *    byte getBlock(byte[][] result, int x, int y, int z) {
          +     *        if (result[y >> 4) == null) {
          +     *            return (byte)0;
          +     *        }
          +     *        return result[y >> 4][((y & 0xF) << 8) | (z << 4) | x];
          +     *    }
          +     * 
          + * + * Note that this method should never attempt to get the Chunk at + * the passed coordinates, as doing so may cause an infinite loop + * + * @param world The world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @param biomes Proposed biome values for chunk - can be updated by + * generator + * @return short[][] containing the types for each block created by this + * generator + * @deprecated Magic value + */ + @Deprecated + public byte[][] generateBlockSections(World world, Random random, int x, int z, BiomeGrid biomes) { + return null; // Default - returns null, which drives call to generate() + } + + /** + * Tests if the specified location is valid for a natural spawn position + * + * @param world The world we're testing on + * @param x X-coordinate of the block to test + * @param z Z-coordinate of the block to test + * @return true if the location is valid, otherwise false + */ + public boolean canSpawn(World world, int x, int z) { + Block highest = world.getBlockAt(x, world.getHighestBlockYAt(x, z), z); + + switch (world.getEnvironment()) { + case NETHER: + return true; + case THE_END: + return highest.getType() != Material.AIR && highest.getType() != Material.WATER && highest.getType() != Material.LAVA; + case NORMAL: + default: + return highest.getType() == Material.SAND || highest.getType() == Material.GRAVEL; + } + } + + /** + * Gets a list of default {@link BlockPopulator}s to apply to a given + * world + * + * @param world World to apply to + * @return List containing any amount of BlockPopulators + */ + public List getDefaultPopulators(World world) { + return new ArrayList(); + } + + /** + * Gets a fixed spawn location to use for a given world. + *

          + * A null value is returned if a world should not use a fixed spawn point, + * and will instead attempt to find one randomly. + * + * @param world The world to locate a spawn point for + * @param random Random generator to use in the calculation + * @return Location containing a new spawn point, otherwise null + */ + public Location getFixedSpawnLocation(World world, Random random) { + return null; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/help/GenericCommandHelpTopic.java b/vspigot-api/src/main/java/org/bukkit/help/GenericCommandHelpTopic.java new file mode 100644 index 0000000..3e85e77 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/help/GenericCommandHelpTopic.java @@ -0,0 +1,80 @@ +package org.bukkit.help; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.defaults.VanillaCommand; +import org.bukkit.help.HelpTopic; + +/** + * Lacking an alternative, the help system will create instances of + * GenericCommandHelpTopic for each command in the server's CommandMap. You + * can use this class as a base class for custom help topics, or as an example + * for how to write your own. + */ +public class GenericCommandHelpTopic extends HelpTopic { + + protected Command command; + + public GenericCommandHelpTopic(Command command) { + this.command = command; + + if (command.getLabel().startsWith("/")) { + name = command.getLabel(); + } else { + name = "/" + command.getLabel(); + } + + // The short text is the first line of the description + int i = command.getDescription().indexOf("\n"); + if (i > 1) { + shortText = command.getDescription().substring(0, i - 1); + } else { + shortText = command.getDescription(); + } + + // Build full text + StringBuffer sb = new StringBuffer(); + + sb.append(ChatColor.GOLD); + sb.append("Description: "); + sb.append(ChatColor.WHITE); + sb.append(command.getDescription()); + + sb.append("\n"); + + sb.append(ChatColor.GOLD); + sb.append("Usage: "); + sb.append(ChatColor.WHITE); + sb.append(command.getUsage().replace("", name.substring(1))); + + if (command.getAliases().size() > 0) { + sb.append("\n"); + sb.append(ChatColor.GOLD); + sb.append("Aliases: "); + sb.append(ChatColor.WHITE); + sb.append(ChatColor.WHITE + StringUtils.join(command.getAliases(), ", ")); + } + fullText = sb.toString(); + } + + public boolean canSee(CommandSender sender) { + if (!command.isRegistered() && !(command instanceof VanillaCommand)) { + // Unregistered commands should not show up in the help (ignore VanillaCommands) + return false; + } + + if (sender instanceof ConsoleCommandSender) { + return true; + } + + if (amendedPermission != null) { + return sender.hasPermission(amendedPermission); + } else { + return command.testPermissionSilent(sender); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/help/HelpMap.java b/vspigot-api/src/main/java/org/bukkit/help/HelpMap.java new file mode 100644 index 0000000..9c1b51b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/help/HelpMap.java @@ -0,0 +1,79 @@ +package org.bukkit.help; + +import java.util.Collection; +import java.util.List; + +/** + * The HelpMap tracks all help topics registered in a Bukkit server. When the + * server starts up or is reloaded, help is processed and topics are added in + * the following order: + *

          + *

            + *
          1. General topics are loaded from the help.yml + *
          2. Plugins load and optionally call {@code addTopic()} + *
          3. Registered plugin commands are processed by {@link HelpTopicFactory} + * objects to create topics + *
          4. Topic contents are amended as directed in help.yml + *
          + */ +public interface HelpMap { + /** + * Returns a help topic for a given topic name. + * + * @param topicName The help topic name to look up. + * @return A {@link HelpTopic} object matching the topic name or null if + * none can be found. + */ + public HelpTopic getHelpTopic(String topicName); + + /** + * Returns a collection of all the registered help topics. + * + * @return All the registered help topics. + */ + public Collection getHelpTopics(); + + /** + * Adds a topic to the server's help index. + * + * @param topic The new help topic to add. + */ + public void addTopic(HelpTopic topic); + + /** + * Clears out the contents of the help index. Normally called during + * server reload. + */ + public void clear(); + + /** + * Associates a {@link HelpTopicFactory} object with given command base + * class. Plugins typically call this method during {@code onLoad()}. Once + * registered, the custom HelpTopicFactory will be used to create a custom + * {@link HelpTopic} for all commands deriving from the {@code + * commandClass} base class, or all commands deriving from {@link + * org.bukkit.command.PluginCommand} who's executor derives from {@code + * commandClass} base class. + * + * @param commandClass The class for which the custom HelpTopicFactory + * applies. Must derive from either {@link org.bukkit.command.Command} + * or {@link org.bukkit.command.CommandExecutor}. + * @param factory The {@link HelpTopicFactory} implementation to associate + * with the {@code commandClass}. + * @throws IllegalArgumentException Thrown if {@code commandClass} does + * not derive from a legal base class. + */ + public void registerHelpTopicFactory(Class commandClass, HelpTopicFactory factory); + + /** + * Gets the list of plugins the server administrator has chosen to exclude + * from the help index. Plugin authors who choose to directly extend + * {@link org.bukkit.command.Command} instead of {@link + * org.bukkit.command.PluginCommand} will need to check this collection in + * their {@link HelpTopicFactory} implementations to ensure they meet the + * server administrator's expectations. + * + * @return A list of plugins that should be excluded from the help index. + */ + public List getIgnoredPlugins(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/help/HelpTopic.java b/vspigot-api/src/main/java/org/bukkit/help/HelpTopic.java new file mode 100644 index 0000000..a2ba5f5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/help/HelpTopic.java @@ -0,0 +1,121 @@ +package org.bukkit.help; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +/** + * HelpTopic implementations are displayed to the user when the user uses the + * /help command. + *

          + * Custom implementations of this class can work at two levels. A simple + * implementation only needs to set the value of {@code name}, {@code + * shortText}, and {@code fullText} in the constructor. This base class will + * take care of the rest. + *

          + * Complex implementations can be created by overriding the behavior of all + * the methods in this class. + */ +public abstract class HelpTopic { + protected String name; + protected String shortText; + protected String fullText; + protected String amendedPermission; + + /** + * Determines if a {@link Player} is allowed to see this help topic. + *

          + * HelpTopic implementations should take server administrator wishes into + * account as set by the {@link HelpTopic#amendCanSee(String)} function. + * + * @param player The Player in question. + * @return True of the Player can see this help topic, false otherwise. + */ + public abstract boolean canSee(CommandSender player); + + /** + * Allows the server administrator to override the permission required to + * see a help topic. + *

          + * HelpTopic implementations should take this into account when + * determining topic visibility on the {@link + * HelpTopic#canSee(org.bukkit.command.CommandSender)} function. + * + * @param amendedPermission The permission node the server administrator + * wishes to apply to this topic. + */ + public void amendCanSee(String amendedPermission) { + this.amendedPermission = amendedPermission; + } + + /** + * Returns the name of this help topic. + * + * @return The topic name. + */ + public String getName() { + return name; + } + + /** + * Returns a brief description that will be displayed in the topic index. + * + * @return A brief topic description. + */ + public String getShortText() { + return shortText; + } + + /** + * Returns the full description of this help topic that is displayed when + * the user requests this topic's details. + *

          + * The result will be paginated to properly fit the user's client. + * + * @param forWho The player or console requesting the full text. Useful + * for further security trimming the command's full text based on + * sub-permissions in custom implementations. + * + * @return A full topic description. + */ + public String getFullText(CommandSender forWho) { + return fullText; + } + + /** + * Allows the server admin (or another plugin) to add or replace the + * contents of a help topic. + *

          + * A null in either parameter will leave that part of the topic unchanged. + * In either amending parameter, the string {@literal } is replaced + * with the existing contents in the help topic. Use this to append or + * prepend additional content into an automatically generated help topic. + * + * @param amendedShortText The new topic short text to use, or null to + * leave alone. + * @param amendedFullText The new topic full text to use, or null to leave + * alone. + */ + public void amendTopic(String amendedShortText, String amendedFullText) { + shortText = applyAmendment(shortText, amendedShortText); + fullText = applyAmendment(fullText, amendedFullText); + } + + /** + * Developers implementing their own custom HelpTopic implementations can + * use this utility method to ensure their implementations comply with the + * expected behavior of the {@link HelpTopic#amendTopic(String, String)} + * method. + * + * @param baseText The existing text of the help topic. + * @param amendment The amending text from the amendTopic() method. + * @return The application of the amending text to the existing text, + * according to the expected rules of amendTopic(). + */ + protected String applyAmendment(String baseText, String amendment) { + if (amendment == null) { + return baseText; + } else { + return amendment.replaceAll("", baseText); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/help/HelpTopicComparator.java b/vspigot-api/src/main/java/org/bukkit/help/HelpTopicComparator.java new file mode 100644 index 0000000..3e43eb3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/help/HelpTopicComparator.java @@ -0,0 +1,48 @@ +package org.bukkit.help; + +import org.bukkit.help.HelpTopic; + +import java.util.Comparator; + +/** + * Used to impose a custom total ordering on help topics. + *

          + * All topics are listed in alphabetic order, but topics that start with a + * slash come after topics that don't. + */ +public class HelpTopicComparator implements Comparator { + + // Singleton implementations + private static final TopicNameComparator tnc = new TopicNameComparator(); + public static TopicNameComparator topicNameComparatorInstance() { + return tnc; + } + + private static final HelpTopicComparator htc = new HelpTopicComparator(); + public static HelpTopicComparator helpTopicComparatorInstance() { + return htc; + } + + private HelpTopicComparator() {} + + public int compare(HelpTopic lhs, HelpTopic rhs) { + return tnc.compare(lhs.getName(), rhs.getName()); + } + + public static class TopicNameComparator implements Comparator { + private TopicNameComparator(){} + + public int compare(String lhs, String rhs) { + boolean lhsStartSlash = lhs.startsWith("/"); + boolean rhsStartSlash = rhs.startsWith("/"); + + if (lhsStartSlash && !rhsStartSlash) { + return 1; + } else if (!lhsStartSlash && rhsStartSlash) { + return -1; + } else { + return lhs.compareToIgnoreCase(rhs); + } + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/help/HelpTopicFactory.java b/vspigot-api/src/main/java/org/bukkit/help/HelpTopicFactory.java new file mode 100644 index 0000000..87d3697 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/help/HelpTopicFactory.java @@ -0,0 +1,42 @@ +package org.bukkit.help; + +import org.bukkit.command.Command; + +/** + * A HelpTopicFactory is used to create custom {@link HelpTopic} objects from + * commands that inherit from a common base class or have executors that + * inherit from a common base class. You can use a custom HelpTopic to change + * the way all the commands in your plugin display in the help. If your plugin + * implements a complex permissions system, a custom help topic may also be + * appropriate. + *

          + * To automatically bind your plugin's commands to your custom HelpTopic + * implementation, first make sure all your commands or executors derive from + * a custom base class (it doesn't have to do anything). Next implement a + * custom HelpTopicFactory that accepts your custom command base class and + * instantiates an instance of your custom HelpTopic from it. Finally, + * register your HelpTopicFactory against your command base class using the + * {@link HelpMap#registerHelpTopicFactory(Class, HelpTopicFactory)} method. + *

          + * As the help system iterates over all registered commands to make help + * topics, it first checks to see if there is a HelpTopicFactory registered + * for the command's base class. If so, the factory is used to make a help + * topic rather than a generic help topic. If no factory is found for the + * command's base class and the command derives from {@link + * org.bukkit.command.PluginCommand}, then the type of the command's executor + * is inspected looking for a registered HelpTopicFactory. Finally, if no + * factory is found, a generic help topic is created for the command. + * + * @param The base class for your custom commands. + */ +public interface HelpTopicFactory { + /** + * This method accepts a command deriving from a custom command base class + * and constructs a custom HelpTopic for it. + * + * @param command The custom command to build a help topic for. + * @return A new custom help topic or {@code null} to intentionally NOT + * create a topic. + */ + public HelpTopic createTopic(TCommand command); +} diff --git a/vspigot-api/src/main/java/org/bukkit/help/IndexHelpTopic.java b/vspigot-api/src/main/java/org/bukkit/help/IndexHelpTopic.java new file mode 100644 index 0000000..c474031 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/help/IndexHelpTopic.java @@ -0,0 +1,112 @@ +package org.bukkit.help; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.ChatPaginator; + +import java.util.Collection; + +/** + * This help topic generates a list of other help topics. This class is useful + * for adding your own index help topics. To enforce a particular order, use a + * sorted collection. + *

          + * If a preamble is provided to the constructor, that text will be displayed + * before the first item in the index. + */ +public class IndexHelpTopic extends HelpTopic { + + protected String permission; + protected String preamble; + protected Collection allTopics; + + public IndexHelpTopic(String name, String shortText, String permission, Collection topics) { + this(name, shortText, permission, topics, null); + } + + public IndexHelpTopic(String name, String shortText, String permission, Collection topics, String preamble) { + this.name = name; + this.shortText = shortText; + this.permission = permission; + this.preamble = preamble; + setTopicsCollection(topics); + } + + /** + * Sets the contents of the internal allTopics collection. + * + * @param topics The topics to set. + */ + protected void setTopicsCollection(Collection topics) { + this.allTopics = topics; + } + + public boolean canSee(CommandSender sender) { + if (sender instanceof ConsoleCommandSender) { + return true; + } + if (permission == null) { + return true; + } + return sender.hasPermission(permission); + } + + @Override + public void amendCanSee(String amendedPermission) { + permission = amendedPermission; + } + + public String getFullText(CommandSender sender) { + StringBuilder sb = new StringBuilder(); + + if (preamble != null) { + sb.append(buildPreamble(sender)); + sb.append("\n"); + } + + for (HelpTopic topic : allTopics) { + if (topic.canSee(sender)) { + String lineStr = buildIndexLine(sender, topic).replace("\n", ". "); + if (sender instanceof Player && lineStr.length() > ChatPaginator.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH) { + sb.append(lineStr.substring(0, ChatPaginator.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH - 3)); + sb.append("..."); + } else { + sb.append(lineStr); + } + sb.append("\n"); + } + } + return sb.toString(); + } + + /** + * Builds the topic preamble. Override this method to change how the index + * preamble looks. + * + * @param sender The command sender requesting the preamble. + * @return The topic preamble. + */ + protected String buildPreamble(CommandSender sender) { + return ChatColor.GRAY + preamble; + } + + /** + * Builds individual lines in the index topic. Override this method to + * change how index lines are rendered. + * + * @param sender The command sender requesting the index line. + * @param topic The topic to render into an index line. + * @return The rendered index line. + */ + protected String buildIndexLine(CommandSender sender, HelpTopic topic) { + StringBuilder line = new StringBuilder(); + line.append(ChatColor.GOLD); + line.append(topic.getName()); + line.append(": "); + line.append(ChatColor.WHITE); + line.append(topic.getShortText()); + return line.toString(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/AnvilInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/AnvilInventory.java new file mode 100644 index 0000000..70fae71 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/AnvilInventory.java @@ -0,0 +1,7 @@ +package org.bukkit.inventory; + +/** + * Interface to the inventory of an Anvil. + */ +public interface AnvilInventory extends Inventory { +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/BeaconInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/BeaconInventory.java new file mode 100644 index 0000000..2f8769e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/BeaconInventory.java @@ -0,0 +1,21 @@ +package org.bukkit.inventory; + +/** + * Interface to the inventory of a Beacon. + */ +public interface BeaconInventory extends Inventory { + + /** + * Set the item powering the beacon. + * + * @param item The new item + */ + void setItem(ItemStack item); + + /** + * Get the item powering the beacon. + * + * @return The current item. + */ + ItemStack getItem(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/BrewerInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/BrewerInventory.java new file mode 100644 index 0000000..9cc31c9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/BrewerInventory.java @@ -0,0 +1,25 @@ +package org.bukkit.inventory; + +import org.bukkit.block.BrewingStand; + +/** + * Interface to the inventory of a Brewing Stand. + */ +public interface BrewerInventory extends Inventory { + + /** + * Get the current ingredient for brewing. + * + * @return The ingredient. + */ + ItemStack getIngredient(); + + /** + * Set the current ingredient for brewing. + * + * @param ingredient The ingredient + */ + void setIngredient(ItemStack ingredient); + + BrewingStand getHolder(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/CraftingInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/CraftingInventory.java new file mode 100644 index 0000000..f71533c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/CraftingInventory.java @@ -0,0 +1,45 @@ +package org.bukkit.inventory; + +/** + * Interface to the crafting inventories + */ +public interface CraftingInventory extends Inventory { + + /** + * Check what item is in the result slot of this crafting inventory. + * + * @return The result item. + */ + ItemStack getResult(); + + /** + * Get the contents of the crafting matrix. + * + * @return The contents. + */ + ItemStack[] getMatrix(); + + /** + * Set the item in the result slot of the crafting inventory. + * + * @param newResult The new result item. + */ + void setResult(ItemStack newResult); + + /** + * Replace the contents of the crafting matrix + * + * @param contents The new contents. + * @throws IllegalArgumentException if the length of contents is greater + * than the size of the crafting matrix. + */ + void setMatrix(ItemStack[] contents); + + /** + * Get the current recipe formed on the crafting inventory, if any. + * + * @return The recipe, or null if the current contents don't match any + * recipe. + */ + Recipe getRecipe(); +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/DoubleChestInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/DoubleChestInventory.java new file mode 100644 index 0000000..c03ad53 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/DoubleChestInventory.java @@ -0,0 +1,25 @@ +package org.bukkit.inventory; + +import org.bukkit.block.DoubleChest; + +/** + * Interface to the inventory of a Double Chest. + */ +public interface DoubleChestInventory extends Inventory { + + /** + * Get the left half of this double chest. + * + * @return The left side inventory + */ + Inventory getLeftSide(); + + /** + * Get the right side of this double chest. + * + * @return The right side inventory + */ + Inventory getRightSide(); + + DoubleChest getHolder(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/EnchantingInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/EnchantingInventory.java new file mode 100644 index 0000000..74a863e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/EnchantingInventory.java @@ -0,0 +1,21 @@ +package org.bukkit.inventory; + +/** + * Interface to the inventory of an Enchantment Table. + */ +public interface EnchantingInventory extends Inventory { + + /** + * Set the item being enchanted. + * + * @param item The new item + */ + void setItem(ItemStack item); + + /** + * Get the item being enchanted. + * + * @return The current item. + */ + ItemStack getItem(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/EntityEquipment.java b/vspigot-api/src/main/java/org/bukkit/inventory/EntityEquipment.java new file mode 100644 index 0000000..9e71235 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/EntityEquipment.java @@ -0,0 +1,236 @@ +package org.bukkit.inventory; + +import org.bukkit.entity.Entity; + +/** + * An interface to a creatures inventory + */ +public interface EntityEquipment { + + /** + * Gets a copy of the item the entity is currently holding + * + * @return the currently held item + */ + ItemStack getItemInHand(); + + /** + * Sets the item the entity is holding + * + * @param stack The item to put into the entities hand + */ + void setItemInHand(ItemStack stack); + + /** + * Gets a copy of the helmet currently being worn by the entity + * + * @return The helmet being worn + */ + ItemStack getHelmet(); + + /** + * Sets the helmet worn by the entity + * + * @param helmet The helmet to put on the entity + */ + void setHelmet(ItemStack helmet); + + /** + * Gets a copy of the chest plate currently being worn by the entity + * + * @return The chest plate being worn + */ + ItemStack getChestplate(); + + /** + * Sets the chest plate worn by the entity + * + * @param chestplate The chest plate to put on the entity + */ + void setChestplate(ItemStack chestplate); + + /** + * Gets a copy of the leggings currently being worn by the entity + * + * @return The leggings being worn + */ + ItemStack getLeggings(); + + /** + * Sets the leggings worn by the entity + * + * @param leggings The leggings to put on the entity + */ + void setLeggings(ItemStack leggings); + + /** + * Gets a copy of the boots currently being worn by the entity + * + * @return The boots being worn + */ + ItemStack getBoots(); + + /** + * Sets the boots worn by the entity + * + * @param boots The boots to put on the entity + */ + void setBoots(ItemStack boots); + + /** + * Gets a copy of all worn armor + * + * @return The array of worn armor + */ + ItemStack[] getArmorContents(); + + /** + * Sets the entities armor to the provided array of ItemStacks + * + * @param items The items to set the armor as + */ + void setArmorContents(ItemStack[] items); + + /** + * Clears the entity of all armor and held items + */ + void clear(); + + /** + * Gets the chance of the currently held item being dropped upon this + * creature's death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @return chance of the currently held item being dropped (1 for players) + */ + float getItemInHandDropChance(); + + /** + * Sets the chance of the item this creature is currently holding being + * dropped upon this creature's death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @param chance the chance of the currently held item being dropped + * @throws UnsupportedOperationException when called on players + */ + void setItemInHandDropChance(float chance); + + /** + * Gets the chance of the helmet being dropped upon this creature's death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @return the chance of the helmet being dropped (1 for players) + */ + float getHelmetDropChance(); + + /** + * Sets the chance of the helmet being dropped upon this creature's death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @param chance of the helmet being dropped + * @throws UnsupportedOperationException when called on players + */ + void setHelmetDropChance(float chance); + + /** + * Gets the chance of the chest plate being dropped upon this creature's + * death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @return the chance of the chest plate being dropped (1 for players) + */ + float getChestplateDropChance(); + + /** + * Sets the chance of the chest plate being dropped upon this creature's + * death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @param chance of the chest plate being dropped + * @throws UnsupportedOperationException when called on players + */ + void setChestplateDropChance(float chance); + + /** + * Gets the chance of the leggings being dropped upon this creature's + * death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @return the chance of the leggings being dropped (1 for players) + */ + float getLeggingsDropChance(); + + /** + * Sets the chance of the leggings being dropped upon this creature's + * death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @param chance chance of the leggings being dropped + * @throws UnsupportedOperationException when called on players + */ + void setLeggingsDropChance(float chance); + + /** + * Gets the chance of the boots being dropped upon this creature's death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @return the chance of the boots being dropped (1 for players) + */ + float getBootsDropChance(); + + /** + * Sets the chance of the boots being dropped upon this creature's death + *

          + *

            + *
          • A drop chance of 0F will never drop + *
          • A drop chance of 1F will always drop + *
          + * + * @param chance of the boots being dropped + * @throws UnsupportedOperationException when called on players + */ + void setBootsDropChance(float chance); + + /** + * Get the entity this EntityEquipment belongs to + * + * @return the entity this EntityEquipment belongs to + */ + Entity getHolder(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/FurnaceInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/FurnaceInventory.java new file mode 100644 index 0000000..93b41d3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/FurnaceInventory.java @@ -0,0 +1,53 @@ +package org.bukkit.inventory; + +import org.bukkit.block.Furnace; + +/** + * Interface to the inventory of a Furnace. + */ +public interface FurnaceInventory extends Inventory { + + /** + * Get the current item in the result slot. + * + * @return The item + */ + ItemStack getResult(); + + /** + * Get the current fuel. + * + * @return The item + */ + ItemStack getFuel(); + + /** + * Get the item currently smelting. + * + * @return The item + */ + ItemStack getSmelting(); + + /** + * Set the current fuel. + * + * @param stack The item + */ + void setFuel(ItemStack stack); + + /** + * Set the current item in the result slot. + * + * @param stack The item + */ + void setResult(ItemStack stack); + + /** + * Set the item currently smelting. + * + * @param stack The item + */ + void setSmelting(ItemStack stack); + + Furnace getHolder(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/FurnaceRecipe.java b/vspigot-api/src/main/java/org/bukkit/inventory/FurnaceRecipe.java new file mode 100644 index 0000000..8075323 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/FurnaceRecipe.java @@ -0,0 +1,100 @@ +package org.bukkit.inventory; + +import org.bukkit.Material; +import org.bukkit.material.MaterialData; + +/** + * Represents a smelting recipe. + */ +public class FurnaceRecipe implements Recipe { + private ItemStack output; + private ItemStack ingredient; + + /** + * Create a furnace recipe to craft the specified ItemStack. + * + * @param result The item you want the recipe to create. + * @param source The input material. + */ + public FurnaceRecipe(ItemStack result, Material source) { + this(result, source, 0); + } + + /** + * Create a furnace recipe to craft the specified ItemStack. + * + * @param result The item you want the recipe to create. + * @param source The input material. + */ + public FurnaceRecipe(ItemStack result, MaterialData source) { + this(result, source.getItemType(), source.getData()); + } + + /** + * Create a furnace recipe to craft the specified ItemStack. + * + * @param result The item you want the recipe to create. + * @param source The input material. + * @param data The data value. (Note: This is currently ignored by the + * CraftBukkit server.) + * @deprecated Magic value + */ + @Deprecated + public FurnaceRecipe(ItemStack result, Material source, int data) { + this.output = new ItemStack(result); + this.ingredient = new ItemStack(source, 1, (short) data); + } + + /** + * Sets the input of this furnace recipe. + * + * @param input The input material. + * @return The changed recipe, so you can chain calls. + */ + public FurnaceRecipe setInput(MaterialData input) { + return setInput(input.getItemType(), input.getData()); + } + + /** + * Sets the input of this furnace recipe. + * + * @param input The input material. + * @return The changed recipe, so you can chain calls. + */ + public FurnaceRecipe setInput(Material input) { + return setInput(input, 0); + } + + /** + * Sets the input of this furnace recipe. + * + * @param input The input material. + * @param data The data value. (Note: This is currently ignored by the + * CraftBukkit server.) + * @return The changed recipe, so you can chain calls. + * @deprecated Magic value + */ + @Deprecated + public FurnaceRecipe setInput(Material input, int data) { + this.ingredient = new ItemStack(input, 1, (short) data); + return this; + } + + /** + * Get the input material. + * + * @return The input material. + */ + public ItemStack getInput() { + return this.ingredient.clone(); + } + + /** + * Get the result of this recipe. + * + * @return The resulting stack. + */ + public ItemStack getResult() { + return output.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/HorseInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/HorseInventory.java new file mode 100644 index 0000000..a71efb8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/HorseInventory.java @@ -0,0 +1,35 @@ +package org.bukkit.inventory; + +/** + * An interface to the inventory of a Horse. + */ +public interface HorseInventory extends Inventory { + + /** + * Gets the item in the horse's saddle slot. + * + * @return the saddle item + */ + ItemStack getSaddle(); + + /** + * Gets the item in the horse's armor slot. + * + * @return the armor item + */ + ItemStack getArmor(); + + /** + * Sets the item in the horse's saddle slot. + * + * @param stack the new item + */ + void setSaddle(ItemStack stack); + + /** + * Sets the item in the horse's armor slot. + * + * @param stack the new item + */ + void setArmor(ItemStack stack); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/Inventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/Inventory.java new file mode 100644 index 0000000..da5d83e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/Inventory.java @@ -0,0 +1,381 @@ +package org.bukkit.inventory; + +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; + +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; + +/** + * Interface to the various inventories. Behavior relating to {@link + * Material#AIR} is unspecified. + */ +public interface Inventory extends Iterable { + + /** + * Returns the size of the inventory + * + * @return The size of the inventory + */ + public int getSize(); + + /** + * Returns the maximum stack size for an ItemStack in this inventory. + * + * @return The maximum size for an ItemStack in this inventory. + */ + public int getMaxStackSize(); + + /** + * This method allows you to change the maximum stack size for an + * inventory. + *

          + * Caveats: + *

            + *
          • Not all inventories respect this value. + *
          • Stacks larger than 127 may be clipped when the world is saved. + *
          • This value is not guaranteed to be preserved; be sure to set it + * before every time you want to set a slot over the max stack size. + *
          • Stacks larger than the default max size for this type of inventory + * may not display correctly in the client. + *
          + * + * @param size The new maximum stack size for items in this inventory. + */ + public void setMaxStackSize(int size); + + /** + * Returns the name of the inventory + * + * @return The String with the name of the inventory + */ + public String getName(); + + /** + * Returns the ItemStack found in the slot at the given index + * + * @param index The index of the Slot's ItemStack to return + * @return The ItemStack in the slot + */ + public ItemStack getItem(int index); + + /** + * Stores the ItemStack at the given index of the inventory. + * + * @param index The index where to put the ItemStack + * @param item The ItemStack to set + */ + public void setItem(int index, ItemStack item); + + /** + * Stores the given ItemStacks in the inventory. This will try to fill + * existing stacks and empty slots as well as it can. + *

          + * The returned HashMap contains what it couldn't store, where the key is + * the index of the parameter, and the value is the ItemStack at that + * index of the varargs parameter. If all items are stored, it will return + * an empty HashMap. + *

          + * If you pass in ItemStacks which exceed the maximum stack size for the + * Material, first they will be added to partial stacks where + * Material.getMaxStackSize() is not exceeded, up to + * Material.getMaxStackSize(). When there are no partial stacks left + * stacks will be split on Inventory.getMaxStackSize() allowing you to + * exceed the maximum stack size for that material. + * + * @param items The ItemStacks to add + * @return A HashMap containing items that didn't fit. + * @throws IllegalArgumentException if items or any element in it is null + */ + public HashMap addItem(ItemStack... items) throws IllegalArgumentException; + + /** + * Removes the given ItemStacks from the inventory. + *

          + * It will try to remove 'as much as possible' from the types and amounts + * you give as arguments. + *

          + * The returned HashMap contains what it couldn't remove, where the key is + * the index of the parameter, and the value is the ItemStack at that + * index of the varargs parameter. If all the given ItemStacks are + * removed, it will return an empty HashMap. + * + * @param items The ItemStacks to remove + * @return A HashMap containing items that couldn't be removed. + * @throws IllegalArgumentException if items is null + */ + public HashMap removeItem(ItemStack... items) throws IllegalArgumentException; + + /** + * Returns all ItemStacks from the inventory + * + * @return An array of ItemStacks from the inventory. + */ + public ItemStack[] getContents(); + + /** + * Completely replaces the inventory's contents. Removes all existing + * contents and replaces it with the ItemStacks given in the array. + * + * @param items A complete replacement for the contents; the length must + * be less than or equal to {@link #getSize()}. + * @throws IllegalArgumentException If the array has more items than the + * inventory. + */ + public void setContents(ItemStack[] items) throws IllegalArgumentException; + + /** + * Checks if the inventory contains any ItemStacks with the given + * materialId + * + * @param materialId The materialId to check for + * @return true if an ItemStack in this inventory contains the materialId + * @deprecated Magic value + */ + @Deprecated + public boolean contains(int materialId); + + /** + * Checks if the inventory contains any ItemStacks with the given + * material. + * + * @param material The material to check for + * @return true if an ItemStack is found with the given Material + * @throws IllegalArgumentException if material is null + */ + public boolean contains(Material material) throws IllegalArgumentException; + + /** + * Checks if the inventory contains any ItemStacks matching the given + * ItemStack. + *

          + * This will only return true if both the type and the amount of the stack + * match. + * + * @param item The ItemStack to match against + * @return false if item is null, true if any exactly matching ItemStacks + * were found + */ + public boolean contains(ItemStack item); + + /** + * Checks if the inventory contains any ItemStacks with the given + * materialId, adding to at least the minimum amount specified. + * + * @param materialId The materialId to check for + * @param amount The minimum amount to look for + * @return true if this contains any matching ItemStack with the given + * materialId and amount + * @deprecated Magic value + */ + @Deprecated + public boolean contains(int materialId, int amount); + + /** + * Checks if the inventory contains any ItemStacks with the given + * material, adding to at least the minimum amount specified. + * + * @param material The material to check for + * @param amount The minimum amount + * @return true if amount is less than 1, true if enough ItemStacks were + * found to add to the given amount + * @throws IllegalArgumentException if material is null + */ + public boolean contains(Material material, int amount) throws IllegalArgumentException; + + /** + * Checks if the inventory contains at least the minimum amount specified + * of exactly matching ItemStacks. + *

          + * An ItemStack only counts if both the type and the amount of the stack + * match. + * + * @param item the ItemStack to match against + * @param amount how many identical stacks to check for + * @return false if item is null, true if amount less than 1, true if + * amount of exactly matching ItemStacks were found + * @see #containsAtLeast(ItemStack, int) + */ + public boolean contains(ItemStack item, int amount); + + /** + * Checks if the inventory contains ItemStacks matching the given + * ItemStack whose amounts sum to at least the minimum amount specified. + * + * @param item the ItemStack to match against + * @param amount the minimum amount + * @return false if item is null, true if amount less than 1, true if + * enough ItemStacks were found to add to the given amount + */ + public boolean containsAtLeast(ItemStack item, int amount); + + /** + * Returns a HashMap with all slots and ItemStacks in the inventory with + * given materialId. + *

          + * The HashMap contains entries where, the key is the slot index, and the + * value is the ItemStack in that slot. If no matching ItemStack with the + * given materialId is found, an empty map is returned. + * + * @param materialId The materialId to look for + * @return A HashMap containing the slot index, ItemStack pairs + * @deprecated Magic value + */ + @Deprecated + public HashMap all(int materialId); + + /** + * Returns a HashMap with all slots and ItemStacks in the inventory with + * the given Material. + *

          + * The HashMap contains entries where, the key is the slot index, and the + * value is the ItemStack in that slot. If no matching ItemStack with the + * given Material is found, an empty map is returned. + * + * @param material The material to look for + * @return A HashMap containing the slot index, ItemStack pairs + * @throws IllegalArgumentException if material is null + */ + public HashMap all(Material material) throws IllegalArgumentException; + + /** + * Finds all slots in the inventory containing any ItemStacks with the + * given ItemStack. This will only match slots if both the type and the + * amount of the stack match + *

          + * The HashMap contains entries where, the key is the slot index, and the + * value is the ItemStack in that slot. If no matching ItemStack with the + * given Material is found, an empty map is returned. + * + * @param item The ItemStack to match against + * @return A map from slot indexes to item at index + */ + public HashMap all(ItemStack item); + + /** + * Finds the first slot in the inventory containing an ItemStack with the + * given materialId. + * + * @param materialId The materialId to look for + * @return The slot index of the given materialId or -1 if not found + * @deprecated Magic value + */ + @Deprecated + public int first(int materialId); + + /** + * Finds the first slot in the inventory containing an ItemStack with the + * given material + * + * @param material The material to look for + * @return The slot index of the given Material or -1 if not found + * @throws IllegalArgumentException if material is null + */ + public int first(Material material) throws IllegalArgumentException; + + /** + * Returns the first slot in the inventory containing an ItemStack with + * the given stack. This will only match a slot if both the type and the + * amount of the stack match + * + * @param item The ItemStack to match against + * @return The slot index of the given ItemStack or -1 if not found + */ + public int first(ItemStack item); + + /** + * Returns the first empty Slot. + * + * @return The first empty Slot found, or -1 if no empty slots. + */ + public int firstEmpty(); + + /** + * Removes all stacks in the inventory matching the given materialId. + * + * @param materialId The material to remove + * @deprecated Magic value + */ + @Deprecated + public void remove(int materialId); + + /** + * Removes all stacks in the inventory matching the given material. + * + * @param material The material to remove + * @throws IllegalArgumentException if material is null + */ + public void remove(Material material) throws IllegalArgumentException; + + /** + * Removes all stacks in the inventory matching the given stack. + *

          + * This will only match a slot if both the type and the amount of the + * stack match + * + * @param item The ItemStack to match against + */ + public void remove(ItemStack item); + + /** + * Clears out a particular slot in the index. + * + * @param index The index to empty. + */ + public void clear(int index); + + /** + * Clears out the whole Inventory. + */ + public void clear(); + + /** + * Gets a list of players viewing the inventory. Note that a player is + * considered to be viewing their own inventory and internal crafting + * screen even when said inventory is not open. They will normally be + * considered to be viewing their inventory even when they have a + * different inventory screen open, but it's possible for customized + * inventory screens to exclude the viewer's inventory, so this should + * never be assumed to be non-empty. + * + * @return A list of HumanEntities who are viewing this Inventory. + */ + public List getViewers(); + + /** + * Returns the title of this inventory. + * + * @return A String with the title. + */ + public String getTitle(); + + /** + * Returns what type of inventory this is. + * + * @return The InventoryType representing the type of inventory. + */ + public InventoryType getType(); + + /** + * Gets the block or entity belonging to the open inventory + * + * @return The holder of the inventory; null if it has no holder. + */ + public InventoryHolder getHolder(); + + @Override + public ListIterator iterator(); + + /** + * Returns an iterator starting at the given index. If the index is + * positive, then the first call to next() will return the item at that + * index; if it is negative, the first call to previous will return the + * item at index (getSize() + index). + * + * @param index The index. + * @return An iterator. + */ + public ListIterator iterator(int index); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/InventoryHolder.java b/vspigot-api/src/main/java/org/bukkit/inventory/InventoryHolder.java new file mode 100644 index 0000000..9c06a3d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/InventoryHolder.java @@ -0,0 +1,11 @@ +package org.bukkit.inventory; + +public interface InventoryHolder { + + /** + * Get the object's inventory. + * + * @return The inventory. + */ + public Inventory getInventory(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/InventoryView.java b/vspigot-api/src/main/java/org/bukkit/inventory/InventoryView.java new file mode 100644 index 0000000..7e98d52 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/InventoryView.java @@ -0,0 +1,231 @@ +package org.bukkit.inventory; + +import org.bukkit.GameMode; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; + +/** + * Represents a view linking two inventories and a single player (whose + * inventory may or may not be one of the two). + *

          + * Note: If you implement this interface but fail to satisfy the expected + * contracts of certain methods, there's no guarantee that the game will work + * as it should. + */ +public abstract class InventoryView { + public final static int OUTSIDE = -999; + /** + * Represents various extra properties of certain inventory windows. + */ + public enum Property { + /** + * The progress of the down-pointing arrow in a brewing inventory. + */ + BREW_TIME(0, InventoryType.BREWING), + /** + * The progress of the right-pointing arrow in a furnace inventory. + */ + COOK_TIME(0, InventoryType.FURNACE), + /** + * The progress of the flame in a furnace inventory. + */ + BURN_TIME(1, InventoryType.FURNACE), + /** + * How many total ticks the current fuel should last. + */ + TICKS_FOR_CURRENT_FUEL(2, InventoryType.FURNACE), + /** + * In an enchanting inventory, the top button's experience level + * value. + */ + ENCHANT_BUTTON1(0, InventoryType.ENCHANTING), + /** + * In an enchanting inventory, the middle button's experience level + * value. + */ + ENCHANT_BUTTON2(1, InventoryType.ENCHANTING), + /** + * In an enchanting inventory, the bottom button's experience level + * value. + */ + ENCHANT_BUTTON3(2, InventoryType.ENCHANTING); + int id; + InventoryType style; + private Property(int id, InventoryType appliesTo) { + this.id = id; + style = appliesTo; + } + + public InventoryType getType() { + return style; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + } + /** + * Get the upper inventory involved in this transaction. + * + * @return the inventory + */ + public abstract Inventory getTopInventory(); + + /** + * Get the lower inventory involved in this transaction. + * + * @return the inventory + */ + public abstract Inventory getBottomInventory(); + + /** + * Get the player viewing. + * + * @return the player + */ + public abstract HumanEntity getPlayer(); + + /** + * Determine the type of inventory involved in the transaction. This + * indicates the window style being shown. It will never return PLAYER, + * since that is common to all windows. + * + * @return the inventory type + */ + public abstract InventoryType getType(); + + /** + * Sets one item in this inventory view by its raw slot ID. + *

          + * Note: If slot ID -999 is chosen, it may be expected that the item is + * dropped on the ground. This is not required behaviour, however. + * + * @param slot The ID as returned by InventoryClickEvent.getRawSlot() + * @param item The new item to put in the slot, or null to clear it. + */ + public void setItem(int slot, ItemStack item) { + if (slot != OUTSIDE) { + if (slot < getTopInventory().getSize()) { + getTopInventory().setItem(convertSlot(slot),item); + } else { + getBottomInventory().setItem(convertSlot(slot),item); + } + } else { + getPlayer().getWorld().dropItemNaturally(getPlayer().getLocation(), item); + } + } + + /** + * Gets one item in this inventory view by its raw slot ID. + * + * @param slot The ID as returned by InventoryClickEvent.getRawSlot() + * @return The item currently in the slot. + */ + public ItemStack getItem(int slot) { + if (slot == OUTSIDE) { + return null; + } + if (slot < getTopInventory().getSize()) { + return getTopInventory().getItem(convertSlot(slot)); + } else { + return getBottomInventory().getItem(convertSlot(slot)); + } + } + + /** + * Sets the item on the cursor of one of the viewing players. + * + * @param item The item to put on the cursor, or null to remove the item + * on their cursor. + */ + public final void setCursor(ItemStack item) { + getPlayer().setItemOnCursor(item); + } + + /** + * Get the item on the cursor of one of the viewing players. + * + * @return The item on the player's cursor, or null if they aren't holding + * one. + */ + public final ItemStack getCursor() { + return getPlayer().getItemOnCursor(); + } + + /** + * Converts a raw slot ID into its local slot ID into whichever of the two + * inventories the slot points to. + *

          + * If the raw slot refers to the upper inventory, it will be returned + * unchanged and thus be suitable for getTopInventory().getItem(); if it + * refers to the lower inventory, the output will differ from the input + * and be suitable for getBottomInventory().getItem(). + * + * @param rawSlot The raw slot ID. + * @return The converted slot ID. + */ + public final int convertSlot(int rawSlot) { + int numInTop = getTopInventory().getSize(); + if (rawSlot < numInTop) { + return rawSlot; + } + int slot = rawSlot - numInTop; + if (getPlayer().getGameMode() == GameMode.CREATIVE && getType() == InventoryType.PLAYER) { + return slot; + } + if (getType() == InventoryType.CRAFTING) { + if(slot < 4) return 39 - slot; + else slot -= 4; + } + if (slot >= 27) slot -= 27; + else slot += 9; + return slot; + } + + /** + * Closes the inventory view. + */ + public final void close() { + getPlayer().closeInventory(); + } + + /** + * Check the total number of slots in this view, combining the upper and + * lower inventories. + *

          + * Note though that it's possible for this to be greater than the sum of + * the two inventories if for example some slots are not being used. + * + * @return The total size + */ + public final int countSlots() { + return getTopInventory().getSize() + getBottomInventory().getSize(); + } + + /** + * Sets an extra property of this inventory if supported by that + * inventory, for example the state of a progress bar. + * + * @param prop the window property to update + * @param value the new value for the window property + * @return true if the property was updated successfully, false if the + * property is not supported by that inventory + */ + public final boolean setProperty(Property prop, int value) { + return getPlayer().setWindowProperty(prop, value); + } + + /** + * Get the title of this inventory window. + * + * @return The title. + */ + public final String getTitle() { + return getTopInventory().getTitle(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/ItemFactory.java b/vspigot-api/src/main/java/org/bukkit/inventory/ItemFactory.java new file mode 100644 index 0000000..52a8d4d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/ItemFactory.java @@ -0,0 +1,124 @@ +package org.bukkit.inventory; + +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +/** + * An instance of the ItemFactory can be obtained with {@link + * Server#getItemFactory()}. + *

          + * The ItemFactory is solely responsible for creating item meta containers to + * apply on item stacks. + */ +public interface ItemFactory { + + /** + * This creates a new item meta for the material. + * + * @param material The material to consider as base for the meta + * @return a new ItemMeta that could be applied to an item stack of the + * specified material + */ + ItemMeta getItemMeta(final Material material); + + /** + * This method checks the item meta to confirm that it is applicable (no + * data lost if applied) to the specified ItemStack. + *

          + * A {@link SkullMeta} would not be valid for a sword, but a normal {@link + * ItemMeta} from an enchanted dirt block would. + * + * @param meta Meta to check + * @param stack Item that meta will be applied to + * @return true if the meta can be applied without losing data, false + * otherwise + * @throws IllegalArgumentException if the meta was not created by this + * factory + */ + boolean isApplicable(final ItemMeta meta, final ItemStack stack) throws IllegalArgumentException; + + /** + * This method checks the item meta to confirm that it is applicable (no + * data lost if applied) to the specified Material. + *

          + * A {@link SkullMeta} would not be valid for a sword, but a normal {@link + * ItemMeta} from an enchanted dirt block would. + * + * @param meta Meta to check + * @param material Material that meta will be applied to + * @return true if the meta can be applied without losing data, false + * otherwise + * @throws IllegalArgumentException if the meta was not created by this + * factory + */ + boolean isApplicable(final ItemMeta meta, final Material material) throws IllegalArgumentException; + + /** + * This method is used to compare two item meta data objects. + * + * @param meta1 First meta to compare, and may be null to indicate no data + * @param meta2 Second meta to compare, and may be null to indicate no + * data + * @return false if one of the meta has data the other does not, otherwise + * true + * @throws IllegalArgumentException if either meta was not created by this + * factory + */ + boolean equals(final ItemMeta meta1, final ItemMeta meta2) throws IllegalArgumentException; + + /** + * Returns an appropriate item meta for the specified stack. + *

          + * The item meta returned will always be a valid meta for a given + * ItemStack of the specified material. It may be a more or less specific + * meta, and could also be the same meta or meta type as the parameter. + * The item meta returned will also always be the most appropriate meta. + *

          + * Example, if a {@link SkullMeta} is being applied to a book, this method + * would return a {@link BookMeta} containing all information in the + * specified meta that is applicable to an {@link ItemMeta}, the highest + * common interface. + * + * @param meta the meta to convert + * @param stack the stack to convert the meta for + * @return An appropriate item meta for the specified item stack. No + * guarantees are made as to if a copy is returned. This will be null + * for a stack of air. + * @throws IllegalArgumentException if the specified meta was not created + * by this factory + */ + ItemMeta asMetaFor(final ItemMeta meta, final ItemStack stack) throws IllegalArgumentException; + + /** + * Returns an appropriate item meta for the specified material. + *

          + * The item meta returned will always be a valid meta for a given + * ItemStack of the specified material. It may be a more or less specific + * meta, and could also be the same meta or meta type as the parameter. + * The item meta returned will also always be the most appropriate meta. + *

          + * Example, if a {@link SkullMeta} is being applied to a book, this method + * would return a {@link BookMeta} containing all information in the + * specified meta that is applicable to an {@link ItemMeta}, the highest + * common interface. + * + * @param meta the meta to convert + * @param material the material to convert the meta for + * @return An appropriate item meta for the specified item material. No + * guarantees are made as to if a copy is returned. This will be null for air. + * @throws IllegalArgumentException if the specified meta was not created + * by this factory + */ + ItemMeta asMetaFor(final ItemMeta meta, final Material material) throws IllegalArgumentException; + + /** + * Returns the default color for all leather armor. + * + * @return the default color for leather armor + */ + Color getDefaultLeatherColor(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/ItemFlag.java b/vspigot-api/src/main/java/org/bukkit/inventory/ItemFlag.java new file mode 100644 index 0000000..7cea916 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/ItemFlag.java @@ -0,0 +1,32 @@ +package org.bukkit.inventory; + +/** + * A ItemFlag can hide some Attributes from ItemStacks + */ +public enum ItemFlag { + + /** + * Setting to show/hide enchants + */ + HIDE_ENCHANTS, + /** + * Setting to show/hide Attributes like Damage + */ + HIDE_ATTRIBUTES, + /**cd + * Setting to show/hide the unbreakable State + */ + HIDE_UNBREAKABLE, + /** + * Setting to show/hide what the ItemStack can break/destroy + */ + HIDE_DESTROYS, + /** + * Setting to show/hide where this ItemStack can be build/placed on + */ + HIDE_PLACED_ON, + /** + * Setting to show/hide potion effects on this ItemStack + */ + HIDE_POTION_EFFECTS; +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/ItemStack.java b/vspigot-api/src/main/java/org/bukkit/inventory/ItemStack.java new file mode 100644 index 0000000..6137c99 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/ItemStack.java @@ -0,0 +1,603 @@ +package org.bukkit.inventory; + +import com.google.common.collect.ImmutableMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Utility; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.material.MaterialData; + +/** + * Represents a stack of items + */ +public class ItemStack implements Cloneable, ConfigurationSerializable { + private int type = 0; + private int amount = 0; + private MaterialData data = null; + private short durability = 0; + private ItemMeta meta; + + @Utility + protected ItemStack() {} + + /** + * Defaults stack size to 1, with no extra data + * + * @param type item material id + * @deprecated Magic value + */ + @Deprecated + public ItemStack(final int type) { + this(type, 1); + } + + /** + * Defaults stack size to 1, with no extra data + * + * @param type item material + */ + public ItemStack(final Material type) { + this(type, 1); + } + + /** + * An item stack with no extra data + * + * @param type item material id + * @param amount stack size + * @deprecated Magic value + */ + @Deprecated + public ItemStack(final int type, final int amount) { + this(type, amount, (short) 0); + } + + /** + * An item stack with no extra data + * + * @param type item material + * @param amount stack size + */ + public ItemStack(final Material type, final int amount) { + this(type.getId(), amount); + } + + /** + * An item stack with the specified damage / durability + * + * @param type item material id + * @param amount stack size + * @param damage durability / damage + * @deprecated Magic value + */ + @Deprecated + public ItemStack(final int type, final int amount, final short damage) { + this.type = type; + this.amount = amount; + this.durability = damage; + } + + /** + * An item stack with the specified damage / durabiltiy + * + * @param type item material + * @param amount stack size + * @param damage durability / damage + */ + public ItemStack(final Material type, final int amount, final short damage) { + this(type.getId(), amount, damage); + } + + /** + * @deprecated this method uses an ambiguous data byte object + */ + @Deprecated + public ItemStack(final int type, final int amount, final short damage, final Byte data) { + this.type = type; + this.amount = amount; + this.durability = damage; + if (data != null) { + createData(data); + this.durability = data; + } + } + + /** + * @deprecated this method uses an ambiguous data byte object + */ + @Deprecated + public ItemStack(final Material type, final int amount, final short damage, final Byte data) { + this(type.getId(), amount, damage, data); + } + + /** + * Creates a new item stack derived from the specified stack + * + * @param stack the stack to copy + * @throws IllegalArgumentException if the specified stack is null or + * returns an item meta not created by the item factory + */ + public ItemStack(final ItemStack stack) throws IllegalArgumentException { + Validate.notNull(stack, "Cannot copy null stack"); + this.type = stack.getTypeId(); + this.amount = stack.getAmount(); + this.durability = stack.getDurability(); + this.data = stack.getData(); + if (stack.hasItemMeta()) { + setItemMeta0(stack.getItemMeta(), getType0()); + } + } + + /** + * Gets the type of this item + * + * @return Type of the items in this stack + */ + @Utility + public Material getType() { + return getType0(getTypeId()); + } + + private Material getType0() { + return getType0(this.type); + } + + private static Material getType0(int id) { + Material material = Material.getMaterial(id); + return material == null ? Material.AIR : material; + } + + /** + * Sets the type of this item + *

          + * Note that in doing so you will reset the MaterialData for this stack + * + * @param type New type to set the items in this stack to + */ + @Utility + public void setType(Material type) { + Validate.notNull(type, "Material cannot be null"); + setTypeId(type.getId()); + } + + /** + * Gets the type id of this item + * + * @return Type Id of the items in this stack + * @deprecated Magic value + */ + @Deprecated + public int getTypeId() { + return type; + } + + /** + * Sets the type id of this item + *

          + * Note that in doing so you will reset the MaterialData for this stack + * + * @param type New type id to set the items in this stack to + * @deprecated Magic value + */ + @Deprecated + public void setTypeId(int type) { + this.type = type; + if (this.meta != null) { + this.meta = Bukkit.getItemFactory().asMetaFor(meta, getType0()); + } + createData((byte) 0); + } + + /** + * Gets the amount of items in this stack + * + * @return Amount of items in this stick + */ + public int getAmount() { + return amount; + } + + /** + * Sets the amount of items in this stack + * + * @param amount New amount of items in this stack + */ + public void setAmount(int amount) { + this.amount = amount; + } + + /** + * Gets the MaterialData for this stack of items + * + * @return MaterialData for this item + */ + public MaterialData getData() { + Material mat = getType(); + if (data == null && mat != null && mat.getData() != null) { + data = mat.getNewData((byte) this.getDurability()); + } + + return data; + } + + /** + * Sets the MaterialData for this stack of items + * + * @param data New MaterialData for this item + */ + public void setData(MaterialData data) { + Material mat = getType(); + + if (data == null || mat == null || mat.getData() == null) { + this.data = data; + } else { + if ((data.getClass() == mat.getData()) || (data.getClass() == MaterialData.class)) { + this.data = data; + } else { + throw new IllegalArgumentException("Provided data is not of type " + mat.getData().getName() + ", found " + data.getClass().getName()); + } + } + } + + /** + * Sets the durability of this item + * + * @param durability Durability of this item + */ + public void setDurability(final short durability) { + this.durability = durability; + } + + /** + * Gets the durability of this item + * + * @return Durability of this item + */ + public short getDurability() { + return durability; + } + + /** + * Get the maximum stacksize for the material hold in this ItemStack. + * (Returns -1 if it has no idea) + * + * @return The maximum you can stack this material to. + */ + @Utility + public int getMaxStackSize() { + Material material = getType(); + if (material != null) { + return material.getMaxStackSize(); + } + return -1; + } + + private void createData(final byte data) { + Material mat = Material.getMaterial(type); + + if (mat == null) { + this.data = new MaterialData(type, data); + } else { + this.data = mat.getNewData(data); + } + } + + @Override + @Utility + public String toString() { + StringBuilder toString = new StringBuilder("ItemStack{").append(getType().name()).append(" x ").append(getAmount()); + if (hasItemMeta()) { + toString.append(", ").append(getItemMeta()); + } + return toString.append('}').toString(); + } + + @Override + @Utility + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ItemStack)) { + return false; + } + + ItemStack stack = (ItemStack) obj; + return getAmount() == stack.getAmount() && isSimilar(stack); + } + + /** + * This method is the same as equals, but does not consider stack size + * (amount). + * + * @param stack the item stack to compare to + * @return true if the two stacks are equal, ignoring the amount + */ + @Utility + public boolean isSimilar(ItemStack stack) { + if (stack == null) { + return false; + } + if (stack == this) { + return true; + } + return getTypeId() == stack.getTypeId() && getDurability() == stack.getDurability() && hasItemMeta() == stack.hasItemMeta() && (hasItemMeta() ? Bukkit.getItemFactory().equals(getItemMeta(), stack.getItemMeta()) : true); + } + + @Override + public ItemStack clone() { + try { + ItemStack itemStack = (ItemStack) super.clone(); + + if (this.meta != null) { + itemStack.meta = this.meta.clone(); + } + + if (this.data != null) { + itemStack.data = this.data.clone(); + } + + return itemStack; + } catch (CloneNotSupportedException e) { + throw new Error(e); + } + } + + @Override + @Utility + public final int hashCode() { + int hash = 1; + + hash = hash * 31 + getTypeId(); + hash = hash * 31 + getAmount(); + hash = hash * 31 + (getDurability() & 0xffff); + hash = hash * 31 + (hasItemMeta() ? (meta == null ? getItemMeta().hashCode() : meta.hashCode()) : 0); + + return hash; + } + + /** + * Checks if this ItemStack contains the given {@link Enchantment} + * + * @param ench Enchantment to test + * @return True if this has the given enchantment + */ + public boolean containsEnchantment(Enchantment ench) { + return meta == null ? false : meta.hasEnchant(ench); + } + + /** + * Gets the level of the specified enchantment on this item stack + * + * @param ench Enchantment to check + * @return Level of the enchantment, or 0 + */ + public int getEnchantmentLevel(Enchantment ench) { + return meta == null ? 0 : meta.getEnchantLevel(ench); + } + + /** + * Gets a map containing all enchantments and their levels on this item. + * + * @return Map of enchantments. + */ + public Map getEnchantments() { + return meta == null ? ImmutableMap.of() : meta.getEnchants(); + } + + /** + * Adds the specified enchantments to this item stack. + *

          + * This method is the same as calling {@link + * #addEnchantment(org.bukkit.enchantments.Enchantment, int)} for each + * element of the map. + * + * @param enchantments Enchantments to add + * @throws IllegalArgumentException if the specified enchantments is null + * @throws IllegalArgumentException if any specific enchantment or level + * is null. Warning: Some enchantments may be added before this + * exception is thrown. + */ + @Utility + public void addEnchantments(Map enchantments) { + Validate.notNull(enchantments, "Enchantments cannot be null"); + for (Map.Entry entry : enchantments.entrySet()) { + addEnchantment(entry.getKey(), entry.getValue()); + } + } + + /** + * Adds the specified {@link Enchantment} to this item stack. + *

          + * If this item stack already contained the given enchantment (at any + * level), it will be replaced. + * + * @param ench Enchantment to add + * @param level Level of the enchantment + * @throws IllegalArgumentException if enchantment null, or enchantment is + * not applicable + */ + @Utility + public void addEnchantment(Enchantment ench, int level) { + Validate.notNull(ench, "Enchantment cannot be null"); + if ((level < ench.getStartLevel()) || (level > ench.getMaxLevel())) { + throw new IllegalArgumentException("Enchantment level is either too low or too high (given " + level + ", bounds are " + ench.getStartLevel() + " to " + ench.getMaxLevel() + ")"); + } else if (!ench.canEnchantItem(this)) { + throw new IllegalArgumentException("Specified enchantment cannot be applied to this itemstack"); + } + + addUnsafeEnchantment(ench, level); + } + + /** + * Adds the specified enchantments to this item stack in an unsafe manner. + *

          + * This method is the same as calling {@link + * #addUnsafeEnchantment(org.bukkit.enchantments.Enchantment, int)} for + * each element of the map. + * + * @param enchantments Enchantments to add + */ + @Utility + public void addUnsafeEnchantments(Map enchantments) { + for (Map.Entry entry : enchantments.entrySet()) { + addUnsafeEnchantment(entry.getKey(), entry.getValue()); + } + } + + /** + * Adds the specified {@link Enchantment} to this item stack. + *

          + * If this item stack already contained the given enchantment (at any + * level), it will be replaced. + *

          + * This method is unsafe and will ignore level restrictions or item type. + * Use at your own discretion. + * + * @param ench Enchantment to add + * @param level Level of the enchantment + */ + public void addUnsafeEnchantment(Enchantment ench, int level) { + (meta == null ? meta = Bukkit.getItemFactory().getItemMeta(getType0()) : meta).addEnchant(ench, level, true); + } + + /** + * Removes the specified {@link Enchantment} if it exists on this + * ItemStack + * + * @param ench Enchantment to remove + * @return Previous level, or 0 + */ + public int removeEnchantment(Enchantment ench) { + int level = getEnchantmentLevel(ench); + if (level == 0 || meta == null) { + return level; + } + meta.removeEnchant(ench); + return level; + } + + @Utility + public Map serialize() { + Map result = new LinkedHashMap(); + + result.put("type", getType().name()); + + if (getDurability() != 0) { + result.put("damage", getDurability()); + } + + if (getAmount() != 1) { + result.put("amount", getAmount()); + } + + ItemMeta meta = getItemMeta(); + if (!Bukkit.getItemFactory().equals(meta, null)) { + result.put("meta", meta); + } + + return result; + } + + /** + * Required method for configuration serialization + * + * @param args map to deserialize + * @return deserialized item stack + * @see ConfigurationSerializable + */ + public static ItemStack deserialize(Map args) { + Material type = Material.getMaterial((String) args.get("type")); + short damage = 0; + int amount = 1; + + if (args.containsKey("damage")) { + damage = ((Number) args.get("damage")).shortValue(); + } + + if (args.containsKey("amount")) { + amount = (Integer) args.get("amount"); + } + + ItemStack result = new ItemStack(type, amount, damage); + + if (args.containsKey("enchantments")) { // Backward compatiblity, @deprecated + Object raw = args.get("enchantments"); + + if (raw instanceof Map) { + Map map = (Map) raw; + + for (Map.Entry entry : map.entrySet()) { + Enchantment enchantment = Enchantment.getByName(entry.getKey().toString()); + + if ((enchantment != null) && (entry.getValue() instanceof Integer)) { + result.addUnsafeEnchantment(enchantment, (Integer) entry.getValue()); + } + } + } + } else if (args.containsKey("meta")) { // We cannot and will not have meta when enchantments (pre-ItemMeta) exist + Object raw = args.get("meta"); + if (raw instanceof ItemMeta) { + result.setItemMeta((ItemMeta) raw); + } + } + + return result; + } + + /** + * Get a copy of this ItemStack's {@link ItemMeta}. + * + * @return a copy of the current ItemStack's ItemData + */ + public ItemMeta getItemMeta() { + return this.meta == null ? Bukkit.getItemFactory().getItemMeta(getType0()) : this.meta.clone(); + } + + /** + * Checks to see if any meta data has been defined. + * + * @return Returns true if some meta data has been set for this item + */ + public boolean hasItemMeta() { + return !Bukkit.getItemFactory().equals(meta, null); + } + + /** + * Set the ItemMeta of this ItemStack. + * + * @param itemMeta new ItemMeta, or null to indicate meta data be cleared. + * @return True if successfully applied ItemMeta, see {@link + * ItemFactory#isApplicable(ItemMeta, ItemStack)} + * @throws IllegalArgumentException if the item meta was not created by + * the {@link ItemFactory} + */ + public boolean setItemMeta(ItemMeta itemMeta) { + return setItemMeta0(itemMeta, getType0()); + } + + /* + * Cannot be overridden, so it's safe for constructor call + */ + private boolean setItemMeta0(ItemMeta itemMeta, Material material) { + if (itemMeta == null) { + this.meta = null; + return true; + } + if (!Bukkit.getItemFactory().isApplicable(itemMeta, material)) { + return false; + } + this.meta = Bukkit.getItemFactory().asMetaFor(itemMeta, material); + if (this.meta == itemMeta) { + this.meta = itemMeta.clone(); + } + + return true; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/MerchantInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/MerchantInventory.java new file mode 100644 index 0000000..163f459 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/MerchantInventory.java @@ -0,0 +1,4 @@ +package org.bukkit.inventory; + +public interface MerchantInventory extends Inventory { +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/PlayerInventory.java b/vspigot-api/src/main/java/org/bukkit/inventory/PlayerInventory.java new file mode 100644 index 0000000..d18c9f4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/PlayerInventory.java @@ -0,0 +1,130 @@ +package org.bukkit.inventory; + +import org.bukkit.entity.HumanEntity; + +/** + * Interface to the inventory of a Player, including the four armor slots. + */ +public interface PlayerInventory extends Inventory { + + /** + * Get all ItemStacks from the armor slots + * + * @return All the ItemStacks from the armor slots + */ + public ItemStack[] getArmorContents(); + + /** + * Return the ItemStack from the helmet slot + * + * @return The ItemStack in the helmet slot + */ + public ItemStack getHelmet(); + + /** + * Return the ItemStack from the chestplate slot + * + * @return The ItemStack in the chestplate slot + */ + public ItemStack getChestplate(); + + /** + * Return the ItemStack from the leg slot + * + * @return The ItemStack in the leg slot + */ + public ItemStack getLeggings(); + + /** + * Return the ItemStack from the boots slot + * + * @return The ItemStack in the boots slot + */ + public ItemStack getBoots(); + + /** + * Put the given ItemStacks into the armor slots + * + * @param items The ItemStacks to use as armour + */ + public void setArmorContents(ItemStack[] items); + + /** + * Put the given ItemStack into the helmet slot. This does not check if + * the ItemStack is a helmet + * + * @param helmet The ItemStack to use as helmet + */ + public void setHelmet(ItemStack helmet); + + /** + * Put the given ItemStack into the chestplate slot. This does not check + * if the ItemStack is a chestplate + * + * @param chestplate The ItemStack to use as chestplate + */ + public void setChestplate(ItemStack chestplate); + + /** + * Put the given ItemStack into the leg slot. This does not check if the + * ItemStack is a pair of leggings + * + * @param leggings The ItemStack to use as leggings + */ + public void setLeggings(ItemStack leggings); + + /** + * Put the given ItemStack into the boots slot. This does not check if the + * ItemStack is a boots + * + * @param boots The ItemStack to use as boots + */ + public void setBoots(ItemStack boots); + + /** + * Returns the ItemStack currently hold + * + * @return The currently held ItemStack + */ + public ItemStack getItemInHand(); + + /** + * Sets the item in hand + * + * @param stack Stack to set + */ + public void setItemInHand(ItemStack stack); + + /** + * Get the slot number of the currently held item + * + * @return Held item slot number + */ + public int getHeldItemSlot(); + + /** + * Set the slot number of the currently held item. + *

          + * This validates whether the slot is between 0 and 8 inclusive. + * + * @param slot The new slot number + * @throws IllegalArgumentException Thrown if slot is not between 0 and 8 + * inclusive + */ + public void setHeldItemSlot(int slot); + + /** + * Clears all matching items from the inventory. Setting either value to + * -1 will skip it's check, while setting both to -1 will clear all items + * in your inventory unconditionally. + * + * @param id the id of the item you want to clear from the inventory + * @param data the data of the item you want to clear from the inventory + * @return The number of items cleared + * @deprecated Magic value + */ + @Deprecated + public int clear(int id, int data); + + public HumanEntity getHolder(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/Recipe.java b/vspigot-api/src/main/java/org/bukkit/inventory/Recipe.java new file mode 100644 index 0000000..7977ce2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/Recipe.java @@ -0,0 +1,14 @@ +package org.bukkit.inventory; + +/** + * Represents some type of crafting recipe. + */ +public interface Recipe { + + /** + * Get the result of this recipe. + * + * @return The result stack + */ + ItemStack getResult(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java b/vspigot-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java new file mode 100644 index 0000000..2796473 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java @@ -0,0 +1,148 @@ +package org.bukkit.inventory; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang.Validate; + +import org.bukkit.Material; +import org.bukkit.material.MaterialData; + +/** + * Represents a shaped (ie normal) crafting recipe. + */ +public class ShapedRecipe implements Recipe { + private ItemStack output; + private String[] rows; + private Map ingredients = new HashMap(); + + /** + * Create a shaped recipe to craft the specified ItemStack. The + * constructor merely determines the result and type; to set the actual + * recipe, you'll need to call the appropriate methods. + * + * @param result The item you want the recipe to create. + * @see ShapedRecipe#shape(String...) + * @see ShapedRecipe#setIngredient(char, Material) + * @see ShapedRecipe#setIngredient(char, Material, int) + * @see ShapedRecipe#setIngredient(char, MaterialData) + */ + public ShapedRecipe(ItemStack result) { + this.output = new ItemStack(result); + } + + /** + * Set the shape of this recipe to the specified rows. Each character + * represents a different ingredient; exactly what each character + * represents is set separately. The first row supplied corresponds with + * the upper most part of the recipe on the workbench e.g. if all three + * rows are supplies the first string represents the top row on the + * workbench. + * + * @param shape The rows of the recipe (up to 3 rows). + * @return The changed recipe, so you can chain calls. + */ + public ShapedRecipe shape(final String... shape) { + Validate.notNull(shape, "Must provide a shape"); + Validate.isTrue(shape.length > 0 && shape.length < 4, "Crafting recipes should be 1, 2, 3 rows, not ", shape.length); + + for (String row : shape) { + Validate.notNull(row, "Shape cannot have null rows"); + Validate.isTrue(row.length() > 0 && row.length() < 4, "Crafting rows should be 1, 2, or 3 characters, not ", row.length()); + } + this.rows = new String[shape.length]; + for (int i = 0; i < shape.length; i++) { + this.rows[i] = shape[i]; + } + + // Remove character mappings for characters that no longer exist in the shape + HashMap newIngredients = new HashMap(); + for (String row : shape) { + for (Character c : row.toCharArray()) { + newIngredients.put(c, ingredients.get(c)); + } + } + this.ingredients = newIngredients; + + return this; + } + + /** + * Sets the material that a character in the recipe shape refers to. + * + * @param key The character that represents the ingredient in the shape. + * @param ingredient The ingredient. + * @return The changed recipe, so you can chain calls. + */ + public ShapedRecipe setIngredient(char key, MaterialData ingredient) { + return setIngredient(key, ingredient.getItemType(), ingredient.getData()); + } + + /** + * Sets the material that a character in the recipe shape refers to. + * + * @param key The character that represents the ingredient in the shape. + * @param ingredient The ingredient. + * @return The changed recipe, so you can chain calls. + */ + public ShapedRecipe setIngredient(char key, Material ingredient) { + return setIngredient(key, ingredient, 0); + } + + /** + * Sets the material that a character in the recipe shape refers to. + * + * @param key The character that represents the ingredient in the shape. + * @param ingredient The ingredient. + * @param raw The raw material data as an integer. + * @return The changed recipe, so you can chain calls. + * @deprecated Magic value + */ + @Deprecated + public ShapedRecipe setIngredient(char key, Material ingredient, int raw) { + Validate.isTrue(ingredients.containsKey(key), "Symbol does not appear in the shape:", key); + + // -1 is the old wildcard, map to Short.MAX_VALUE as the new one + if (raw == -1) { + raw = Short.MAX_VALUE; + } + + ingredients.put(key, new ItemStack(ingredient, 1, (short) raw)); + return this; + } + + /** + * Get a copy of the ingredients map. + * + * @return The mapping of character to ingredients. + */ + public Map getIngredientMap() { + HashMap result = new HashMap(); + for (Map.Entry ingredient : ingredients.entrySet()) { + if (ingredient.getValue() == null) { + result.put(ingredient.getKey(), null); + } else { + result.put(ingredient.getKey(), ingredient.getValue().clone()); + } + } + return result; + } + + /** + * Get the shape. + * + * @return The recipe's shape. + */ + public String[] getShape() { + return rows.clone(); + } + + /** + * Get the result. + * + * @return The result stack. + */ + public ItemStack getResult() { + return output.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/ShapelessRecipe.java b/vspigot-api/src/main/java/org/bukkit/inventory/ShapelessRecipe.java new file mode 100644 index 0000000..a718086 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/ShapelessRecipe.java @@ -0,0 +1,226 @@ +package org.bukkit.inventory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang.Validate; + +import org.bukkit.Material; +import org.bukkit.material.MaterialData; + +/** + * Represents a shapeless recipe, where the arrangement of the ingredients on + * the crafting grid does not matter. + */ +public class ShapelessRecipe implements Recipe { + private ItemStack output; + private List ingredients = new ArrayList(); + + /** + * Create a shapeless recipe to craft the specified ItemStack. The + * constructor merely determines the result and type; to set the actual + * recipe, you'll need to call the appropriate methods. + * + * @param result The item you want the recipe to create. + * @see ShapelessRecipe#addIngredient(Material) + * @see ShapelessRecipe#addIngredient(MaterialData) + * @see ShapelessRecipe#addIngredient(Material,int) + * @see ShapelessRecipe#addIngredient(int,Material) + * @see ShapelessRecipe#addIngredient(int,MaterialData) + * @see ShapelessRecipe#addIngredient(int,Material,int) + */ + public ShapelessRecipe(ItemStack result) { + this.output = new ItemStack(result); + } + + /** + * Adds the specified ingredient. + * + * @param ingredient The ingredient to add. + * @return The changed recipe, so you can chain calls. + */ + public ShapelessRecipe addIngredient(MaterialData ingredient) { + return addIngredient(1, ingredient); + } + + /** + * Adds the specified ingredient. + * + * @param ingredient The ingredient to add. + * @return The changed recipe, so you can chain calls. + */ + public ShapelessRecipe addIngredient(Material ingredient) { + return addIngredient(1, ingredient, 0); + } + + /** + * Adds the specified ingredient. + * + * @param ingredient The ingredient to add. + * @param rawdata The data value, or -1 to allow any data value. + * @return The changed recipe, so you can chain calls. + * @deprecated Magic value + */ + @Deprecated + public ShapelessRecipe addIngredient(Material ingredient, int rawdata) { + return addIngredient(1, ingredient, rawdata); + } + + /** + * Adds multiples of the specified ingredient. + * + * @param count How many to add (can't be more than 9!) + * @param ingredient The ingredient to add. + * @return The changed recipe, so you can chain calls. + */ + public ShapelessRecipe addIngredient(int count, MaterialData ingredient) { + return addIngredient(count, ingredient.getItemType(), ingredient.getData()); + } + + /** + * Adds multiples of the specified ingredient. + * + * @param count How many to add (can't be more than 9!) + * @param ingredient The ingredient to add. + * @return The changed recipe, so you can chain calls. + */ + public ShapelessRecipe addIngredient(int count, Material ingredient) { + return addIngredient(count, ingredient, 0); + } + + /** + * Adds multiples of the specified ingredient. + * + * @param count How many to add (can't be more than 9!) + * @param ingredient The ingredient to add. + * @param rawdata The data value, or -1 to allow any data value. + * @return The changed recipe, so you can chain calls. + * @deprecated Magic value + */ + @Deprecated + public ShapelessRecipe addIngredient(int count, Material ingredient, int rawdata) { + Validate.isTrue(ingredients.size() + count <= 9, "Shapeless recipes cannot have more than 9 ingredients"); + + // -1 is the old wildcard, map to Short.MAX_VALUE as the new one + if (rawdata == -1) { + rawdata = Short.MAX_VALUE; + } + + while (count-- > 0) { + ingredients.add(new ItemStack(ingredient, 1, (short) rawdata)); + } + return this; + } + + /** + * Removes an ingredient from the list. If the ingredient occurs multiple + * times, only one instance of it is removed. Only removes exact matches, + * with a data value of 0. + * + * @param ingredient The ingredient to remove + * @return The changed recipe. + */ + public ShapelessRecipe removeIngredient(Material ingredient) { + return removeIngredient(ingredient, 0); + } + + /** + * Removes an ingredient from the list. If the ingredient occurs multiple + * times, only one instance of it is removed. If the data value is -1, + * only ingredients with a -1 data value will be removed. + * + * @param ingredient The ingredient to remove + * @return The changed recipe. + */ + public ShapelessRecipe removeIngredient(MaterialData ingredient) { + return removeIngredient(ingredient.getItemType(), ingredient.getData()); + } + + /** + * Removes multiple instances of an ingredient from the list. If there are + * less instances then specified, all will be removed. Only removes exact + * matches, with a data value of 0. + * + * @param count The number of copies to remove. + * @param ingredient The ingredient to remove + * @return The changed recipe. + */ + public ShapelessRecipe removeIngredient(int count, Material ingredient) { + return removeIngredient(count, ingredient, 0); + } + + /** + * Removes multiple instances of an ingredient from the list. If there are + * less instances then specified, all will be removed. If the data value + * is -1, only ingredients with a -1 data value will be removed. + * + * @param count The number of copies to remove. + * @param ingredient The ingredient to remove. + * @return The changed recipe. + */ + public ShapelessRecipe removeIngredient(int count, MaterialData ingredient) { + return removeIngredient(count, ingredient.getItemType(), ingredient.getData()); + } + + /** + * Removes an ingredient from the list. If the ingredient occurs multiple + * times, only one instance of it is removed. If the data value is -1, + * only ingredients with a -1 data value will be removed. + * + * @param ingredient The ingredient to remove + * @param rawdata The data value; + * @return The changed recipe. + * @deprecated Magic value + */ + @Deprecated + public ShapelessRecipe removeIngredient(Material ingredient, int rawdata) { + return removeIngredient(1, ingredient, rawdata); + } + + /** + * Removes multiple instances of an ingredient from the list. If there are + * less instances then specified, all will be removed. If the data value + * is -1, only ingredients with a -1 data value will be removed. + * + * @param count The number of copies to remove. + * @param ingredient The ingredient to remove. + * @param rawdata The data value. + * @return The changed recipe. + * @deprecated Magic value + */ + @Deprecated + public ShapelessRecipe removeIngredient(int count, Material ingredient, int rawdata) { + Iterator iterator = ingredients.iterator(); + while (count > 0 && iterator.hasNext()) { + ItemStack stack = iterator.next(); + if (stack.getType() == ingredient && stack.getDurability() == rawdata) { + iterator.remove(); + count--; + } + } + return this; + } + + /** + * Get the result of this recipe. + * + * @return The result stack. + */ + public ItemStack getResult() { + return output.clone(); + } + + /** + * Get the list of ingredients used for this recipe. + * + * @return The input list + */ + public List getIngredientList() { + ArrayList result = new ArrayList(ingredients.size()); + for (ItemStack ingredient : ingredients) { + result.add(ingredient.clone()); + } + return result; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/BookMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/BookMeta.java new file mode 100644 index 0000000..0017596 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/BookMeta.java @@ -0,0 +1,130 @@ +package org.bukkit.inventory.meta; + +import java.util.List; + +import org.bukkit.Material; + +/** + * Represents a book ({@link Material#BOOK_AND_QUILL} or {@link + * Material#WRITTEN_BOOK}) that can have a title, an author, and pages. + */ +public interface BookMeta extends ItemMeta { + + /** + * Checks for the existence of a title in the book. + * + * @return true if the book has a title + */ + boolean hasTitle(); + + /** + * Gets the title of the book. + *

          + * Plugins should check that hasTitle() returns true before calling this + * method. + * + * @return the title of the book + */ + String getTitle(); + + /** + * Sets the title of the book. + *

          + * Limited to 16 characters. Removes title when given null. + * + * @param title the title to set + * @return true if the title was successfully set + */ + boolean setTitle(String title); + + /** + * Checks for the existence of an author in the book. + * + * @return the author of the book + */ + boolean hasAuthor(); + + /** + * Gets the author of the book. + *

          + * Plugins should check that hasAuthor() returns true before calling this + * method. + * + * @return the author of the book + */ + String getAuthor(); + + /** + * Sets the author of the book. Removes author when given null. + * + * @param author the author of the book + */ + void setAuthor(String author); + + /** + * Checks for the existence of pages in the book. + * + * @return true if the book has pages + */ + boolean hasPages(); + + /** + * Gets the specified page in the book. The given page must exist. + * + * @param page the page number to get + * @return the page from the book + */ + String getPage(int page); + + /** + * Sets the specified page in the book. Pages of the book must be + * contiguous. + *

          + * The data can be up to 256 characters in length, additional characters + * are truncated. + * + * @param page the page number to set + * @param data the data to set for that page + */ + void setPage(int page, String data); + + /** + * Gets all the pages in the book. + * + * @return list of all the pages in the book + */ + List getPages(); + + /** + * Clears the existing book pages, and sets the book to use the provided + * pages. Maximum 50 pages with 256 characters per page. + * + * @param pages A list of pages to set the book to use + */ + void setPages(List pages); + + /** + * Clears the existing book pages, and sets the book to use the provided + * pages. Maximum 50 pages with 256 characters per page. + * + * @param pages A list of strings, each being a page + */ + void setPages(String... pages); + + /** + * Adds new pages to the end of the book. Up to a maximum of 50 pages with + * 256 characters per page. + * + * @param pages A list of strings, each being a page + */ + void addPage(String... pages); + + /** + * Gets the number of pages in the book. + * + * @return the number of pages in the book + */ + int getPageCount(); + + BookMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/EnchantmentStorageMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/EnchantmentStorageMeta.java new file mode 100644 index 0000000..fb93d03 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/EnchantmentStorageMeta.java @@ -0,0 +1,79 @@ +package org.bukkit.inventory.meta; + +import java.util.Map; + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; + +/** + * EnchantmentMeta is specific to items that can store enchantments, as + * opposed to being enchanted. {@link Material#ENCHANTED_BOOK} is an example + * of an item with enchantment storage. + */ +public interface EnchantmentStorageMeta extends ItemMeta { + + /** + * Checks for the existence of any stored enchantments. + * + * @return true if an enchantment exists on this meta + */ + boolean hasStoredEnchants(); + + /** + * Checks for storage of the specified enchantment. + * + * @param ench enchantment to check + * @return true if this enchantment is stored in this meta + */ + boolean hasStoredEnchant(Enchantment ench); + + /** + * Checks for the level of the stored enchantment. + * + * @param ench enchantment to check + * @return The level that the specified stored enchantment has, or 0 if + * none + */ + int getStoredEnchantLevel(Enchantment ench); + + /** + * Gets a copy the stored enchantments in this ItemMeta. + * + * @return An immutable copy of the stored enchantments + */ + Map getStoredEnchants(); + + /** + * Stores the specified enchantment in this item meta. + * + * @param ench Enchantment to store + * @param level Level for the enchantment + * @param ignoreLevelRestriction this indicates the enchantment should be + * applied, ignoring the level limit + * @return true if the item meta changed as a result of this call, false + * otherwise + * @throws IllegalArgumentException if enchantment is null + */ + boolean addStoredEnchant(Enchantment ench, int level, boolean ignoreLevelRestriction); + + /** + * Remove the specified stored enchantment from this item meta. + * + * @param ench Enchantment to remove + * @return true if the item meta changed as a result of this call, false + * otherwise + * @throws IllegalArgumentException if enchantment is null + */ + boolean removeStoredEnchant(Enchantment ench) throws IllegalArgumentException; + + /** + * Checks if the specified enchantment conflicts with any enchantments in + * this ItemMeta. + * + * @param ench enchantment to test + * @return true if the enchantment conflicts, false otherwise + */ + boolean hasConflictingStoredEnchant(Enchantment ench); + + EnchantmentStorageMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/FireworkEffectMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/FireworkEffectMeta.java new file mode 100644 index 0000000..47046f1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/FireworkEffectMeta.java @@ -0,0 +1,34 @@ +package org.bukkit.inventory.meta; + +import org.bukkit.FireworkEffect; +import org.bukkit.Material; + +/** + * Represents a meta that can store a single FireworkEffect. An example + * includes {@link Material#FIREWORK_CHARGE}. + */ +public interface FireworkEffectMeta extends ItemMeta { + + /** + * Sets the firework effect for this meta. + * + * @param effect the effect to set, or null to indicate none. + */ + void setEffect(FireworkEffect effect); + + /** + * Checks if this meta has an effect. + * + * @return true if this meta has an effect, false otherwise + */ + boolean hasEffect(); + + /** + * Gets the firework effect for this meta. + * + * @return the current effect, or null if none + */ + FireworkEffect getEffect(); + + FireworkEffectMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/FireworkMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/FireworkMeta.java new file mode 100644 index 0000000..3e06ee0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/FireworkMeta.java @@ -0,0 +1,94 @@ +package org.bukkit.inventory.meta; + +import java.util.List; + +import org.bukkit.FireworkEffect; +import org.bukkit.Material; + +/** + * Represents a {@link Material#FIREWORK} and its effects. + */ +public interface FireworkMeta extends ItemMeta { + + /** + * Add another effect to this firework. + * + * @param effect The firework effect to add + * @throws IllegalArgumentException If effect is null + */ + void addEffect(FireworkEffect effect) throws IllegalArgumentException; + + /** + * Add several effects to this firework. + * + * @param effects The firework effects to add + * @throws IllegalArgumentException If effects is null + * @throws IllegalArgumentException If any effect is null (may be thrown + * after changes have occurred) + */ + void addEffects(FireworkEffect...effects) throws IllegalArgumentException; + + /** + * Add several firework effects to this firework. + * + * @param effects An iterable object whose iterator yields the desired + * firework effects + * @throws IllegalArgumentException If effects is null + * @throws IllegalArgumentException If any effect is null (may be thrown + * after changes have occurred) + */ + void addEffects(Iterable effects) throws IllegalArgumentException; + + /** + * Get the effects in this firework. + * + * @return An immutable list of the firework effects + */ + List getEffects(); + + /** + * Get the number of effects in this firework. + * + * @return The number of effects + */ + int getEffectsSize(); + + /** + * Remove an effect from this firework. + * + * @param index The index of the effect to remove + * @throws IndexOutOfBoundsException If index < 0 or index > {@link + * #getEffectsSize()} + */ + void removeEffect(int index) throws IndexOutOfBoundsException; + + /** + * Remove all effects from this firework. + */ + void clearEffects(); + + /** + * Get whether this firework has any effects. + * + * @return true if it has effects, false if there are no effects + */ + boolean hasEffects(); + + /** + * Gets the approximate height the firework will fly. + * + * @return approximate flight height of the firework. + */ + int getPower(); + + /** + * Sets the approximate power of the firework. Each level of power is half + * a second of flight time. + * + * @param power the power of the firework, from 0-128 + * @throws IllegalArgumentException if height<0 or height>128 + */ + void setPower(int power) throws IllegalArgumentException; + + FireworkMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java new file mode 100644 index 0000000..bfe6845 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java @@ -0,0 +1,189 @@ +package org.bukkit.inventory.meta; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; + +/** + * This type represents the storage mechanism for auxiliary item data. + *

          + * An implementation will handle the creation and application for ItemMeta. + * This class should not be implemented by a plugin in a live environment. + */ +public interface ItemMeta extends Cloneable, ConfigurationSerializable { + + /** + * Checks for existence of a display name. + * + * @return true if this has a display name + */ + boolean hasDisplayName(); + + /** + * Gets the display name that is set. + *

          + * Plugins should check that hasDisplayName() returns true + * before calling this method. + * + * @return the display name that is set + */ + String getDisplayName(); + + /** + * Sets the display name. + * + * @param name the name to set + */ + void setDisplayName(String name); + + /** + * Checks for existence of lore. + * + * @return true if this has lore + */ + boolean hasLore(); + + /** + * Gets the lore that is set. + *

          + * Plugins should check if hasLore() returns true before + * calling this method. + * + * @return a list of lore that is set + */ + List getLore(); + + /** + * Sets the lore for this item. + * Removes lore when given null. + * + * @param lore the lore that will be set + */ + void setLore(List lore); + + /** + * Checks for the existence of any enchantments. + * + * @return true if an enchantment exists on this meta + */ + boolean hasEnchants(); + + /** + * Checks for existence of the specified enchantment. + * + * @param ench enchantment to check + * @return true if this enchantment exists for this meta + */ + boolean hasEnchant(Enchantment ench); + + /** + * Checks for the level of the specified enchantment. + * + * @param ench enchantment to check + * @return The level that the specified enchantment has, or 0 if none + */ + int getEnchantLevel(Enchantment ench); + + /** + * Returns a copy the enchantments in this ItemMeta.
          + * Returns an empty map if none. + * + * @return An immutable copy of the enchantments + */ + Map getEnchants(); + + /** + * Adds the specified enchantment to this item meta. + * + * @param ench Enchantment to add + * @param level Level for the enchantment + * @param ignoreLevelRestriction this indicates the enchantment should be + * applied, ignoring the level limit + * @return true if the item meta changed as a result of this call, false + * otherwise + */ + boolean addEnchant(Enchantment ench, int level, boolean ignoreLevelRestriction); + + /** + * Removes the specified enchantment from this item meta. + * + * @param ench Enchantment to remove + * @return true if the item meta changed as a result of this call, false + * otherwise + */ + boolean removeEnchant(Enchantment ench); + + /** + * Checks if the specified enchantment conflicts with any enchantments in + * this ItemMeta. + * + * @param ench enchantment to test + * @return true if the enchantment conflicts, false otherwise + */ + boolean hasConflictingEnchant(Enchantment ench); + + /** + * Set itemflags which should be ignored when rendering a ItemStack in the Client. This Method does silently ignore double set itemFlags. + * + * @param itemFlags The hideflags which shouldn't be rendered + */ + void addItemFlags(ItemFlag... itemFlags); + + /** + * Remove specific set of itemFlags. This tells the Client it should render it again. This Method does silently ignore double removed itemFlags. + * + * @param itemFlags Hideflags which should be removed + */ + void removeItemFlags(ItemFlag... itemFlags); + + /** + * Get current set itemFlags. The collection returned is unmodifiable. + * + * @return A set of all itemFlags set + */ + Set getItemFlags(); + + /** + * Check if the specified flag is present on this item. + * + * @param flag the flag to check + * @return if it is present + */ + boolean hasItemFlag(ItemFlag flag); + + + // Spigot start + public class Spigot + { + + /** + * Sets the unbreakable tag + * + * @param unbreakable true if set unbreakable + */ + public void setUnbreakable(boolean unbreakable) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Return if the unbreakable tag is true + * + * @return true if the unbreakable tag is true + */ + public boolean isUnbreakable() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); + // Spigot end + + @SuppressWarnings("javadoc") + ItemMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/LeatherArmorMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/LeatherArmorMeta.java new file mode 100644 index 0000000..2dc2420 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/LeatherArmorMeta.java @@ -0,0 +1,31 @@ +package org.bukkit.inventory.meta; + +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.inventory.ItemFactory; + +/** + * Represents leather armor ({@link Material#LEATHER_BOOTS}, {@link + * Material#LEATHER_CHESTPLATE}, {@link Material#LEATHER_HELMET}, or {@link + * Material#LEATHER_LEGGINGS}) that can be colored. + */ +public interface LeatherArmorMeta extends ItemMeta { + + /** + * Gets the color of the armor. If it has not been set otherwise, it will + * be {@link ItemFactory#getDefaultLeatherColor()}. + * + * @return the color of the armor, never null + */ + Color getColor(); + + /** + * Sets the color of the armor. + * + * @param color the color to set. Setting it to null is equivalent to + * setting it to {@link ItemFactory#getDefaultLeatherColor()}. + */ + void setColor(Color color); + + LeatherArmorMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/MapMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/MapMeta.java new file mode 100644 index 0000000..fb5c297 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/MapMeta.java @@ -0,0 +1,23 @@ +package org.bukkit.inventory.meta; + +/** + * Represents a map that can be scalable. + */ +public interface MapMeta extends ItemMeta { + + /** + * Checks to see if this map is scaling. + * + * @return true if this map is scaling + */ + boolean isScaling(); + + /** + * Sets if this map is scaling or not. + * + * @param value true to scale + */ + void setScaling(boolean value); + + MapMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/PotionMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/PotionMeta.java new file mode 100644 index 0000000..8dca983 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/PotionMeta.java @@ -0,0 +1,77 @@ +package org.bukkit.inventory.meta; + +import org.bukkit.Material; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.List; + +/** + * Represents a potion ({@link Material#POTION}) that can have custom effects. + */ +public interface PotionMeta extends ItemMeta { + + /** + * Checks for the presence of custom potion effects. + * + * @return true if custom potion effects are applied + */ + boolean hasCustomEffects(); + + /** + * Gets an immutable list containing all custom potion effects applied to + * this potion. + *

          + * Plugins should check that hasCustomEffects() returns true before + * calling this method. + * + * @return the immutable list of custom potion effects + */ + List getCustomEffects(); + + /** + * Adds a custom potion effect to this potion. + * + * @param effect the potion effect to add + * @param overwrite true if any existing effect of the same type should be + * overwritten + * @return true if the potion meta changed as a result of this call + */ + boolean addCustomEffect(PotionEffect effect, boolean overwrite); + + /** + * Removes a custom potion effect from this potion. + * + * @param type the potion effect type to remove + * @return true if the potion meta changed as a result of this call + */ + boolean removeCustomEffect(PotionEffectType type); + + /** + * Checks for a specific custom potion effect type on this potion. + * + * @param type the potion effect type to check for + * @return true if the potion has this effect + */ + boolean hasCustomEffect(PotionEffectType type); + + /** + * Moves a potion effect to the top of the potion effect list. + *

          + * This causes the client to display the potion effect in the potion's + * name. + * + * @param type the potion effect type to move + * @return true if the potion meta changed as a result of this call + */ + boolean setMainEffect(PotionEffectType type); + + /** + * Removes all custom potion effects from this potion. + * + * @return true if the potion meta changed as a result of this call + */ + boolean clearCustomEffects(); + + PotionMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/Repairable.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/Repairable.java new file mode 100644 index 0000000..c49844e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/Repairable.java @@ -0,0 +1,31 @@ +package org.bukkit.inventory.meta; + +/** + * Represents an item that can be repaired at an anvil. + */ +public interface Repairable { + + /** + * Checks to see if this has a repair penalty + * + * @return true if this has a repair penalty + */ + boolean hasRepairCost(); + + /** + * Gets the repair penalty + * + * @return the repair penalty + */ + int getRepairCost(); + + /** + * Sets the repair penalty + * + * @param cost repair penalty + */ + void setRepairCost(int cost); + + @SuppressWarnings("javadoc") + Repairable clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/inventory/meta/SkullMeta.java b/vspigot-api/src/main/java/org/bukkit/inventory/meta/SkullMeta.java new file mode 100644 index 0000000..fab3119 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/inventory/meta/SkullMeta.java @@ -0,0 +1,36 @@ +package org.bukkit.inventory.meta; + +import org.bukkit.Material; + +/** + * Represents a skull ({@link Material#SKULL_ITEM}) that can have an owner. + */ +public interface SkullMeta extends ItemMeta { + + /** + * Gets the owner of the skull. + * + * @return the owner if the skull + */ + String getOwner(); + + /** + * Checks to see if the skull has an owner. + * + * @return true if the skull has an owner + */ + boolean hasOwner(); + + /** + * Sets the owner of the skull. + *

          + * Plugins should check that hasOwner() returns true before calling this + * plugin. + * + * @param owner the new owner of the skull + * @return true if the owner was successfully set + */ + boolean setOwner(String owner); + + SkullMeta clone(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MapCanvas.java b/vspigot-api/src/main/java/org/bukkit/map/MapCanvas.java new file mode 100644 index 0000000..d68bb17 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MapCanvas.java @@ -0,0 +1,84 @@ +package org.bukkit.map; + +import java.awt.Image; + +/** + * Represents a canvas for drawing to a map. Each canvas is associated with a + * specific {@link MapRenderer} and represents that renderer's layer on the + * map. + */ +public interface MapCanvas { + + /** + * Get the map this canvas is attached to. + * + * @return The MapView this canvas is attached to. + */ + public MapView getMapView(); + + /** + * Get the cursor collection associated with this canvas. + * + * @return The MapCursorCollection associated with this canvas. + */ + public MapCursorCollection getCursors(); + + /** + * Set the cursor collection associated with this canvas. This does not + * usually need to be called since a MapCursorCollection is already + * provided. + * + * @param cursors The MapCursorCollection to associate with this canvas. + */ + public void setCursors(MapCursorCollection cursors); + + /** + * Draw a pixel to the canvas. + * + * @param x The x coordinate, from 0 to 127. + * @param y The y coordinate, from 0 to 127. + * @param color The color. See {@link MapPalette}. + */ + public void setPixel(int x, int y, byte color); + + /** + * Get a pixel from the canvas. + * + * @param x The x coordinate, from 0 to 127. + * @param y The y coordinate, from 0 to 127. + * @return The color. See {@link MapPalette}. + */ + public byte getPixel(int x, int y); + + /** + * Get a pixel from the layers below this canvas. + * + * @param x The x coordinate, from 0 to 127. + * @param y The y coordinate, from 0 to 127. + * @return The color. See {@link MapPalette}. + */ + public byte getBasePixel(int x, int y); + + /** + * Draw an image to the map. The image will be clipped if necessary. + * + * @param x The x coordinate of the image. + * @param y The y coordinate of the image. + * @param image The Image to draw. + */ + public void drawImage(int x, int y, Image image); + + /** + * Render text to the map using fancy formatting. Newline (\n) characters + * will move down one line and return to the original column, and the text + * color can be changed using sequences such as "§12;", replacing 12 with + * the palette index of the color (see {@link MapPalette}). + * + * @param x The column to start rendering on. + * @param y The row to start rendering on. + * @param font The font to use. + * @param text The formatted text to render. + */ + public void drawText(int x, int y, MapFont font, String text); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MapCursor.java b/vspigot-api/src/main/java/org/bukkit/map/MapCursor.java new file mode 100644 index 0000000..d3698a6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MapCursor.java @@ -0,0 +1,189 @@ +package org.bukkit.map; + +/** + * Represents a cursor on a map. + */ +public final class MapCursor { + private byte x, y; + private byte direction, type; + private boolean visible; + + /** + * Initialize the map cursor. + * + * @param x The x coordinate, from -128 to 127. + * @param y The y coordinate, from -128 to 127. + * @param direction The facing of the cursor, from 0 to 15. + * @param type The type (color/style) of the map cursor. + * @param visible Whether the cursor is visible by default. + * @deprecated Magic value + */ + @Deprecated + public MapCursor(byte x, byte y, byte direction, byte type, boolean visible) { + this.x = x; + this.y = y; + setDirection(direction); + setRawType(type); + this.visible = visible; + } + + /** + * Get the X position of this cursor. + * + * @return The X coordinate. + */ + public byte getX() { + return x; + } + + /** + * Get the Y position of this cursor. + * + * @return The Y coordinate. + */ + public byte getY() { + return y; + } + + /** + * Get the direction of this cursor. + * + * @return The facing of the cursor, from 0 to 15. + */ + public byte getDirection() { + return direction; + } + + /** + * Get the type of this cursor. + * + * @return The type (color/style) of the map cursor. + */ + public Type getType() { + return Type.byValue(type); + } + + /** + * Get the type of this cursor. + * + * @return The type (color/style) of the map cursor. + * @deprecated Magic value + */ + @Deprecated + public byte getRawType() { + return type; + } + + /** + * Get the visibility status of this cursor. + * + * @return True if visible, false otherwise. + */ + public boolean isVisible() { + return visible; + } + + /** + * Set the X position of this cursor. + * + * @param x The X coordinate. + */ + public void setX(byte x) { + this.x = x; + } + + /** + * Set the Y position of this cursor. + * + * @param y The Y coordinate. + */ + public void setY(byte y) { + this.y = y; + } + + /** + * Set the direction of this cursor. + * + * @param direction The facing of the cursor, from 0 to 15. + */ + public void setDirection(byte direction) { + if (direction < 0 || direction > 15) { + throw new IllegalArgumentException("Direction must be in the range 0-15"); + } + this.direction = direction; + } + + /** + * Set the type of this cursor. + * + * @param type The type (color/style) of the map cursor. + */ + public void setType(Type type) { + setRawType(type.value); + } + + /** + * Set the type of this cursor. + * + * @param type The type (color/style) of the map cursor. + * @deprecated Magic value + */ + @Deprecated + public void setRawType(byte type) { + if (type < 0 || type > 15) { + throw new IllegalArgumentException("Type must be in the range 0-15"); + } + this.type = type; + } + + /** + * Set the visibility status of this cursor. + * + * @param visible True if visible. + */ + public void setVisible(boolean visible) { + this.visible = visible; + } + + /** + * Represents the standard types of map cursors. More may be made + * available by texture packs - the value is used by the client as an + * index in the file './misc/mapicons.png' from minecraft.jar or from a + * texture pack. + */ + public enum Type { + WHITE_POINTER(0), + GREEN_POINTER(1), + RED_POINTER(2), + BLUE_POINTER(3), + WHITE_CROSS(4); + + private byte value; + + private Type(int value) { + this.value = (byte) value; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public byte getValue() { + return value; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public static Type byValue(byte value) { + for (Type t : values()) { + if (t.value == value) return t; + } + return null; + } + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MapCursorCollection.java b/vspigot-api/src/main/java/org/bukkit/map/MapCursorCollection.java new file mode 100644 index 0000000..1dc9025 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MapCursorCollection.java @@ -0,0 +1,96 @@ +package org.bukkit.map; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents all the map cursors on a {@link MapCanvas}. Like MapCanvas, a + * MapCursorCollection is linked to a specific {@link MapRenderer}. + */ +public final class MapCursorCollection { + private List cursors = new ArrayList(); + + /** + * Get the amount of cursors in this collection. + * + * @return The size of this collection. + */ + public int size() { + return cursors.size(); + } + + /** + * Get a cursor from this collection. + * + * @param index The index of the cursor. + * @return The MapCursor. + */ + public MapCursor getCursor(int index) { + return cursors.get(index); + } + + /** + * Remove a cursor from the collection. + * + * @param cursor The MapCursor to remove. + * @return Whether the cursor was removed successfully. + */ + public boolean removeCursor(MapCursor cursor) { + return cursors.remove(cursor); + } + + /** + * Add a cursor to the collection. + * + * @param cursor The MapCursor to add. + * @return The MapCursor that was passed. + */ + public MapCursor addCursor(MapCursor cursor) { + cursors.add(cursor); + return cursor; + } + + /** + * Add a cursor to the collection. + * + * @param x The x coordinate, from -128 to 127. + * @param y The y coordinate, from -128 to 127. + * @param direction The facing of the cursor, from 0 to 15. + * @return The newly added MapCursor. + */ + public MapCursor addCursor(int x, int y, byte direction) { + return addCursor(x, y, direction, (byte) 0, true); + } + + /** + * Add a cursor to the collection. + * + * @param x The x coordinate, from -128 to 127. + * @param y The y coordinate, from -128 to 127. + * @param direction The facing of the cursor, from 0 to 15. + * @param type The type (color/style) of the map cursor. + * @return The newly added MapCursor. + * @deprecated Magic value + */ + @Deprecated + public MapCursor addCursor(int x, int y, byte direction, byte type) { + return addCursor(x, y, direction, type, true); + } + + /** + * Add a cursor to the collection. + * + * @param x The x coordinate, from -128 to 127. + * @param y The y coordinate, from -128 to 127. + * @param direction The facing of the cursor, from 0 to 15. + * @param type The type (color/style) of the map cursor. + * @param visible Whether the cursor is visible. + * @return The newly added MapCursor. + * @deprecated Magic value + */ + @Deprecated + public MapCursor addCursor(int x, int y, byte direction, byte type, boolean visible) { + return addCursor(new MapCursor((byte) x, (byte) y, direction, type, visible)); + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MapFont.java b/vspigot-api/src/main/java/org/bukkit/map/MapFont.java new file mode 100644 index 0000000..ea8f0ea --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MapFont.java @@ -0,0 +1,144 @@ +package org.bukkit.map; + +import java.util.HashMap; + +/** + * Represents a bitmap font drawable to a map. + */ +public class MapFont { + + private final HashMap chars = new HashMap(); + private int height = 0; + protected boolean malleable = true; + + /** + * Set the sprite for a given character. + * + * @param ch The character to set the sprite for. + * @param sprite The CharacterSprite to set. + * @throws IllegalStateException if this font is static. + */ + public void setChar(char ch, CharacterSprite sprite) { + if (!malleable) { + throw new IllegalStateException("this font is not malleable"); + } + + chars.put(ch, sprite); + if (sprite.getHeight() > height) { + height = sprite.getHeight(); + } + } + + /** + * Get the sprite for a given character. + * + * @param ch The character to get the sprite for. + * @return The CharacterSprite associated with the character, or null if + * there is none. + */ + public CharacterSprite getChar(char ch) { + return chars.get(ch); + } + + /** + * Get the width of the given text as it would be rendered using this + * font. + * + * @param text The text. + * @return The width in pixels. + */ + public int getWidth(String text) { + if (!isValid(text)) { + throw new IllegalArgumentException("text contains invalid characters"); + } + + if (text.length() == 0){ + return 0; + } + + int result = 0; + for (int i = 0; i < text.length(); ++i) { + result += chars.get(text.charAt(i)).getWidth(); + } + result += text.length() - 1; // Account for 1px spacing between characters + + return result; + } + + /** + * Get the height of this font. + * + * @return The height of the font. + */ + public int getHeight() { + return height; + } + + /** + * Check whether the given text is valid. + * + * @param text The text. + * @return True if the string contains only defined characters, false + * otherwise. + */ + public boolean isValid(String text) { + for (int i = 0; i < text.length(); ++i) { + char ch = text.charAt(i); + if (ch == '\u00A7' || ch == '\n') continue; + if (chars.get(ch) == null) return false; + } + return true; + } + + /** + * Represents the graphics for a single character in a MapFont. + */ + public static class CharacterSprite { + + private final int width; + private final int height; + private final boolean[] data; + + public CharacterSprite(int width, int height, boolean[] data) { + this.width = width; + this.height = height; + this.data = data; + + if (data.length != width * height) { + throw new IllegalArgumentException("size of data does not match dimensions"); + } + } + + /** + * Get the value of a pixel of the character. + * + * @param row The row, in the range [0,8). + * @param col The column, in the range [0,8). + * @return True if the pixel is solid, false if transparent. + */ + public boolean get(int row, int col) { + if (row < 0 || col < 0 || row >= height || col >= width) return false; + return data[row * width + col]; + } + + /** + * Get the width of the character sprite. + * + * @return The width of the character. + */ + public int getWidth() { + return width; + } + + /** + * Get the height of the character sprite. + * + * @return The height of the character. + */ + public int getHeight() { + return height; + } + + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MapPalette.java b/vspigot-api/src/main/java/org/bukkit/map/MapPalette.java new file mode 100644 index 0000000..35f3e19 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MapPalette.java @@ -0,0 +1,241 @@ +package org.bukkit.map; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; + +/** + * Represents the palette that map items use. + *

          + * These fields are hee base color ranges. Each entry corresponds to four + * colors of varying shades with values entry to entry + 3. + */ +public final class MapPalette { + // Internal mechanisms + private MapPalette() {} + + private static Color c(int r, int g, int b) { + return new Color(r, g, b); + } + + private static double getDistance(Color c1, Color c2) { + double rmean = (c1.getRed() + c2.getRed()) / 2.0; + double r = c1.getRed() - c2.getRed(); + double g = c1.getGreen() - c2.getGreen(); + int b = c1.getBlue() - c2.getBlue(); + double weightR = 2 + rmean / 256.0; + double weightG = 4.0; + double weightB = 2 + (255 - rmean) / 256.0; + return weightR * r * r + weightG * g * g + weightB * b * b; + } + + private static final Color[] colors = { + new Color(0, 0, 0, 0), new Color(0, 0, 0, 0), + new Color(0, 0, 0, 0), new Color(0, 0, 0, 0), + c(89, 125, 39), c(109, 153, 48), c(127, 178, 56), c(67, 94, 29), + c(174, 164, 115), c(213, 201, 140), c(247, 233, 163), c(130, 123, 86), + c(117, 117, 117), c(144, 144, 144), c(167, 167, 167), c(88, 88, 88), + c(180, 0, 0), c(220, 0, 0), c(255, 0, 0), c(135, 0, 0), + c(112, 112, 180), c(138, 138, 220), c(160, 160, 255), c(84, 84, 135), + c(117, 117, 117), c(144, 144, 144), c(167, 167, 167), c(88, 88, 88), + c(0, 87, 0), c(0, 106, 0), c(0, 124, 0), c(0, 65, 0), + c(180, 180, 180), c(220, 220, 220), c(255, 255, 255), c(135, 135, 135), + c(115, 118, 129), c(141, 144, 158), c(164, 168, 184), c(86, 88, 97), + c(129, 74, 33), c(157, 91, 40), c(183, 106, 47), c(96, 56, 24), + c(79, 79, 79), c(96, 96, 96), c(112, 112, 112), c(59, 59, 59), + c(45, 45, 180), c(55, 55, 220), c(64, 64, 255), c(33, 33, 135), + c(73, 58, 35), c(89, 71, 43), c(104, 83, 50), c(55, 43, 26), + c(180, 177, 172), c(220, 217, 211), c(255, 252, 245), c(135, 133, 129), + c(152, 89, 36), c(186, 109, 44), c(216, 127, 51), c(114, 67, 27), + c(125, 53, 152), c(153, 65, 186), c(178, 76, 216), c(94, 40, 114), + c(72, 108, 152), c(88, 132, 186), c(102, 153, 216), c(54, 81, 114), + c(161, 161, 36), c(197, 197, 44), c(229, 229, 51), c(121, 121, 27), + c(89, 144, 17), c(109, 176, 21), c(127, 204, 25), c(67, 108, 13), + c(170, 89, 116), c(208, 109, 142), c(242, 127, 165), c(128, 67, 87), + c(53, 53, 53), c(65, 65, 65), c(76, 76, 76), c(40, 40, 40), + c(108, 108, 108), c(132, 132, 132), c(153, 153, 153), c(81, 81, 81), + c(53, 89, 108), c(65, 109, 132), c(76, 127, 153), c(40, 67, 81), + c(89, 44, 125), c(109, 54, 153), c(127, 63, 178), c(67, 33, 94), + c(36, 53, 125), c(44, 65, 153), c(51, 76, 178), c(27, 40, 94), + c(72, 53, 36), c(88, 65, 44), c(102, 76, 51), c(54, 40, 27), + c(72, 89, 36), c(88, 109, 44), c(102, 127, 51), c(54, 67, 27), + c(108, 36, 36), c(132, 44, 44), c(153, 51, 51), c(81, 27, 27), + c(17, 17, 17), c(21, 21, 21), c(25, 25, 25), c(13, 13, 13), + c(176, 168, 54), c(215, 205, 66), c(250, 238, 77), c(132, 126, 40), + c(64, 154, 150), c(79, 188, 183), c(92, 219, 213), c(48, 115, 112), + c(52, 90, 180), c(63, 110, 220), c(74, 128, 255), c(39, 67, 135), + c(0, 153, 40), c(0, 187, 50), c(0, 217, 58), c(0, 114, 30), + c(14, 14, 21), c(18, 17, 26), c(21, 20, 31), c(11, 10, 16), + c(79, 1, 0), c(96, 1, 0), c(112, 2, 0), c(59, 1, 0) + }; + + // Interface + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte TRANSPARENT = 0; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte LIGHT_GREEN = 4; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte LIGHT_BROWN = 8; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte GRAY_1 = 12; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte RED = 16; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte PALE_BLUE = 20; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte GRAY_2 = 24; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte DARK_GREEN = 28; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte WHITE = 32; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte LIGHT_GRAY = 36; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte BROWN = 40; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte DARK_GRAY = 44; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte BLUE = 48; + /** + * @deprecated Magic value + */ + @Deprecated + public static final byte DARK_BROWN = 52; + + /** + * Resize an image to 128x128. + * + * @param image The image to resize. + * @return The resized image. + */ + public static BufferedImage resizeImage(Image image) { + BufferedImage result = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = result.createGraphics(); + graphics.drawImage(image, 0, 0, 128, 128, null); + graphics.dispose(); + return result; + } + + /** + * Convert an Image to a byte[] using the palette. + * + * @param image The image to convert. + * @return A byte[] containing the pixels of the image. + * @deprecated Magic value + */ + @Deprecated + public static byte[] imageToBytes(Image image) { + BufferedImage temp = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = temp.createGraphics(); + graphics.drawImage(image, 0, 0, null); + graphics.dispose(); + + int[] pixels = new int[temp.getWidth() * temp.getHeight()]; + temp.getRGB(0, 0, temp.getWidth(), temp.getHeight(), pixels, 0, temp.getWidth()); + + byte[] result = new byte[temp.getWidth() * temp.getHeight()]; + for (int i = 0; i < pixels.length; i++) { + result[i] = matchColor(new Color(pixels[i], true)); + } + return result; + } + + /** + * Get the index of the closest matching color in the palette to the given + * color. + * + * @param r The red component of the color. + * @param b The blue component of the color. + * @param g The green component of the color. + * @return The index in the palette. + * @deprecated Magic value + */ + @Deprecated + public static byte matchColor(int r, int g, int b) { + return matchColor(new Color(r, g, b)); + } + + /** + * Get the index of the closest matching color in the palette to the given + * color. + * + * @param color The Color to match. + * @return The index in the palette. + * @deprecated Magic value + */ + @Deprecated + public static byte matchColor(Color color) { + if (color.getAlpha() < 128) return 0; + + int index = 0; + double best = -1; + + for (int i = 4; i < colors.length; i++) { + double distance = getDistance(color, colors[i]); + if (distance < best || best == -1) { + best = distance; + index = i; + } + } + + // Minecraft has 143 colors, some of which have negative byte representations + return (byte) (index < 128 ? index : -129 + (index - 127)); + } + + /** + * Get the value of the given color in the palette. + * + * @param index The index in the palette. + * @return The Color of the palette entry. + * @deprecated Magic value + */ + @Deprecated + public static Color getColor(byte index) { + if ((index > -113 && index < 0) || index > 127) { + throw new IndexOutOfBoundsException(); + } else { + // Minecraft has 143 colors, some of which have negative byte representations + return colors[index >= 0 ? index : index + 256]; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MapRenderer.java b/vspigot-api/src/main/java/org/bukkit/map/MapRenderer.java new file mode 100644 index 0000000..322d0ce --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MapRenderer.java @@ -0,0 +1,56 @@ +package org.bukkit.map; + +import org.bukkit.entity.Player; + +/** + * Represents a renderer for a map. + */ +public abstract class MapRenderer { + + private boolean contextual; + + /** + * Initialize the map renderer base to be non-contextual. See {@link + * #isContextual()}. + */ + public MapRenderer() { + this(false); + } + + /** + * Initialize the map renderer base with the given contextual status. + * + * @param contextual Whether the renderer is contextual. See {@link + * #isContextual()}. + */ + public MapRenderer(boolean contextual) { + this.contextual = contextual; + } + + /** + * Get whether the renderer is contextual, i.e. has different canvases for + * different players. + * + * @return True if contextual, false otherwise. + */ + final public boolean isContextual() { + return contextual; + } + + /** + * Initialize this MapRenderer for the given map. + * + * @param map The MapView being initialized. + */ + public void initialize(MapView map) {} + + /** + * Render to the given map. + * + * @param map The MapView being rendered to. + * @param canvas The canvas to use for rendering. + * @param player The player who triggered the rendering. + */ + abstract public void render(MapView map, MapCanvas canvas, Player player); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MapView.java b/vspigot-api/src/main/java/org/bukkit/map/MapView.java new file mode 100644 index 0000000..ff370f4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MapView.java @@ -0,0 +1,157 @@ +package org.bukkit.map; + +import java.util.List; +import org.bukkit.World; + +/** + * Represents a map item. + */ +public interface MapView { + + /** + * An enum representing all possible scales a map can be set to. + */ + public static enum Scale { + CLOSEST(0), + CLOSE(1), + NORMAL(2), + FAR(3), + FARTHEST(4); + + private byte value; + + private Scale(int value) { + this.value = (byte) value; + } + + /** + * Get the scale given the raw value. + * + * @param value The raw scale + * @return The enum scale, or null for an invalid input + * @deprecated Magic value + */ + @Deprecated + public static Scale valueOf(byte value) { + switch (value) { + case 0: return CLOSEST; + case 1: return CLOSE; + case 2: return NORMAL; + case 3: return FAR; + case 4: return FARTHEST; + default: return null; + } + } + + /** + * Get the raw value of this scale level. + * + * @return The scale value + * @deprecated Magic value + */ + @Deprecated + public byte getValue() { + return value; + } + } + + /** + * Get the ID of this map item. Corresponds to the damage value of a map + * in an inventory. + * + * @return The ID of the map. + * @deprecated Magic value + */ + @Deprecated + public short getId(); + + /** + * Check whether this map is virtual. A map is virtual if its lowermost + * MapRenderer is plugin-provided. + * + * @return Whether the map is virtual. + */ + public boolean isVirtual(); + + /** + * Get the scale of this map. + * + * @return The scale of the map. + */ + public Scale getScale(); + + /** + * Set the scale of this map. + * + * @param scale The scale to set. + */ + public void setScale(Scale scale); + + /** + * Get the center X position of this map. + * + * @return The center X position. + */ + public int getCenterX(); + + /** + * Get the center Z position of this map. + * + * @return The center Z position. + */ + public int getCenterZ(); + + /** + * Set the center X position of this map. + * + * @param x The center X position. + */ + public void setCenterX(int x); + + /** + * Set the center Z position of this map. + * + * @param z The center Z position. + */ + public void setCenterZ(int z); + + /** + * Get the world that this map is associated with. Primarily used by the + * internal renderer, but may be used by external renderers. May return + * null if the world the map is associated with is not loaded. + * + * @return The World this map is associated with. + */ + public World getWorld(); + + /** + * Set the world that this map is associated with. The world is used by + * the internal renderer, and may also be used by external renderers. + * + * @param world The World to associate this map with. + */ + public void setWorld(World world); + + /** + * Get a list of MapRenderers currently in effect. + * + * @return A List containing each map renderer. + */ + public List getRenderers(); + + /** + * Add a renderer to this map. + * + * @param renderer The MapRenderer to add. + */ + public void addRenderer(MapRenderer renderer); + + /** + * Remove a renderer from this map. + * + * @param renderer The MapRenderer to remove. + * @return True if the renderer was successfully removed. + */ + public boolean removeRenderer(MapRenderer renderer); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/map/MinecraftFont.java b/vspigot-api/src/main/java/org/bukkit/map/MinecraftFont.java new file mode 100644 index 0000000..9ec8d10 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/map/MinecraftFont.java @@ -0,0 +1,328 @@ +package org.bukkit.map; + +/** + * Represents the built-in Minecraft font. + */ +public class MinecraftFont extends MapFont { + + private static final int spaceSize = 2; + + private static final String fontChars = + " !\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "'abcdefghijklmnopqrstuvwxyz{|}~\u007F" + + "\u00C7\u00FC\u00E9\u00E2\u00E4\u00E0\u00E5\u00E7" + // Çüéâäàåç + "\u00EA\u00EB\u00E8\u00EF\u00EE\u00EC\u00C4\u00C5" + // êëèïîìÄÅ + "\u00C9\u00E6\u00C6\u00F4\u00F6\u00F2\u00FB\u00F9" + // ÉæÆôöòûù + "\u00FF\u00D6\u00DC\u00F8\u00A3\u00D8\u00D7\u0191" + // ÿÖÜø£Ø׃ + "\u00E1\u00ED\u00F3\u00FA\u00F1\u00D1\u00AA\u00BA" + // áíóúñѪº + "\u00BF\u00AE\u00AC\u00BD\u00BC\u00A1\u00AB\u00BB"; // ¿®¬½¼¡«» + + private static final int[][] fontData = new int[][] { + /* null */ {0,0,0,0,0,0,0,0}, + /* 1 */ {126,129,165,129,189,153,129,126}, + /* 2 */ {126,255,219,255,195,231,255,126}, + /* 3 */ {54,127,127,127,62,28,8,0}, + /* 4 */ {8,28,62,127,62,28,8,0}, + /* 5 */ {28,62,28,127,127,62,28,62}, + /* 6 */ {8,8,28,62,127,62,28,62}, + /* 7 */ {0,0,24,60,60,24,0,0}, + /* 8 */ {255,255,231,195,195,231,255,255}, + /* 9 */ {0,60,102,66,66,102,60,0}, + /* 10 */ {255,195,153,189,189,153,195,255}, + /* 11 */ {240,224,240,190,51,51,51,30}, + /* 12 */ {60,102,102,102,60,24,126,24}, + /* 13 */ {252,204,252,12,12,14,15,7}, + /* 14 */ {254,198,254,198,198,230,103,3}, + /* 15 */ {153,90,60,231,231,60,90,153}, + /* 16 */ {1,7,31,127,31,7,1,0}, + /* 17 */ {64,112,124,127,124,112,64,0}, + /* 18 */ {24,60,126,24,24,126,60,24}, + /* 19 */ {102,102,102,102,102,0,102,0}, + /* 20 */ {254,219,219,222,216,216,216,0}, + /* 21 */ {124,198,28,54,54,28,51,30}, + /* 22 */ {0,0,0,0,126,126,126,0}, + /* 23 */ {24,60,126,24,126,60,24,255}, + /* 24 */ {24,60,126,24,24,24,24,0}, + /* 25 */ {24,24,24,24,126,60,24,0}, + /* 26 */ {0,24,48,127,48,24,0,0}, + /* 27 */ {0,12,6,127,6,12,0,0}, + /* 28 */ {0,0,3,3,3,127,0,0}, + /* 29 */ {0,36,102,255,102,36,0,0}, + /* 30 */ {0,24,60,126,255,255,0,0}, + /* 31 */ {0,255,255,126,60,24,0,0}, + /* */ {0,0,0,0,0,0,0,0}, + /* ! */ {1,1,1,1,1,0,1,0}, + /* " */ {10,10,5,0,0,0,0,0}, + /* # */ {10,10,31,10,31,10,10,0}, + /* $ */ {4,30,1,14,16,15,4,0}, + /* % */ {17,9,8,4,2,18,17,0}, + /* & */ {4,10,4,22,13,9,22,0}, + /* ' */ {2,2,1,0,0,0,0,0}, + /* ( */ {12,2,1,1,1,2,12,0}, + /* ) */ {3,4,8,8,8,4,3,0}, + /* * */ {0,0,9,6,9,0,0,0}, + /* + */ {0,4,4,31,4,4,0,0}, + /* , */ {0,0,0,0,0,1,1,1}, + /* - */ {0,0,0,31,0,0,0,0}, + /* . */ {0,0,0,0,0,1,1,0}, + /* / */ {16,8,8,4,2,2,1,0}, + /* 0 */ {14,17,25,21,19,17,14,0}, + /* 1 */ {4,6,4,4,4,4,31,0}, + /* 2 */ {14,17,16,12,2,17,31,0}, + /* 3 */ {14,17,16,12,16,17,14,0}, + /* 4 */ {24,20,18,17,31,16,16,0}, + /* 5 */ {31,1,15,16,16,17,14,0}, + /* 6 */ {12,2,1,15,17,17,14,0}, + /* 7 */ {31,17,16,8,4,4,4,0}, + /* 8 */ {14,17,17,14,17,17,14,0}, + /* 9 */ {14,17,17,30,16,8,6,0}, + /* : */ {0,1,1,0,0,1,1,0}, + /* ; */ {0,1,1,0,0,1,1,1}, + /* < */ {8,4,2,1,2,4,8,0}, + /* = */ {0,0,31,0,0,31,0,0}, + /* > */ {1,2,4,8,4,2,1,0}, + /* ? */ {14,17,16,8,4,0,4,0}, + /* @ */ {30,33,45,45,61,1,30,0}, + /* A */ {14,17,31,17,17,17,17,0}, + /* B */ {15,17,15,17,17,17,15,0}, + /* C */ {14,17,1,1,1,17,14,0}, + /* D */ {15,17,17,17,17,17,15,0}, + /* E */ {31,1,7,1,1,1,31,0}, + /* F */ {31,1,7,1,1,1,1,0}, + /* G */ {30,1,25,17,17,17,14,0}, + /* H */ {17,17,31,17,17,17,17,0}, + /* I */ {7,2,2,2,2,2,7,0}, + /* J */ {16,16,16,16,16,17,14,0}, + /* K */ {17,9,7,9,17,17,17,0}, + /* L */ {1,1,1,1,1,1,31,0}, + /* M */ {17,27,21,17,17,17,17,0}, + /* N */ {17,19,21,25,17,17,17,0}, + /* O */ {14,17,17,17,17,17,14,0}, + /* P */ {15,17,15,1,1,1,1,0}, + /* Q */ {14,17,17,17,17,9,22,0}, + /* R */ {15,17,15,17,17,17,17,0}, + /* S */ {30,1,14,16,16,17,14,0}, + /* T */ {31,4,4,4,4,4,4,0}, + /* U */ {17,17,17,17,17,17,14,0}, + /* V */ {17,17,17,17,10,10,4,0}, + /* W */ {17,17,17,17,21,27,17,0}, + /* X */ {17,10,4,10,17,17,17,0}, + /* Y */ {17,10,4,4,4,4,4,0}, + /* Z */ {31,16,8,4,2,1,31,0}, + /* [ */ {7,1,1,1,1,1,7,0}, + /* \ */ {1,2,2,4,8,8,16,0}, + /* ] */ {7,4,4,4,4,4,7,0}, + /* ^ */ {4,10,17,0,0,0,0,0}, + /* _ */ {0,0,0,0,0,0,0,31}, + /* ` */ {1,1,2,0,0,0,0,0}, + /* a */ {0,0,14,16,30,17,30,0}, + /* b */ {1,1,13,19,17,17,15,0}, + /* c */ {0,0,14,17,1,17,14,0}, + /* d */ {16,16,22,25,17,17,30,0}, + /* e */ {0,0,14,17,31,1,30,0}, + /* f */ {12,2,15,2,2,2,2,0}, + /* g */ {0,0,30,17,17,30,16,15}, + /* h */ {1,1,13,19,17,17,17,0}, + /* i */ {1,0,1,1,1,1,1,0}, + /* j */ {16,0,16,16,16,17,17,14}, + /* k */ {1,1,9,5,3,5,9,0}, + /* l */ {1,1,1,1,1,1,2,0}, + /* m */ {0,0,11,21,21,17,17,0}, + /* n */ {0,0,15,17,17,17,17,0}, + /* o */ {0,0,14,17,17,17,14,0}, + /* p */ {0,0,13,19,17,15,1,1}, + /* q */ {0,0,22,25,17,30,16,16}, + /* r */ {0,0,13,19,1,1,1,0}, + /* s */ {0,0,30,1,14,16,15,0}, + /* t */ {2,2,7,2,2,2,4,0}, + /* u */ {0,0,17,17,17,17,30,0}, + /* v */ {0,0,17,17,17,10,4,0}, + /* w */ {0,0,17,17,21,21,30,0}, + /* x */ {0,0,17,10,4,10,17,0}, + /* y */ {0,0,17,17,17,30,16,15}, + /* z */ {0,0,31,8,4,2,31,0}, + /* { */ {12,2,2,1,2,2,12,0}, + /* | */ {1,1,1,0,1,1,1,0}, + /* } */ {3,4,4,8,4,4,3,0}, + /* ~ */ {38,25,0,0,0,0,0,0}, + /* ⌂ */ {0,0,4,10,17,17,31,0}, + /* Ç */ {14,17,1,1,17,14,16,12}, + /* ü */ {10,0,17,17,17,17,30,0}, + /* é */ {24,0,14,17,31,1,30,0}, + /* â */ {14,17,14,16,30,17,30,0}, + /* ä */ {10,0,14,16,30,17,30,0}, + /* à */ {3,0,14,16,30,17,30,0}, + /* å */ {4,0,14,16,30,17,30,0}, + /* ç */ {0,14,17,1,17,14,16,12}, + /* ê */ {14,17,14,17,31,1,30,0}, + /* ë */ {10,0,14,17,31,1,30,0}, + /* è */ {3,0,14,17,31,1,30,0}, + /* ï */ {5,0,2,2,2,2,2,0}, + /* î */ {14,17,4,4,4,4,4,0}, + /* ì */ {3,0,2,2,2,2,2,0}, + /* Ä */ {17,14,17,31,17,17,17,0}, + /* Å */ {4,0,14,17,31,17,17,0}, + /* É */ {24,0,31,1,7,1,31,0}, + /* æ */ {0,0,10,20,30,5,30,0}, + /* Æ */ {30,5,15,5,5,5,29,0}, + /* ô */ {14,17,14,17,17,17,14,0}, + /* ö */ {10,0,14,17,17,17,14,0}, + /* ò */ {3,0,14,17,17,17,14,0}, + /* û */ {14,17,0,17,17,17,30,0}, + /* ù */ {3,0,17,17,17,17,30,0}, + /* ÿ */ {10,0,17,17,17,30,16,15}, + /* Ö */ {17,14,17,17,17,17,14,0}, + /* Ü */ {17,0,17,17,17,17,14,0}, + /* ø */ {0,0,14,25,21,19,14,4}, + /* £ */ {12,18,2,15,2,2,31,0}, + /* Ø */ {14,17,25,21,19,17,14,0}, + /* × */ {0,0,5,2,5,0,0,0}, + /* ƒ */ {8,20,4,14,4,4,5,2}, + /* á */ {24,0,14,16,30,17,30,0}, + /* í */ {3,0,1,1,1,1,1,0}, + /* ó */ {24,0,14,17,17,17,14,0}, + /* ú */ {24,0,17,17,17,17,30,0}, + /* ñ */ {31,0,15,17,17,17,17,0}, + /* Ñ */ {31,0,17,19,21,25,17,0}, + /* ª */ {14,16,31,30,0,31,0,0}, + /* º */ {14,17,17,14,0,31,0,0}, + /* ¿ */ {4,0,4,2,1,17,14,0}, + /* ® */ {0,30,45,37,43,30,0,0}, + /* ¬ */ {0,0,0,31,16,16,0,0}, + /* ½ */ {17,9,8,4,18,10,25,0}, + /* ¼ */ {17,9,8,4,26,26,17,0}, + /* ¡ */ {0,1,0,1,1,1,1,0}, + /* « */ {0,20,10,5,10,20,0,0}, + /* » */ {0,5,10,20,10,5,0,0}, + /* 176 */ {68,17,68,17,68,17,68,17}, + /* 177 */ {170,85,170,85,170,85,170,85}, + /* 178 */ {219,238,219,119,219,238,219,119}, + /* 179 */ {24,24,24,24,24,24,24,24}, + /* 180 */ {24,24,24,24,31,24,24,24}, + /* 181 */ {24,24,31,24,31,24,24,24}, + /* 182 */ {108,108,108,108,111,108,108,108}, + /* 183 */ {0,0,0,0,127,108,108,108}, + /* 184 */ {0,0,31,24,31,24,24,24}, + /* 185 */ {108,108,111,96,111,108,108,108}, + /* 186 */ {108,108,108,108,108,108,108,108}, + /* 187 */ {0,0,127,96,111,108,108,108}, + /* 188 */ {108,108,111,96,127,0,0,0}, + /* 189 */ {108,108,108,108,127,0,0,0}, + /* 190 */ {24,24,31,24,31,0,0,0}, + /* 191 */ {0,0,0,0,31,24,24,24}, + /* 192 */ {24,24,24,24,248,0,0,0}, + /* 193 */ {24,24,24,24,255,0,0,0}, + /* 194 */ {0,0,0,0,255,24,24,24}, + /* 195 */ {24,24,24,24,248,24,24,24}, + /* 196 */ {0,0,0,0,255,0,0,0}, + /* 197 */ {24,24,24,24,255,24,24,24}, + /* 198 */ {24,24,248,24,248,24,24,24}, + /* 199 */ {108,108,108,108,236,108,108,108}, + /* 200 */ {108,108,236,12,252,0,0,0}, + /* 201 */ {0,0,252,12,236,108,108,108}, + /* 202 */ {108,108,239,0,255,0,0,0}, + /* 203 */ {0,0,255,0,239,108,108,108}, + /* 204 */ {108,108,236,12,236,108,108,108}, + /* 205 */ {0,0,255,0,255,0,0,0}, + /* 206 */ {108,108,239,0,239,108,108,108}, + /* 207 */ {24,24,255,0,255,0,0,0}, + /* 208 */ {108,108,108,108,255,0,0,0}, + /* 209 */ {0,0,255,0,255,24,24,24}, + /* 210 */ {0,0,0,0,255,108,108,108}, + /* 211 */ {108,108,108,108,252,0,0,0}, + /* 212 */ {24,24,248,24,248,0,0,0}, + /* 213 */ {0,0,248,24,248,24,24,24}, + /* 214 */ {0,0,0,0,252,108,108,108}, + /* 215 */ {108,108,108,108,255,108,108,108}, + /* 216 */ {24,24,255,24,255,24,24,24}, + /* 217 */ {24,24,24,24,31,0,0,0}, + /* 218 */ {0,0,0,0,248,24,24,24}, + /* 219 */ {255,255,255,255,255,255,255,255}, + /* 220 */ {0,0,0,0,255,255,255,255}, + /* 221 */ {15,15,15,15,15,15,15,15}, + /* 222 */ {240,240,240,240,240,240,240,240}, + /* 223 */ {255,255,255,255,0,0,0,0}, + /* 224 */ {0,0,110,59,19,59,110,0}, + /* 225 */ {0,30,51,31,51,31,3,3}, + /* 226 */ {0,63,51,3,3,3,3,0}, + /* 227 */ {0,127,54,54,54,54,54,0}, + /* 228 */ {63,51,6,12,6,51,63,0}, + /* 229 */ {0,0,126,27,27,27,14,0}, + /* 230 */ {0,102,102,102,102,62,6,3}, + /* 231 */ {0,110,59,24,24,24,24,0}, + /* 232 */ {63,12,30,51,51,30,12,63}, + /* 233 */ {28,54,99,127,99,54,28,0}, + /* 234 */ {28,54,99,99,54,54,119,0}, + /* 235 */ {56,12,24,62,51,51,30,0}, + /* 236 */ {0,0,126,219,219,126,0,0}, + /* 237 */ {96,48,126,219,219,126,6,3}, + /* 238 */ {28,6,3,31,3,6,28,0}, + /* 239 */ {30,51,51,51,51,51,51,0}, + /* 240 */ {0,63,0,63,0,63,0,0}, + /* 241 */ {12,12,63,12,12,0,63,0}, + /* 242 */ {6,12,24,12,6,0,63,0}, + /* 243 */ {24,12,6,12,24,0,63,0}, + /* 244 */ {112,216,216,24,24,24,24,24}, + /* 245 */ {24,24,24,24,24,27,27,14}, + /* 246 */ {12,12,0,63,0,12,12,0}, + /* 247 */ {0,110,59,0,110,59,0,0}, + /* 248 */ {28,54,54,28,0,0,0,0}, + /* 249 */ {0,0,0,24,24,0,0,0}, + /* 250 */ {0,0,0,0,24,0,0,0}, + /* 251 */ {240,48,48,48,55,54,60,56}, + /* 252 */ {30,54,54,54,54,0,0,0}, + /* 253 */ {14,24,12,6,30,0,0,0}, + /* 254 */ {0,0,60,60,60,60,0,0}, + /* 255 */ {0,0,0,0,0,0,0,0}, + }; + + /** + * A static non-malleable MinecraftFont. + */ + public static final MinecraftFont Font = new MinecraftFont(false); + + /** + * Initialize a new MinecraftFont. + */ + public MinecraftFont() { + this(true); + } + + private MinecraftFont(boolean malleable) { + for (int i = 1; i < fontData.length; ++i) { + char ch = (char) i; + if (i >= 32 && i < 32 + fontChars.length()) { + ch = fontChars.charAt(i - 32); + } + + if (ch == ' ') { + setChar(ch, new CharacterSprite(spaceSize, 8, new boolean[spaceSize * 8])); + continue; + } + + int[] rows = fontData[i]; + int width = 0; + for (int r = 0; r < 8; ++r) { + for (int c = 0; c < 8; ++c) { + if ((rows[r] & (1 << c)) != 0 && c > width) { + width = c; + } + } + } + ++width; + + boolean[] data = new boolean[width * 8]; + for (int r = 0; r < 8; ++r) { + for (int c = 0; c < width; ++c) { + data[r * width + c] = (rows[r] & (1 << c)) != 0; + } + } + + setChar(ch, new CharacterSprite(width, 8, data)); + } + + this.malleable = malleable; + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Attachable.java b/vspigot-api/src/main/java/org/bukkit/material/Attachable.java new file mode 100644 index 0000000..1d3f107 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Attachable.java @@ -0,0 +1,16 @@ +package org.bukkit.material; + +import org.bukkit.block.BlockFace; + +/** + * Indicates that a block can be attached to another block + */ +public interface Attachable extends Directional { + + /** + * Gets the face that this block is attached on + * + * @return BlockFace attached to + */ + public BlockFace getAttachedFace(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Bed.java b/vspigot-api/src/main/java/org/bukkit/material/Bed.java new file mode 100644 index 0000000..a1c087a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Bed.java @@ -0,0 +1,142 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a bed. + */ +public class Bed extends MaterialData implements Directional { + + /** + * Default constructor for a bed. + */ + public Bed() { + super(Material.BED_BLOCK); + } + + /** + * Instantiate a bed facing in a particular direction. + * + * @param direction the direction the bed's head is facing + */ + public Bed(BlockFace direction) { + this(); + setFacingDirection(direction); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Bed(final int type) { + super(type); + } + + public Bed(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Bed(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Bed(final Material type, final byte data) { + super(type, data); + } + + /** + * Determine if this block represents the head of the bed + * + * @return true if this is the head of the bed, false if it is the foot + */ + public boolean isHeadOfBed() { + return (getData() & 0x8) == 0x8; + } + + /** + * Configure this to be either the head or the foot of the bed + * + * @param isHeadOfBed True to make it the head. + */ + public void setHeadOfBed(boolean isHeadOfBed) { + setData((byte) (isHeadOfBed ? (getData() | 0x8) : (getData() & ~0x8))); + } + + /** + * Set which direction the head of the bed is facing. Note that this will + * only affect one of the two blocks the bed is made of. + */ + public void setFacingDirection(BlockFace face) { + byte data; + + switch (face) { + case SOUTH: + data = 0x0; + break; + + case WEST: + data = 0x1; + break; + + case NORTH: + data = 0x2; + break; + + case EAST: + default: + data = 0x3; + } + + if (isHeadOfBed()) { + data |= 0x8; + } + + setData(data); + } + + /** + * Get the direction that this bed's head is facing toward + * + * @return the direction the head of the bed is facing + */ + public BlockFace getFacing() { + byte data = (byte) (getData() & 0x7); + + switch (data) { + case 0x0: + return BlockFace.SOUTH; + + case 0x1: + return BlockFace.WEST; + + case 0x2: + return BlockFace.NORTH; + + case 0x3: + default: + return BlockFace.EAST; + } + } + + @Override + public String toString() { + return (isHeadOfBed() ? "HEAD" : "FOOT") + " of " + super.toString() + " facing " + getFacing(); + } + + @Override + public Bed clone() { + return (Bed) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Button.java b/vspigot-api/src/main/java/org/bukkit/material/Button.java new file mode 100644 index 0000000..2eeeaa6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Button.java @@ -0,0 +1,126 @@ +package org.bukkit.material; + +import org.bukkit.block.BlockFace; +import org.bukkit.Material; + +/** + * Represents a button + */ +public class Button extends SimpleAttachableMaterialData implements Redstone { + public Button() { + super(Material.STONE_BUTTON); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Button(final int type) { + super(type); + } + + public Button(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Button(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Button(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current state of this Material, indicating if it's powered or + * unpowered + * + * @return true if powered, otherwise false + */ + public boolean isPowered() { + return (getData() & 0x8) == 0x8; + } + + /** + * Sets the current state of this button + * + * @param bool + * whether or not the button is powered + */ + public void setPowered(boolean bool) { + setData((byte) (bool ? (getData() | 0x8) : (getData() & ~0x8))); + } + + /** + * Gets the face that this block is attached on + * + * @return BlockFace attached to + */ + public BlockFace getAttachedFace() { + byte data = (byte) (getData() & 0x7); + + switch (data) { + case 0x1: + return BlockFace.WEST; + + case 0x2: + return BlockFace.EAST; + + case 0x3: + return BlockFace.NORTH; + + case 0x4: + return BlockFace.SOUTH; + } + + return null; + } + + /** + * Sets the direction this button is pointing toward + */ + public void setFacingDirection(BlockFace face) { + byte data = (byte) (getData() & 0x8); + + switch (face) { + case EAST: + data |= 0x1; + break; + + case WEST: + data |= 0x2; + break; + + case SOUTH: + data |= 0x3; + break; + + case NORTH: + data |= 0x4; + break; + } + + setData(data); + } + + @Override + public String toString() { + return super.toString() + " " + (isPowered() ? "" : "NOT ") + "POWERED"; + } + + @Override + public Button clone() { + return (Button) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Cake.java b/vspigot-api/src/main/java/org/bukkit/material/Cake.java new file mode 100644 index 0000000..360ae58 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Cake.java @@ -0,0 +1,91 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +public class Cake extends MaterialData { + public Cake() { + super(Material.CAKE_BLOCK); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Cake(int type) { + super(type); + } + + public Cake(Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Cake(int type, byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Cake(Material type, byte data) { + super(type, data); + } + + /** + * Gets the number of slices eaten from this cake + * + * @return The number of slices eaten + */ + public int getSlicesEaten() { + return getData(); + } + + /** + * Gets the number of slices remaining on this cake + * + * @return The number of slices remaining + */ + public int getSlicesRemaining() { + return 6 - getData(); + } + + /** + * Sets the number of slices eaten from this cake + * + * @param n The number of slices eaten + */ + public void setSlicesEaten(int n) { + if (n < 6) { + setData((byte) n); + } // TODO: else destroy the block? Probably not possible though + } + + /** + * Sets the number of slices remaining on this cake + * + * @param n The number of slices remaining + */ + public void setSlicesRemaining(int n) { + if (n > 6) { + n = 6; + } + setData((byte) (6 - n)); + } + + @Override + public String toString() { + return super.toString() + " " + getSlicesEaten() + "/" + getSlicesRemaining() + " slices eaten/remaining"; + } + + @Override + public Cake clone() { + return (Cake) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Cauldron.java b/vspigot-api/src/main/java/org/bukkit/material/Cauldron.java new file mode 100644 index 0000000..b464bbd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Cauldron.java @@ -0,0 +1,61 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents a cauldron + */ +public class Cauldron extends MaterialData { + private static final int CAULDRON_FULL = 3; + private static final int CAULDRON_EMPTY = 0; + + public Cauldron() { + super(Material.CAULDRON); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Cauldron(int type, byte data){ + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Cauldron(byte data) { + super(Material.CAULDRON, data); + } + + /** + * Check if the cauldron is full. + * + * @return True if it is full. + */ + public boolean isFull() { + return getData() >= CAULDRON_FULL; + } + + /** + * Check if the cauldron is empty. + * + * @return True if it is empty. + */ + public boolean isEmpty() { + return getData() <= CAULDRON_EMPTY; + } + + @Override + public String toString() { + return (isEmpty() ? "EMPTY" : (isFull() ? "FULL" : getData() + "/3 FULL")) + " CAULDRON"; + } + + @Override + public Cauldron clone() { + return (Cauldron) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Chest.java b/vspigot-api/src/main/java/org/bukkit/material/Chest.java new file mode 100644 index 0000000..b9f6988 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Chest.java @@ -0,0 +1,60 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a chest + */ +public class Chest extends DirectionalContainer { + + public Chest() { + super(Material.CHEST); + } + + /** + * Instantiate a chest facing in a particular direction. + * + * @param direction the direction the chest's lit opens towards + */ + public Chest(BlockFace direction) { + this(); + setFacingDirection(direction); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Chest(final int type) { + super(type); + } + + public Chest(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Chest(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Chest(final Material type, final byte data) { + super(type, data); + } + + @Override + public Chest clone() { + return (Chest) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Coal.java b/vspigot-api/src/main/java/org/bukkit/material/Coal.java new file mode 100644 index 0000000..3a4f7c3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Coal.java @@ -0,0 +1,77 @@ +package org.bukkit.material; + +import org.bukkit.CoalType; +import org.bukkit.Material; + +/** + * Represents the different types of coals. + */ +public class Coal extends MaterialData { + public Coal() { + super(Material.COAL); + } + + public Coal(CoalType type) { + this(); + setType(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Coal(final int type) { + super(type); + } + + public Coal(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Coal(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Coal(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current type of this coal + * + * @return CoalType of this coal + */ + public CoalType getType() { + return CoalType.getByData(getData()); + } + + /** + * Sets the type of this coal + * + * @param type New type of this coal + */ + public void setType(CoalType type) { + setData(type.getData()); + } + + @Override + public String toString() { + return getType() + " " + super.toString(); + } + + @Override + public Coal clone() { + return (Coal) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/CocoaPlant.java b/vspigot-api/src/main/java/org/bukkit/material/CocoaPlant.java new file mode 100644 index 0000000..b8280b5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/CocoaPlant.java @@ -0,0 +1,132 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents the cocoa plant + */ +public class CocoaPlant extends MaterialData implements Directional, Attachable { + + public enum CocoaPlantSize { + SMALL, + MEDIUM, + LARGE + } + + public CocoaPlant() { + super(Material.COCOA); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public CocoaPlant(final int type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public CocoaPlant(final int type, final byte data) { + super(type, data); + } + + public CocoaPlant(CocoaPlantSize sz) { + this(); + setSize(sz); + } + + public CocoaPlant(CocoaPlantSize sz, BlockFace dir) { + this(); + setSize(sz); + setFacingDirection(dir); + } + + /** + * Get size of plant + * + * @return size + */ + public CocoaPlantSize getSize() { + switch (getData() & 0xC) { + case 0: + return CocoaPlantSize.SMALL; + case 4: + return CocoaPlantSize.MEDIUM; + default: + return CocoaPlantSize.LARGE; + } + } + + /** + * Set size of plant + * + * @param sz - size of plant + */ + public void setSize(CocoaPlantSize sz) { + int dat = getData() & 0x3; + switch (sz) { + case SMALL: + break; + case MEDIUM: + dat |= 0x4; + break; + case LARGE: + dat |= 0x8; + break; + } + setData((byte) dat); + } + + public BlockFace getAttachedFace() { + return getFacing().getOppositeFace(); + } + + public void setFacingDirection(BlockFace face) { + int dat = getData() & 0xC; + switch (face) { + default: + case SOUTH: + break; + case WEST: + dat |= 0x1; + break; + case NORTH: + dat |= 0x2; + break; + case EAST: + dat |= 0x3; + break; + } + setData((byte) dat); + } + + public BlockFace getFacing() { + switch (getData() & 0x3) { + case 0: + return BlockFace.SOUTH; + case 1: + return BlockFace.WEST; + case 2: + return BlockFace.NORTH; + case 3: + return BlockFace.EAST; + } + return null; + } + + @Override + public CocoaPlant clone() { + return (CocoaPlant) super.clone(); + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing() + " " + getSize(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Colorable.java b/vspigot-api/src/main/java/org/bukkit/material/Colorable.java new file mode 100644 index 0000000..3b91b24 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Colorable.java @@ -0,0 +1,24 @@ +package org.bukkit.material; + +import org.bukkit.DyeColor; + +/** + * An object that can be colored. + */ +public interface Colorable { + + /** + * Gets the color of this object. + * + * @return The DyeColor of this object. + */ + public DyeColor getColor(); + + /** + * Sets the color of this object to the specified DyeColor. + * + * @param color The color of the object, as a DyeColor. + */ + public void setColor(DyeColor color); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Command.java b/vspigot-api/src/main/java/org/bukkit/material/Command.java new file mode 100644 index 0000000..174e1ff --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Command.java @@ -0,0 +1,73 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents a command block + */ +public class Command extends MaterialData implements Redstone { + public Command() { + super(Material.COMMAND); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Command(final int type) { + super(type); + } + + public Command(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Command(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Command(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current state of this Material, indicating if it's powered or + * unpowered + * + * @return true if powered, otherwise false + */ + public boolean isPowered() { + return (getData() & 1) != 0; + } + + /** + * Sets the current state of this Material + * + * @param bool + * whether or not the command block is powered + */ + public void setPowered(boolean bool) { + setData((byte) (bool ? (getData() | 1) : (getData() & -2))); + } + + @Override + public String toString() { + return super.toString() + " " + (isPowered() ? "" : "NOT ") + "POWERED"; + } + + @Override + public Command clone() { + return (Command) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Crops.java b/vspigot-api/src/main/java/org/bukkit/material/Crops.java new file mode 100644 index 0000000..2791998 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Crops.java @@ -0,0 +1,77 @@ +package org.bukkit.material; + +import org.bukkit.CropState; +import org.bukkit.Material; + +/** + * Represents the different types of crops. + */ +public class Crops extends MaterialData { + public Crops() { + super(Material.CROPS); + } + + public Crops(CropState state) { + this(); + setState(state); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Crops(final int type) { + super(type); + } + + public Crops(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Crops(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Crops(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current growth state of this crop + * + * @return CropState of this crop + */ + public CropState getState() { + return CropState.getByData(getData()); + } + + /** + * Sets the growth state of this crop + * + * @param state New growth state of this crop + */ + public void setState(CropState state) { + setData(state.getData()); + } + + @Override + public String toString() { + return getState() + " " + super.toString(); + } + + @Override + public Crops clone() { + return (Crops) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/DetectorRail.java b/vspigot-api/src/main/java/org/bukkit/material/DetectorRail.java new file mode 100644 index 0000000..b1d3073 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/DetectorRail.java @@ -0,0 +1,56 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents a detector rail + */ +public class DetectorRail extends ExtendedRails implements PressureSensor { + public DetectorRail() { + super(Material.DETECTOR_RAIL); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public DetectorRail(final int type) { + super(type); + } + + public DetectorRail(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public DetectorRail(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public DetectorRail(final Material type, final byte data) { + super(type, data); + } + + public boolean isPressed() { + return (getData() & 0x8) == 0x8; + } + + public void setPressed(boolean isPressed) { + setData((byte) (isPressed ? (getData() | 0x8) : (getData() & ~0x8))); + } + + @Override + public DetectorRail clone() { + return (DetectorRail) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Diode.java b/vspigot-api/src/main/java/org/bukkit/material/Diode.java new file mode 100644 index 0000000..04210b7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Diode.java @@ -0,0 +1,123 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +public class Diode extends MaterialData implements Directional { + public Diode() { + super(Material.DIODE_BLOCK_ON); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Diode(int type) { + super(type); + } + + public Diode(Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Diode(int type, byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Diode(Material type, byte data) { + super(type, data); + } + + /** + * Sets the delay of the repeater + * + * @param delay + * The new delay (1-4) + */ + public void setDelay(int delay) { + if (delay > 4) { + delay = 4; + } + if (delay < 1) { + delay = 1; + } + byte newData = (byte) (getData() & 0x3); + + setData((byte) (newData | ((delay - 1) << 2))); + } + + /** + * Gets the delay of the repeater in ticks + * + * @return The delay (1-4) + */ + public int getDelay() { + return (getData() >> 2) + 1; + } + + public void setFacingDirection(BlockFace face) { + int delay = getDelay(); + byte data; + + switch (face) { + case EAST: + data = 0x1; + break; + + case SOUTH: + data = 0x2; + break; + + case WEST: + data = 0x3; + break; + + case NORTH: + default: + data = 0x0; + } + + setData(data); + setDelay(delay); + } + + public BlockFace getFacing() { + byte data = (byte) (getData() & 0x3); + + switch (data) { + case 0x0: + default: + return BlockFace.NORTH; + + case 0x1: + return BlockFace.EAST; + + case 0x2: + return BlockFace.SOUTH; + + case 0x3: + return BlockFace.WEST; + } + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing() + " with " + getDelay() + " ticks delay"; + } + + @Override + public Diode clone() { + return (Diode) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Directional.java b/vspigot-api/src/main/java/org/bukkit/material/Directional.java new file mode 100644 index 0000000..25624d2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Directional.java @@ -0,0 +1,20 @@ +package org.bukkit.material; + +import org.bukkit.block.BlockFace; + +public interface Directional { + + /** + * Sets the direction that this block is facing in + * + * @param face The facing direction + */ + public void setFacingDirection(BlockFace face); + + /** + * Gets the direction this block is facing + * + * @return the direction this block is facing + */ + public BlockFace getFacing(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/DirectionalContainer.java b/vspigot-api/src/main/java/org/bukkit/material/DirectionalContainer.java new file mode 100644 index 0000000..9b0a047 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/DirectionalContainer.java @@ -0,0 +1,93 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a furnace or a dispenser. + */ +public class DirectionalContainer extends MaterialData implements Directional { + /** + * + * @deprecated Magic value + */ + @Deprecated + public DirectionalContainer(final int type) { + super(type); + } + + public DirectionalContainer(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public DirectionalContainer(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public DirectionalContainer(final Material type, final byte data) { + super(type, data); + } + + public void setFacingDirection(BlockFace face) { + byte data; + + switch (face) { + case NORTH: + data = 0x2; + break; + + case SOUTH: + data = 0x3; + break; + + case WEST: + data = 0x4; + break; + + case EAST: + default: + data = 0x5; + } + + setData(data); + } + + public BlockFace getFacing() { + byte data = getData(); + + switch (data) { + case 0x2: + return BlockFace.NORTH; + + case 0x3: + return BlockFace.SOUTH; + + case 0x4: + return BlockFace.WEST; + + case 0x5: + default: + return BlockFace.EAST; + } + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing(); + } + + @Override + public DirectionalContainer clone() { + return (DirectionalContainer) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Dispenser.java b/vspigot-api/src/main/java/org/bukkit/material/Dispenser.java new file mode 100644 index 0000000..b62f8c9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Dispenser.java @@ -0,0 +1,112 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a dispenser. + */ +public class Dispenser extends FurnaceAndDispenser { + + public Dispenser() { + super(Material.DISPENSER); + } + + public Dispenser(BlockFace direction) { + this(); + setFacingDirection(direction); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Dispenser(final int type) { + super(type); + } + + public Dispenser(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Dispenser(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Dispenser(final Material type, final byte data) { + super(type, data); + } + + public void setFacingDirection(BlockFace face) { + byte data; + + switch (face) { + case DOWN: + data = 0x0; + break; + + case UP: + data = 0x1; + break; + + case NORTH: + data = 0x2; + break; + + case SOUTH: + data = 0x3; + break; + + case WEST: + data = 0x4; + break; + + case EAST: + default: + data = 0x5; + } + + setData(data); + } + + public BlockFace getFacing() { + int data = getData() & 0x7; + + switch (data) { + case 0x0: + return BlockFace.DOWN; + + case 0x1: + return BlockFace.UP; + + case 0x2: + return BlockFace.NORTH; + + case 0x3: + return BlockFace.SOUTH; + + case 0x4: + return BlockFace.WEST; + + case 0x5: + default: + return BlockFace.EAST; + } + } + + @Override + public Dispenser clone() { + return (Dispenser) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Door.java b/vspigot-api/src/main/java/org/bukkit/material/Door.java new file mode 100644 index 0000000..65fa32c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Door.java @@ -0,0 +1,160 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a door. + * + * @deprecated No longer functions. Do not use. + */ +@Deprecated +public class Door extends MaterialData implements Directional, Openable { + public Door() { + super(Material.WOODEN_DOOR); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Door(final int type) { + super(type); + } + + public Door(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Door(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Door(final Material type, final byte data) { + super(type, data); + } + + /** + * @deprecated Does not work (correctly) anymore + */ + @Deprecated + public boolean isOpen() { + return ((getData() & 0x4) == 0x4); + } + + /** + * @deprecated Does not work (correctly) anymore + */ + @Deprecated + public void setOpen(boolean isOpen) { + setData((byte) (isOpen ? (getData() | 0x4) : (getData() & ~0x4))); + } + + /** + * @return whether this is the top half of the door + */ + public boolean isTopHalf() { + return ((getData() & 0x8) == 0x8); + } + + /** + * Configure this part of the door to be either the top or the bottom half + * + * @param isTopHalf True to make it the top half. + * @deprecated Shouldn't be used anymore + */ + @Deprecated + public void setTopHalf(boolean isTopHalf) { + setData((byte) (isTopHalf ? (getData() | 0x8) : (getData() & ~0x8))); + } + + /** + * @return BlockFace.SELF + * @deprecated Does not work (correctly) anymore + */ + @Deprecated + public BlockFace getHingeCorner() { + byte d = getData(); + + if ((d & 0x3) == 0x3) { + return BlockFace.NORTH_WEST; + } else if ((d & 0x1) == 0x1) { + return BlockFace.SOUTH_EAST; + } else if ((d & 0x2) == 0x2) { + return BlockFace.SOUTH_WEST; + } + + return BlockFace.NORTH_EAST; + } + + @Override + public String toString() { + return (isTopHalf() ? "TOP" : "BOTTOM") + " half of " + super.toString(); + } + + /** + * Set the direction that this door should is facing. + * + * @param face the direction + * @deprecated Does not work (correctly) anymore + */ + @Deprecated + public void setFacingDirection(BlockFace face) { + byte data = (byte) (getData() & 0x12); + switch (face) { + case NORTH: + data |= 0x1; + break; + + case EAST: + data |= 0x2; + break; + + case SOUTH: + data |= 0x3; + break; + } + setData(data); + } + + /** + * Get the direction that this door is facing. + * + * @return the direction + * @deprecated Does not work (correctly) anymore + */ + @Deprecated + public BlockFace getFacing() { + byte data = (byte) (getData() & 0x3); + switch (data) { + case 0: + return BlockFace.WEST; + + case 1: + return BlockFace.NORTH; + + case 2: + return BlockFace.EAST; + + case 3: + return BlockFace.SOUTH; + } + return null; // shouldn't happen + } + + @Override + public Door clone() { + return (Door) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Dye.java b/vspigot-api/src/main/java/org/bukkit/material/Dye.java new file mode 100644 index 0000000..4412c1f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Dye.java @@ -0,0 +1,72 @@ +package org.bukkit.material; + +import org.bukkit.DyeColor; +import org.bukkit.Material; + +/** + * Represents dye + */ +public class Dye extends MaterialData implements Colorable { + public Dye() { + super(Material.INK_SACK); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Dye(final int type) { + super(type); + } + + public Dye(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Dye(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Dye(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current color of this dye + * + * @return DyeColor of this dye + */ + public DyeColor getColor() { + return DyeColor.getByDyeData(getData()); + } + + /** + * Sets the color of this dye + * + * @param color New color of this dye + */ + public void setColor(DyeColor color) { + setData(color.getDyeData()); + } + + @Override + public String toString() { + return getColor() + " DYE(" + getData() + ")"; + } + + @Override + public Dye clone() { + return (Dye) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/EnderChest.java b/vspigot-api/src/main/java/org/bukkit/material/EnderChest.java new file mode 100644 index 0000000..696dc65 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/EnderChest.java @@ -0,0 +1,60 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents an ender chest + */ +public class EnderChest extends DirectionalContainer { + + public EnderChest() { + super(Material.ENDER_CHEST); + } + + /** + * Instantiate an ender chest facing in a particular direction. + * + * @param direction the direction the ender chest's lid opens towards + */ + public EnderChest(BlockFace direction) { + this(); + setFacingDirection(direction); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public EnderChest(final int type) { + super(type); + } + + public EnderChest(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public EnderChest(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public EnderChest(final Material type, final byte data) { + super(type, data); + } + + @Override + public EnderChest clone() { + return (EnderChest) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/ExtendedRails.java b/vspigot-api/src/main/java/org/bukkit/material/ExtendedRails.java new file mode 100644 index 0000000..0dbbf7c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/ExtendedRails.java @@ -0,0 +1,73 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * This is the superclass for the {@link DetectorRail} and {@link PoweredRail} + * classes + */ +public class ExtendedRails extends Rails { + /** + * + * @deprecated Magic value + */ + @Deprecated + public ExtendedRails(final int type) { + super(type); + } + + public ExtendedRails(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public ExtendedRails(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public ExtendedRails(final Material type, final byte data) { + super(type, data); + } + + @Override + public boolean isCurve() { + return false; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + @Override + protected byte getConvertedData() { + return (byte) (getData() & 0x7); + } + + @Override + public void setDirection(BlockFace face, boolean isOnSlope) { + boolean extraBitSet = (getData() & 0x8) == 0x8; + + if (face != BlockFace.WEST && face != BlockFace.EAST && face != BlockFace.NORTH && face != BlockFace.SOUTH) { + throw new IllegalArgumentException("Detector rails and powered rails cannot be set on a curve!"); + } + + super.setDirection(face, isOnSlope); + setData((byte) (extraBitSet ? (getData() | 0x8) : (getData() & ~0x8))); + } + + @Override + public ExtendedRails clone() { + return (ExtendedRails) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/FlowerPot.java b/vspigot-api/src/main/java/org/bukkit/material/FlowerPot.java new file mode 100644 index 0000000..787c58d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/FlowerPot.java @@ -0,0 +1,135 @@ +package org.bukkit.material; + +import org.bukkit.GrassSpecies; +import org.bukkit.Material; +import org.bukkit.TreeSpecies; + +/** + * Represents a flower pot. + */ +public class FlowerPot extends MaterialData { + + /** + * Default constructor for a flower pot. + */ + public FlowerPot() { + super(Material.FLOWER_POT); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public FlowerPot(final int type) { + super(type); + } + + public FlowerPot(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public FlowerPot(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public FlowerPot(final Material type, final byte data) { + super(type, data); + } + + /** + * Get the material in the flower pot + * + * @return material MaterialData for the block currently in the flower pot + * or null if empty + */ + public MaterialData getContents() { + switch (getData()) { + case 1: + return new MaterialData(Material.RED_ROSE); + case 2: + return new MaterialData(Material.YELLOW_FLOWER); + case 3: + return new Tree(TreeSpecies.GENERIC); + case 4: + return new Tree(TreeSpecies.REDWOOD); + case 5: + return new Tree(TreeSpecies.BIRCH); + case 6: + return new Tree(TreeSpecies.JUNGLE); + case 7: + return new MaterialData(Material.RED_MUSHROOM); + case 8: + return new MaterialData(Material.BROWN_MUSHROOM); + case 9: + return new MaterialData(Material.CACTUS); + case 10: + return new MaterialData(Material.DEAD_BUSH); + case 11: + return new LongGrass(GrassSpecies.FERN_LIKE); + default: + return null; + } + } + + /** + * Set the contents of the flower pot + * + * @param materialData MaterialData of the block to put in the flower pot. + */ + public void setContents(MaterialData materialData) { + Material mat = materialData.getItemType(); + + if (mat == Material.RED_ROSE) { + setData((byte) 1); + } else if (mat == Material.YELLOW_FLOWER) { + setData((byte) 2); + } else if (mat == Material.RED_MUSHROOM) { + setData((byte) 7); + } else if (mat == Material.BROWN_MUSHROOM) { + setData((byte) 8); + } else if (mat == Material.CACTUS) { + setData((byte) 9); + } else if (mat == Material.DEAD_BUSH) { + setData((byte) 10); + } else if (mat == Material.SAPLING) { + TreeSpecies species = ((Tree) materialData).getSpecies(); + + if (species == TreeSpecies.GENERIC) { + setData((byte) 3); + } else if (species == TreeSpecies.REDWOOD) { + setData((byte) 4); + } else if (species == TreeSpecies.BIRCH) { + setData((byte) 5); + } else { + setData((byte) 6); + } + } else if (mat == Material.LONG_GRASS) { + GrassSpecies species = ((LongGrass) materialData).getSpecies(); + + if (species == GrassSpecies.FERN_LIKE) { + setData((byte) 11); + } + } + } + + @Override + public String toString() { + return super.toString() + " containing " + getContents(); + } + + @Override + public FlowerPot clone() { + return (FlowerPot) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Furnace.java b/vspigot-api/src/main/java/org/bukkit/material/Furnace.java new file mode 100644 index 0000000..49645aa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Furnace.java @@ -0,0 +1,60 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a furnace. + */ +public class Furnace extends FurnaceAndDispenser { + + public Furnace() { + super(Material.FURNACE); + } + + /** + * Instantiate a furnace facing in a particular direction. + * + * @param direction the direction the furnace's "opening" is facing + */ + public Furnace(BlockFace direction) { + this(); + setFacingDirection(direction); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Furnace(final int type) { + super(type); + } + + public Furnace(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Furnace(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Furnace(final Material type, final byte data) { + super(type, data); + } + + @Override + public Furnace clone() { + return (Furnace) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/FurnaceAndDispenser.java b/vspigot-api/src/main/java/org/bukkit/material/FurnaceAndDispenser.java new file mode 100644 index 0000000..3665479 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/FurnaceAndDispenser.java @@ -0,0 +1,45 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents a furnace or dispenser, two types of directional containers + */ +public class FurnaceAndDispenser extends DirectionalContainer { + + /** + * + * @deprecated Magic value + */ + @Deprecated + public FurnaceAndDispenser(final int type) { + super(type); + } + + public FurnaceAndDispenser(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public FurnaceAndDispenser(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public FurnaceAndDispenser(final Material type, final byte data) { + super(type, data); + } + + @Override + public FurnaceAndDispenser clone() { + return (FurnaceAndDispenser) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Gate.java b/vspigot-api/src/main/java/org/bukkit/material/Gate.java new file mode 100644 index 0000000..8adc1cf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Gate.java @@ -0,0 +1,91 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a fence gate + */ +public class Gate extends MaterialData implements Directional, Openable { + private static final byte OPEN_BIT = 0x4; + private static final byte DIR_BIT = 0x3; + private static final byte GATE_SOUTH = 0x0; + private static final byte GATE_WEST = 0x1; + private static final byte GATE_NORTH = 0x2; + private static final byte GATE_EAST = 0x3; + + public Gate() { + super(Material.FENCE_GATE); + } + + public Gate(int type, byte data){ + super(type, data); + } + + public Gate(byte data) { + super(Material.FENCE_GATE, data); + } + + public void setFacingDirection(BlockFace face) { + byte data = (byte) (getData() &~ DIR_BIT); + + switch (face) { + default: + case EAST: + data |= GATE_SOUTH; + break; + case SOUTH: + data |= GATE_WEST; + break; + case WEST: + data |= GATE_NORTH; + break; + case NORTH: + data |= GATE_EAST; + break; + } + + setData(data); + } + + public BlockFace getFacing() { + switch (getData() & DIR_BIT) { + case GATE_SOUTH: + return BlockFace.EAST; + case GATE_WEST: + return BlockFace.SOUTH; + case GATE_NORTH: + return BlockFace.WEST; + case GATE_EAST: + return BlockFace.NORTH; + } + + return BlockFace.EAST; + } + + public boolean isOpen() { + return (getData() & OPEN_BIT) > 0; + } + + public void setOpen(boolean isOpen) { + byte data = getData(); + + if (isOpen) { + data |= OPEN_BIT; + } else { + data &= ~OPEN_BIT; + } + + setData(data); + } + + @Override + public String toString() { + return (isOpen() ? "OPEN " : "CLOSED ") + " facing and opening " + getFacing(); + } + + @Override + public Gate clone() { + return (Gate) super.clone(); + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/material/Ladder.java b/vspigot-api/src/main/java/org/bukkit/material/Ladder.java new file mode 100644 index 0000000..09862bf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Ladder.java @@ -0,0 +1,102 @@ +package org.bukkit.material; + +import org.bukkit.block.BlockFace; +import org.bukkit.Material; + +/** + * Represents Ladder data + */ +public class Ladder extends SimpleAttachableMaterialData { + public Ladder() { + super(Material.LADDER); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Ladder(final int type) { + super(type); + } + + public Ladder(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Ladder(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Ladder(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the face that this block is attached on + * + * @return BlockFace attached to + */ + public BlockFace getAttachedFace() { + byte data = getData(); + + switch (data) { + case 0x2: + return BlockFace.SOUTH; + + case 0x3: + return BlockFace.NORTH; + + case 0x4: + return BlockFace.EAST; + + case 0x5: + return BlockFace.WEST; + } + + return null; + } + + /** + * Sets the direction this ladder is facing + */ + public void setFacingDirection(BlockFace face) { + byte data = (byte) 0x0; + + switch (face) { + case SOUTH: + data = 0x2; + break; + + case NORTH: + data = 0x3; + break; + + case EAST: + data = 0x4; + break; + + case WEST: + data = 0x5; + break; + } + + setData(data); + + } + + @Override + public Ladder clone() { + return (Ladder) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Leaves.java b/vspigot-api/src/main/java/org/bukkit/material/Leaves.java new file mode 100644 index 0000000..97ba382 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Leaves.java @@ -0,0 +1,77 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.TreeSpecies; + +/** + * Represents the different types of leaves. + */ +public class Leaves extends MaterialData { + public Leaves() { + super(Material.LEAVES); + } + + public Leaves(TreeSpecies species) { + this(); + setSpecies(species); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Leaves(final int type) { + super(type); + } + + public Leaves(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Leaves(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Leaves(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current species of this leave + * + * @return TreeSpecies of this leave + */ + public TreeSpecies getSpecies() { + return TreeSpecies.getByData((byte) (getData() & 3)); + } + + /** + * Sets the species of this leave + * + * @param species New species of this leave + */ + public void setSpecies(TreeSpecies species) { + setData(species.getData()); + } + + @Override + public String toString() { + return getSpecies() + " " + super.toString(); + } + + @Override + public Leaves clone() { + return (Leaves) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Lever.java b/vspigot-api/src/main/java/org/bukkit/material/Lever.java new file mode 100644 index 0000000..b88c536 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Lever.java @@ -0,0 +1,160 @@ +package org.bukkit.material; + +import org.bukkit.block.BlockFace; +import org.bukkit.Material; + +/** + * Represents a lever + */ +public class Lever extends SimpleAttachableMaterialData implements Redstone { + public Lever() { + super(Material.LEVER); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Lever(final int type) { + super(type); + } + + public Lever(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Lever(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Lever(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current state of this Material, indicating if it's powered or + * unpowered + * + * @return true if powered, otherwise false + */ + public boolean isPowered() { + return (getData() & 0x8) == 0x8; + } + + /** + * Set this lever to be powered or not. + * + * @param isPowered whether the lever should be powered or not + */ + public void setPowered(boolean isPowered) { + setData((byte) (isPowered ? (getData() | 0x8) : (getData() & ~0x8))); + } + + /** + * Gets the face that this block is attached on + * + * @return BlockFace attached to + */ + public BlockFace getAttachedFace() { + byte data = (byte) (getData() & 0x7); + + switch (data) { + case 0x1: + return BlockFace.WEST; + + case 0x2: + return BlockFace.EAST; + + case 0x3: + return BlockFace.NORTH; + + case 0x4: + return BlockFace.SOUTH; + + case 0x5: + case 0x6: + return BlockFace.DOWN; + + case 0x0: + case 0x7: + return BlockFace.UP; + + } + + return null; + } + + /** + * Sets the direction this lever is pointing in + */ + public void setFacingDirection(BlockFace face) { + byte data = (byte) (getData() & 0x8); + BlockFace attach = getAttachedFace(); + + if (attach == BlockFace.DOWN) { + switch (face) { + case SOUTH: + case NORTH: + data |= 0x5; + break; + + case EAST: + case WEST: + data |= 0x6; + break; + } + } else if (attach == BlockFace.UP) { + switch (face) { + case SOUTH: + case NORTH: + data |= 0x7; + break; + + case EAST: + case WEST: + data |= 0x0; + break; + } + } else { + switch (face) { + case EAST: + data |= 0x1; + break; + + case WEST: + data |= 0x2; + break; + + case SOUTH: + data |= 0x3; + break; + + case NORTH: + data |= 0x4; + break; + } + } + setData(data); + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing() + " " + (isPowered() ? "" : "NOT ") + "POWERED"; + } + + @Override + public Lever clone() { + return (Lever) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/LongGrass.java b/vspigot-api/src/main/java/org/bukkit/material/LongGrass.java new file mode 100644 index 0000000..e8d1f38 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/LongGrass.java @@ -0,0 +1,77 @@ +package org.bukkit.material; + +import org.bukkit.GrassSpecies; +import org.bukkit.Material; + +/** + * Represents the different types of long grasses. + */ +public class LongGrass extends MaterialData { + public LongGrass() { + super(Material.LONG_GRASS); + } + + public LongGrass(GrassSpecies species) { + this(); + setSpecies(species); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public LongGrass(final int type) { + super(type); + } + + public LongGrass(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public LongGrass(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public LongGrass(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current species of this grass + * + * @return GrassSpecies of this grass + */ + public GrassSpecies getSpecies() { + return GrassSpecies.getByData(getData()); + } + + /** + * Sets the species of this grass + * + * @param species New species of this grass + */ + public void setSpecies(GrassSpecies species) { + setData(species.getData()); + } + + @Override + public String toString() { + return getSpecies() + " " + super.toString(); + } + + @Override + public LongGrass clone() { + return (LongGrass) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/MaterialData.java b/vspigot-api/src/main/java/org/bukkit/material/MaterialData.java new file mode 100644 index 0000000..ad7675f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/MaterialData.java @@ -0,0 +1,135 @@ +package org.bukkit.material; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.Material; + +/** + * Handles specific metadata for certain items or blocks + */ +public class MaterialData implements Cloneable { + private final int type; + private byte data = 0; + + /** + * + * @deprecated Magic value + */ + @Deprecated + public MaterialData(final int type) { + this(type, (byte) 0); + } + + public MaterialData(final Material type) { + this(type, (byte) 0); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public MaterialData(final int type, final byte data) { + this.type = type; + this.data = data; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public MaterialData(final Material type, final byte data) { + this(type.getId(), data); + } + + /** + * Gets the raw data in this material + * + * @return Raw data + * @deprecated Magic value + */ + @Deprecated + public byte getData() { + return data; + } + + /** + * Sets the raw data of this material + * + * @param data New raw data + * @deprecated Magic value + */ + @Deprecated + public void setData(byte data) { + this.data = data; + } + + /** + * Gets the Material that this MaterialData represents + * + * @return Material represented by this MaterialData + */ + public Material getItemType() { + return Material.getMaterial(type); + } + + /** + * Gets the Material Id that this MaterialData represents + * + * @return Material Id represented by this MaterialData + * @deprecated Magic value + */ + @Deprecated + public int getItemTypeId() { + return type; + } + + /** + * Creates a new ItemStack based on this MaterialData + * + * @return New ItemStack containing a copy of this MaterialData + */ + public ItemStack toItemStack() { + return new ItemStack(type, 0, data); + } + + /** + * Creates a new ItemStack based on this MaterialData + * + * @param amount The stack size of the new stack + * @return New ItemStack containing a copy of this MaterialData + */ + public ItemStack toItemStack(int amount) { + return new ItemStack(type, amount, data); + } + + @Override + public String toString() { + return getItemType() + "(" + getData() + ")"; + } + + @Override + public int hashCode() { + return ((getItemTypeId() << 8) ^ getData()); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof MaterialData) { + MaterialData md = (MaterialData) obj; + + return (md.getItemTypeId() == getItemTypeId() && md.getData() == getData()); + } else { + return false; + } + } + + @Override + public MaterialData clone() { + try { + return (MaterialData) super.clone(); + } catch (CloneNotSupportedException e) { + throw new Error(e); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/MonsterEggs.java b/vspigot-api/src/main/java/org/bukkit/material/MonsterEggs.java new file mode 100644 index 0000000..d8b627b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/MonsterEggs.java @@ -0,0 +1,67 @@ +package org.bukkit.material; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; + +/** + * Represents the different types of monster eggs + */ +public class MonsterEggs extends TexturedMaterial { + + private static final List textures = new ArrayList(); + static { + textures.add(Material.STONE); + textures.add(Material.COBBLESTONE); + textures.add(Material.SMOOTH_BRICK); + } + + public MonsterEggs() { + super(Material.MONSTER_EGGS); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public MonsterEggs(final int type) { + super(type); + } + + public MonsterEggs(final Material type) { + super((textures.contains(type)) ? Material.MONSTER_EGGS : type); + if (textures.contains(type)) { + setMaterial(type); + } + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public MonsterEggs(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public MonsterEggs(final Material type, final byte data) { + super(type, data); + } + + @Override + public List getTextures() { + return textures; + } + + @Override + public MonsterEggs clone() { + return (MonsterEggs) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Mushroom.java b/vspigot-api/src/main/java/org/bukkit/material/Mushroom.java new file mode 100644 index 0000000..716d378 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Mushroom.java @@ -0,0 +1,197 @@ +package org.bukkit.material; + +import java.util.EnumSet; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a huge mushroom block + */ +public class Mushroom extends MaterialData { + private static final byte SHROOM_NONE = 0; + private static final byte SHROOM_STEM = 10; + private static final byte NORTH_LIMIT = 4; + private static final byte SOUTH_LIMIT = 6; + private static final byte EAST_WEST_LIMIT = 3; + private static final byte EAST_REMAINDER = 0; + private static final byte WEST_REMAINDER = 1; + private static final byte NORTH_SOUTH_MOD = 3; + private static final byte EAST_WEST_MOD = 1; + + public Mushroom(Material shroom) { + super(shroom); + Validate.isTrue(shroom == Material.HUGE_MUSHROOM_1 || shroom == Material.HUGE_MUSHROOM_2, "Not a mushroom!"); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Mushroom(Material shroom, byte data) { + super(shroom, data); + Validate.isTrue(shroom == Material.HUGE_MUSHROOM_1 || shroom == Material.HUGE_MUSHROOM_2, "Not a mushroom!"); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Mushroom(int type, byte data){ + super(type, data); + Validate.isTrue(type == Material.HUGE_MUSHROOM_1.getId() || type == Material.HUGE_MUSHROOM_2.getId(), "Not a mushroom!"); + } + + /** + * @return Whether this is a mushroom stem. + */ + public boolean isStem() { + return getData() == SHROOM_STEM; + } + + /** + * Sets this to be a mushroom stem. + */ + public void setStem() { + setData((byte) 10); + } + + /** + * Checks whether a face of the block is painted. + * + * @param face The face to check. + * @return True if it is painted. + */ + public boolean isFacePainted(BlockFace face) { + byte data = getData(); + + if (data == SHROOM_NONE || data == SHROOM_STEM) { + return false; + } + + switch (face) { + case WEST: + return data < NORTH_LIMIT; + case EAST: + return data > SOUTH_LIMIT; + case NORTH: + return data % EAST_WEST_LIMIT == EAST_REMAINDER; + case SOUTH: + return data % EAST_WEST_LIMIT == WEST_REMAINDER; + case UP: + return true; + default: + return false; + } + } + + /** + * Set a face of the block to be painted or not. Note that due to the + * nature of how the data is stored, setting a face painted or not is not + * guaranteed to leave the other faces unchanged. + * + * @param face The face to paint or unpaint. + * @param painted True if you want to paint it, false if you want the + * pores to show. + */ + public void setFacePainted(BlockFace face, boolean painted) { + if (painted == isFacePainted(face)) { + return; + } + + byte data = getData(); + + if (data == SHROOM_STEM) { + data = 5; + } + + switch (face) { + case WEST: + if (painted) { + data -= NORTH_SOUTH_MOD; + } else { + data += NORTH_SOUTH_MOD; + } + + break; + case EAST: + if (painted) { + data += NORTH_SOUTH_MOD; + } else { + data -= NORTH_SOUTH_MOD; + } + + break; + case NORTH: + if (painted) { + data += EAST_WEST_MOD; + } else { + data -= EAST_WEST_MOD; + } + + break; + case SOUTH: + if (painted) { + data -= EAST_WEST_MOD; + } else { + data += EAST_WEST_MOD; + } + + break; + case UP: + if (!painted) { + data = 0; + } + + break; + default: + throw new IllegalArgumentException("Can't paint that face of a mushroom!"); + } + + setData(data); + } + + /** + * @return A set of all faces that are currently painted (an empty set if + * it is a stem) + */ + public Set getPaintedFaces() { + EnumSet faces = EnumSet.noneOf(BlockFace.class); + + if (isFacePainted(BlockFace.WEST)) { + faces.add(BlockFace.WEST); + } + + if (isFacePainted(BlockFace.NORTH)) { + faces.add(BlockFace.NORTH); + } + + if (isFacePainted(BlockFace.SOUTH)) { + faces.add(BlockFace.SOUTH); + } + + if (isFacePainted(BlockFace.EAST)) { + faces.add(BlockFace.EAST); + } + + if (isFacePainted(BlockFace.UP)) { + faces.add(BlockFace.UP); + } + + return faces; + } + + @Override + public String toString() { + return Material.getMaterial(getItemTypeId()).toString() + (isStem() ? "{STEM}" : getPaintedFaces()); + } + + @Override + public Mushroom clone() { + return (Mushroom) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/NetherWarts.java b/vspigot-api/src/main/java/org/bukkit/material/NetherWarts.java new file mode 100644 index 0000000..99ea3f0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/NetherWarts.java @@ -0,0 +1,99 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.NetherWartsState; + +/** + * Represents nether wart + */ +public class NetherWarts extends MaterialData { + public NetherWarts() { + super(Material.NETHER_WARTS); + } + + public NetherWarts(NetherWartsState state) { + this(); + setState(state); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public NetherWarts(final int type) { + super(type); + } + + public NetherWarts(final Material type) { + super (type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public NetherWarts(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public NetherWarts(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current growth state of this nether wart + * + * @return NetherWartsState of this nether wart + */ + public NetherWartsState getState() { + switch (getData()) { + case 0: + return NetherWartsState.SEEDED; + case 1: + return NetherWartsState.STAGE_ONE; + case 2: + return NetherWartsState.STAGE_TWO; + default: + return NetherWartsState.RIPE; + } + } + + /** + * Sets the growth state of this nether wart + * + * @param state New growth state of this nether wart + */ + public void setState(NetherWartsState state) { + switch (state) { + case SEEDED: + setData((byte) 0x0); + return; + case STAGE_ONE: + setData((byte) 0x1); + return; + case STAGE_TWO: + setData((byte) 0x2); + return; + case RIPE: + setData((byte) 0x3); + return; + } + } + + @Override + public String toString() { + return getState() + " " + super.toString(); + } + + @Override + public NetherWarts clone() { + return (NetherWarts) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Openable.java b/vspigot-api/src/main/java/org/bukkit/material/Openable.java new file mode 100644 index 0000000..0ae54f9 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Openable.java @@ -0,0 +1,18 @@ +package org.bukkit.material; + +public interface Openable { + + /** + * Check to see if the door is open. + * + * @return true if the door has swung counterclockwise around its hinge. + */ + boolean isOpen(); + + /** + * Configure this door to be either open or closed; + * + * @param isOpen True to open the door. + */ + void setOpen(boolean isOpen); +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/PistonBaseMaterial.java b/vspigot-api/src/main/java/org/bukkit/material/PistonBaseMaterial.java new file mode 100644 index 0000000..f3586dc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/PistonBaseMaterial.java @@ -0,0 +1,111 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Material data for the piston base block + */ +public class PistonBaseMaterial extends MaterialData implements Directional, Redstone { + /** + * + * @deprecated Magic value + */ + @Deprecated + public PistonBaseMaterial(final int type) { + super(type); + } + + public PistonBaseMaterial(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PistonBaseMaterial(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PistonBaseMaterial(final Material type, final byte data) { + super(type, data); + } + + public void setFacingDirection(BlockFace face) { + byte data = (byte) (getData() & 0x8); + + switch (face) { + case UP: + data |= 1; + break; + case NORTH: + data |= 2; + break; + case SOUTH: + data |= 3; + break; + case WEST: + data |= 4; + break; + case EAST: + data |= 5; + break; + } + setData(data); + } + + public BlockFace getFacing() { + byte dir = (byte) (getData() & 7); + + switch (dir) { + case 0: + return BlockFace.DOWN; + case 1: + return BlockFace.UP; + case 2: + return BlockFace.NORTH; + case 3: + return BlockFace.SOUTH; + case 4: + return BlockFace.WEST; + case 5: + return BlockFace.EAST; + default: + return BlockFace.SELF; + } + } + + public boolean isPowered() { + return (getData() & 0x8) == 0x8; + } + + /** + * Sets the current state of this piston + * + * @param powered true if the piston is extended & powered, or false + */ + public void setPowered(boolean powered) { + setData((byte) (powered ? (getData() | 0x8) : (getData() & ~0x8))); + } + + /** + * Checks if this piston base is sticky, and returns true if so + * + * @return true if this piston is "sticky", or false + */ + public boolean isSticky() { + return this.getItemType() == Material.PISTON_STICKY_BASE; + } + + @Override + public PistonBaseMaterial clone() { + return (PistonBaseMaterial) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/PistonExtensionMaterial.java b/vspigot-api/src/main/java/org/bukkit/material/PistonExtensionMaterial.java new file mode 100644 index 0000000..85dee84 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/PistonExtensionMaterial.java @@ -0,0 +1,111 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Material data for the piston extension block + */ +public class PistonExtensionMaterial extends MaterialData implements Attachable { + /** + * + * @deprecated Magic value + */ + @Deprecated + public PistonExtensionMaterial(final int type) { + super(type); + } + + public PistonExtensionMaterial(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PistonExtensionMaterial(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PistonExtensionMaterial(final Material type, final byte data) { + super(type, data); + } + + public void setFacingDirection(BlockFace face) { + byte data = (byte) (getData() & 0x8); + + switch (face) { + case UP: + data |= 1; + break; + case NORTH: + data |= 2; + break; + case SOUTH: + data |= 3; + break; + case WEST: + data |= 4; + break; + case EAST: + data |= 5; + break; + } + setData(data); + } + + public BlockFace getFacing() { + byte dir = (byte) (getData() & 7); + + switch (dir) { + case 0: + return BlockFace.DOWN; + case 1: + return BlockFace.UP; + case 2: + return BlockFace.NORTH; + case 3: + return BlockFace.SOUTH; + case 4: + return BlockFace.WEST; + case 5: + return BlockFace.EAST; + default: + return BlockFace.SELF; + } + } + + /** + * Checks if this piston extension is sticky, and returns true if so + * + * @return true if this piston is "sticky", or false + */ + public boolean isSticky() { + return (getData() & 8) == 8; + } + + /** + * Sets whether or not this extension is sticky + * + * @param sticky true if sticky, otherwise false + */ + public void setSticky(boolean sticky) { + setData((byte) (sticky ? (getData() | 0x8) : (getData() & ~0x8))); + } + + public BlockFace getAttachedFace() { + return getFacing().getOppositeFace(); + } + + @Override + public PistonExtensionMaterial clone() { + return (PistonExtensionMaterial) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/PoweredRail.java b/vspigot-api/src/main/java/org/bukkit/material/PoweredRail.java new file mode 100644 index 0000000..444e53b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/PoweredRail.java @@ -0,0 +1,61 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents a powered rail + */ +public class PoweredRail extends ExtendedRails implements Redstone { + public PoweredRail() { + super(Material.POWERED_RAIL); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PoweredRail(final int type) { + super(type); + } + + public PoweredRail(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PoweredRail(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PoweredRail(final Material type, final byte data) { + super(type, data); + } + + public boolean isPowered() { + return (getData() & 0x8) == 0x8; + } + + /** + * Set whether this PoweredRail should be powered or not. + * + * @param isPowered whether or not the rail is powered + */ + public void setPowered(boolean isPowered) { + setData((byte) (isPowered ? (getData() | 0x8) : (getData() & ~0x8))); + } + + @Override + public PoweredRail clone() { + return (PoweredRail) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/PressurePlate.java b/vspigot-api/src/main/java/org/bukkit/material/PressurePlate.java new file mode 100644 index 0000000..8c3bc75 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/PressurePlate.java @@ -0,0 +1,57 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents a pressure plate + */ +public class PressurePlate extends MaterialData implements PressureSensor { + public PressurePlate() { + super(Material.WOOD_PLATE); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PressurePlate(int type) { + super(type); + } + + public PressurePlate(Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PressurePlate(int type, byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public PressurePlate(Material type, byte data) { + super(type, data); + } + + public boolean isPressed() { + return getData() == 0x1; + } + + @Override + public String toString() { + return super.toString() + (isPressed() ? " PRESSED" : ""); + } + + @Override + public PressurePlate clone() { + return (PressurePlate) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/PressureSensor.java b/vspigot-api/src/main/java/org/bukkit/material/PressureSensor.java new file mode 100644 index 0000000..de20bd3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/PressureSensor.java @@ -0,0 +1,5 @@ +package org.bukkit.material; + +public interface PressureSensor { + public boolean isPressed(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Pumpkin.java b/vspigot-api/src/main/java/org/bukkit/material/Pumpkin.java new file mode 100644 index 0000000..d6ca83c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Pumpkin.java @@ -0,0 +1,112 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a pumpkin. + */ +public class Pumpkin extends MaterialData implements Directional { + + public Pumpkin() { + super(Material.PUMPKIN); + } + + /** + * Instantiate a pumpkin facing in a particular direction. + * + * @param direction the direction the pumkin's face is facing + */ + public Pumpkin(BlockFace direction) { + this(); + setFacingDirection(direction); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Pumpkin(final int type) { + super(type); + } + + public Pumpkin(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Pumpkin(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Pumpkin(final Material type, final byte data) { + super(type, data); + } + + public boolean isLit() { + return getItemType() == Material.JACK_O_LANTERN; + } + + public void setFacingDirection(BlockFace face) { + byte data; + + switch (face) { + case NORTH: + data = 0x0; + break; + + case EAST: + data = 0x1; + break; + + case SOUTH: + data = 0x2; + break; + + case WEST: + default: + data = 0x3; + } + + setData(data); + } + + public BlockFace getFacing() { + byte data = getData(); + + switch (data) { + case 0x0: + return BlockFace.NORTH; + + case 0x1: + return BlockFace.EAST; + + case 0x2: + return BlockFace.SOUTH; + + case 0x3: + default: + return BlockFace.EAST; + } + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing() + " " + (isLit() ? "" : "NOT ") + "LIT"; + } + + @Override + public Pumpkin clone() { + return (Pumpkin) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Rails.java b/vspigot-api/src/main/java/org/bukkit/material/Rails.java new file mode 100644 index 0000000..74fd95a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Rails.java @@ -0,0 +1,176 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents minecart rails. + */ +public class Rails extends MaterialData { + + public Rails() { + super(Material.RAILS); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Rails(final int type) { + super(type); + } + + public Rails(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Rails(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Rails(final Material type, final byte data) { + super(type, data); + } + + /** + * @return the whether this track is set on a slope + */ + public boolean isOnSlope() { + byte d = getConvertedData(); + + return (d == 0x2 || d == 0x3 || d == 0x4 || d == 0x5); + } + + /** + * @return the whether this track is set as a curve + */ + public boolean isCurve() { + byte d = getConvertedData(); + + return (d == 0x6 || d == 0x7 || d == 0x8 || d == 0x9); + } + + /** + * @return the direction these tracks are set + *

          + * Note that tracks are bidirectional and that the direction returned + * is the ascending direction if the track is set on a slope. If it is + * set as a curve, the corner of the track is returned. + */ + public BlockFace getDirection() { + byte d = getConvertedData(); + + switch (d) { + case 0x0: + default: + return BlockFace.SOUTH; + + case 0x1: + return BlockFace.EAST; + + case 0x2: + return BlockFace.EAST; + + case 0x3: + return BlockFace.WEST; + + case 0x4: + return BlockFace.NORTH; + + case 0x5: + return BlockFace.SOUTH; + + case 0x6: + return BlockFace.NORTH_WEST; + + case 0x7: + return BlockFace.NORTH_EAST; + + case 0x8: + return BlockFace.SOUTH_EAST; + + case 0x9: + return BlockFace.SOUTH_WEST; + } + } + + @Override + public String toString() { + return super.toString() + " facing " + getDirection() + (isCurve() ? " on a curve" : (isOnSlope() ? " on a slope" : "")); + } + + /** + * Return the data without the extended properties used by {@link + * PoweredRail} and {@link DetectorRail}. Overridden in {@link + * ExtendedRails} + * + * @return the data without the extended part + * @deprecated Magic value + */ + @Deprecated + protected byte getConvertedData() { + return getData(); + } + + /** + * Set the direction of these tracks + *

          + * Note that tracks are bidirectional and that the direction returned is + * the ascending direction if the track is set on a slope. If it is set as + * a curve, the corner of the track should be supplied. + * + * @param face the direction the track should be facing + * @param isOnSlope whether or not the track should be on a slope + */ + public void setDirection(BlockFace face, boolean isOnSlope) { + switch (face) { + case EAST: + setData((byte) (isOnSlope ? 0x2 : 0x1)); + break; + + case WEST: + setData((byte) (isOnSlope ? 0x3 : 0x1)); + break; + + case NORTH: + setData((byte) (isOnSlope ? 0x4 : 0x0)); + break; + + case SOUTH: + setData((byte) (isOnSlope ? 0x5 : 0x0)); + break; + + case NORTH_WEST: + setData((byte) 0x6); + break; + + case NORTH_EAST: + setData((byte) 0x7); + break; + + case SOUTH_EAST: + setData((byte) 0x8); + break; + + case SOUTH_WEST: + setData((byte) 0x9); + break; + } + } + + @Override + public Rails clone() { + return (Rails) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Redstone.java b/vspigot-api/src/main/java/org/bukkit/material/Redstone.java new file mode 100644 index 0000000..3e46603 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Redstone.java @@ -0,0 +1,15 @@ +package org.bukkit.material; + +/** + * Indicated a Material that may carry or create a Redstone current + */ +public interface Redstone { + + /** + * Gets the current state of this Material, indicating if it's powered or + * unpowered + * + * @return true if powered, otherwise false + */ + public boolean isPowered(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/RedstoneTorch.java b/vspigot-api/src/main/java/org/bukkit/material/RedstoneTorch.java new file mode 100644 index 0000000..76a3ddd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/RedstoneTorch.java @@ -0,0 +1,63 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents a redstone torch + */ +public class RedstoneTorch extends Torch implements Redstone { + public RedstoneTorch() { + super(Material.REDSTONE_TORCH_ON); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public RedstoneTorch(final int type) { + super(type); + } + + public RedstoneTorch(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public RedstoneTorch(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public RedstoneTorch(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current state of this Material, indicating if it's powered or + * unpowered + * + * @return true if powered, otherwise false + */ + public boolean isPowered() { + return getItemType() == Material.REDSTONE_TORCH_ON; + } + + @Override + public String toString() { + return super.toString() + " " + (isPowered() ? "" : "NOT ") + "POWERED"; + } + + @Override + public RedstoneTorch clone() { + return (RedstoneTorch) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/RedstoneWire.java b/vspigot-api/src/main/java/org/bukkit/material/RedstoneWire.java new file mode 100644 index 0000000..b429af0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/RedstoneWire.java @@ -0,0 +1,63 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents redstone wire + */ +public class RedstoneWire extends MaterialData implements Redstone { + public RedstoneWire() { + super(Material.REDSTONE_WIRE); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public RedstoneWire(final int type) { + super(type); + } + + public RedstoneWire(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public RedstoneWire(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public RedstoneWire(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current state of this Material, indicating if it's powered or + * unpowered + * + * @return true if powered, otherwise false + */ + public boolean isPowered() { + return getData() > 0; + } + + @Override + public String toString() { + return super.toString() + " " + (isPowered() ? "" : "NOT ") + "POWERED"; + } + + @Override + public RedstoneWire clone() { + return (RedstoneWire) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Sandstone.java b/vspigot-api/src/main/java/org/bukkit/material/Sandstone.java new file mode 100644 index 0000000..26cbbb5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Sandstone.java @@ -0,0 +1,77 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.SandstoneType; + +/** + * Represents the different types of sandstone. + */ +public class Sandstone extends MaterialData { + public Sandstone() { + super(Material.SANDSTONE); + } + + public Sandstone(SandstoneType type) { + this(); + setType(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Sandstone(final int type) { + super(type); + } + + public Sandstone(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Sandstone(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Sandstone(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current type of this sandstone + * + * @return SandstoneType of this sandstone + */ + public SandstoneType getType() { + return SandstoneType.getByData(getData()); + } + + /** + * Sets the type of this sandstone + * + * @param type New type of this sandstone + */ + public void setType(SandstoneType type) { + setData(type.getData()); + } + + @Override + public String toString() { + return getType() + " " + super.toString(); + } + + @Override + public Sandstone clone() { + return (Sandstone) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Sign.java b/vspigot-api/src/main/java/org/bukkit/material/Sign.java new file mode 100644 index 0000000..e8da7aa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Sign.java @@ -0,0 +1,250 @@ +package org.bukkit.material; + +import org.bukkit.block.BlockFace; +import org.bukkit.Material; + +/** + * MaterialData for signs + */ +public class Sign extends MaterialData implements Attachable { + public Sign() { + super(Material.SIGN_POST); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Sign(final int type) { + super(type); + } + + public Sign(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Sign(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Sign(final Material type, final byte data) { + super(type, data); + } + + /** + * Check if this sign is attached to a wall + * + * @return true if this sign is attached to a wall, false if set on top of + * a block + */ + public boolean isWallSign() { + return getItemType() == Material.WALL_SIGN; + } + + /** + * Gets the face that this block is attached on + * + * @return BlockFace attached to + */ + public BlockFace getAttachedFace() { + if (isWallSign()) { + byte data = getData(); + + switch (data) { + case 0x2: + return BlockFace.SOUTH; + + case 0x3: + return BlockFace.NORTH; + + case 0x4: + return BlockFace.EAST; + + case 0x5: + return BlockFace.WEST; + } + + return null; + } else { + return BlockFace.DOWN; + } + } + + /** + * Gets the direction that this sign is currently facing + * + * @return BlockFace indicating where this sign is facing + */ + public BlockFace getFacing() { + byte data = getData(); + + if (!isWallSign()) { + switch (data) { + case 0x0: + return BlockFace.SOUTH; + + case 0x1: + return BlockFace.SOUTH_SOUTH_WEST; + + case 0x2: + return BlockFace.SOUTH_WEST; + + case 0x3: + return BlockFace.WEST_SOUTH_WEST; + + case 0x4: + return BlockFace.WEST; + + case 0x5: + return BlockFace.WEST_NORTH_WEST; + + case 0x6: + return BlockFace.NORTH_WEST; + + case 0x7: + return BlockFace.NORTH_NORTH_WEST; + + case 0x8: + return BlockFace.NORTH; + + case 0x9: + return BlockFace.NORTH_NORTH_EAST; + + case 0xA: + return BlockFace.NORTH_EAST; + + case 0xB: + return BlockFace.EAST_NORTH_EAST; + + case 0xC: + return BlockFace.EAST; + + case 0xD: + return BlockFace.EAST_SOUTH_EAST; + + case 0xE: + return BlockFace.SOUTH_EAST; + + case 0xF: + return BlockFace.SOUTH_SOUTH_EAST; + } + + return null; + } else { + return getAttachedFace().getOppositeFace(); + } + } + + public void setFacingDirection(BlockFace face) { + byte data; + + if (isWallSign()) { + switch (face) { + case NORTH: + data = 0x2; + break; + + case SOUTH: + data = 0x3; + break; + + case WEST: + data = 0x4; + break; + + case EAST: + default: + data = 0x5; + } + } else { + switch (face) { + case SOUTH: + data = 0x0; + break; + + case SOUTH_SOUTH_WEST: + data = 0x1; + break; + + case SOUTH_WEST: + data = 0x2; + break; + + case WEST_SOUTH_WEST: + data = 0x3; + break; + + case WEST: + data = 0x4; + break; + + case WEST_NORTH_WEST: + data = 0x5; + break; + + case NORTH_WEST: + data = 0x6; + break; + + case NORTH_NORTH_WEST: + data = 0x7; + break; + + case NORTH: + data = 0x8; + break; + + case NORTH_NORTH_EAST: + data = 0x9; + break; + + case NORTH_EAST: + data = 0xA; + break; + + case EAST_NORTH_EAST: + data = 0xB; + break; + + case EAST: + data = 0xC; + break; + + case EAST_SOUTH_EAST: + data = 0xD; + break; + + case SOUTH_SOUTH_EAST: + data = 0xF; + break; + + case SOUTH_EAST: + default: + data = 0xE; + } + } + + setData(data); + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing(); + } + + @Override + public Sign clone() { + return (Sign) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/SimpleAttachableMaterialData.java b/vspigot-api/src/main/java/org/bukkit/material/SimpleAttachableMaterialData.java new file mode 100644 index 0000000..b5703c6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/SimpleAttachableMaterialData.java @@ -0,0 +1,66 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Simple utility class for attachable MaterialData subclasses + */ +public abstract class SimpleAttachableMaterialData extends MaterialData implements Attachable { + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SimpleAttachableMaterialData(int type) { + super(type); + } + + public SimpleAttachableMaterialData(int type, BlockFace direction) { + this(type); + setFacingDirection(direction); + } + + public SimpleAttachableMaterialData(Material type, BlockFace direction) { + this(type); + setFacingDirection(direction); + } + + public SimpleAttachableMaterialData(Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SimpleAttachableMaterialData(int type, byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SimpleAttachableMaterialData(Material type, byte data) { + super(type, data); + } + + public BlockFace getFacing() { + BlockFace attachedFace = getAttachedFace(); + return attachedFace == null ? null : attachedFace.getOppositeFace(); + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing(); + } + + @Override + public SimpleAttachableMaterialData clone() { + return (SimpleAttachableMaterialData) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Skull.java b/vspigot-api/src/main/java/org/bukkit/material/Skull.java new file mode 100644 index 0000000..6e0d71f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Skull.java @@ -0,0 +1,114 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a skull. + */ +public class Skull extends MaterialData implements Directional { + public Skull() { + super(Material.SKULL); + } + + /** + * Instantiate a skull facing in a particular direction. + * + * @param direction the direction the skull's face is facing + */ + public Skull(BlockFace direction) { + this(); + setFacingDirection(direction); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Skull(final int type) { + super(type); + } + + public Skull(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Skull(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Skull(final Material type, final byte data) { + super(type, data); + } + + public void setFacingDirection(BlockFace face) { + int data; + + switch (face) { + case SELF: + default: + data = 0x1; + break; + + case NORTH: + data = 0x2; + break; + + case EAST: + data = 0x4; + break; + + case SOUTH: + data = 0x3; + break; + + case WEST: + data = 0x5; + } + + setData((byte) data); + } + + public BlockFace getFacing() { + int data = getData(); + + switch (data) { + case 0x1: + default: + return BlockFace.SELF; + + case 0x2: + return BlockFace.NORTH; + + case 0x3: + return BlockFace.SOUTH; + + case 0x4: + return BlockFace.EAST; + + case 0x5: + return BlockFace.WEST; + } + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing(); + } + + @Override + public Skull clone() { + return (Skull) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/SmoothBrick.java b/vspigot-api/src/main/java/org/bukkit/material/SmoothBrick.java new file mode 100644 index 0000000..b7ab607 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/SmoothBrick.java @@ -0,0 +1,68 @@ +package org.bukkit.material; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; + +/** + * Represents the different types of smooth bricks. + */ +public class SmoothBrick extends TexturedMaterial { + + private static final List textures = new ArrayList(); + static { + textures.add(Material.STONE); + textures.add(Material.MOSSY_COBBLESTONE); + textures.add(Material.COBBLESTONE); + textures.add(Material.SMOOTH_BRICK); + } + + public SmoothBrick() { + super(Material.SMOOTH_BRICK); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SmoothBrick(final int type) { + super(type); + } + + public SmoothBrick(final Material type) { + super((textures.contains(type)) ? Material.SMOOTH_BRICK : type); + if (textures.contains(type)) { + setMaterial(type); + } + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SmoothBrick(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SmoothBrick(final Material type, final byte data) { + super(type, data); + } + + @Override + public List getTextures() { + return textures; + } + + @Override + public SmoothBrick clone() { + return (SmoothBrick) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/SpawnEgg.java b/vspigot-api/src/main/java/org/bukkit/material/SpawnEgg.java new file mode 100644 index 0000000..ed973c5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/SpawnEgg.java @@ -0,0 +1,65 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + +/** + * Represents a spawn egg that can be used to spawn mobs + */ +public class SpawnEgg extends MaterialData { + + public SpawnEgg() { + super(Material.MONSTER_EGG); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SpawnEgg(int type, byte data){ + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public SpawnEgg(byte data) { + super(Material.MONSTER_EGG, data); + } + + public SpawnEgg(EntityType type) { + this(); + setSpawnedType(type); + } + + /** + * Get the type of entity this egg will spawn. + * + * @return The entity type. + */ + public EntityType getSpawnedType() { + return EntityType.fromId(getData()); + } + + /** + * Set the type of entity this egg will spawn. + * + * @param type The entity type. + */ + public void setSpawnedType(EntityType type) { + setData((byte) type.getTypeId()); + } + + @Override + public String toString() { + return "SPAWN EGG{" + getSpawnedType() + "}"; + } + + @Override + public SpawnEgg clone() { + return (SpawnEgg) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Stairs.java b/vspigot-api/src/main/java/org/bukkit/material/Stairs.java new file mode 100644 index 0000000..1f73c77 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Stairs.java @@ -0,0 +1,138 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents stairs. + */ +public class Stairs extends MaterialData implements Directional { + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Stairs(final int type) { + super(type); + } + + public Stairs(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Stairs(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Stairs(final Material type, final byte data) { + super(type, data); + } + + /** + * @return the direction the stairs ascend towards + */ + public BlockFace getAscendingDirection() { + byte data = getData(); + + switch (data & 0x3) { + case 0x0: + default: + return BlockFace.EAST; + + case 0x1: + return BlockFace.WEST; + + case 0x2: + return BlockFace.SOUTH; + + case 0x3: + return BlockFace.NORTH; + } + } + + /** + * @return the direction the stairs descend towards + */ + public BlockFace getDescendingDirection() { + return getAscendingDirection().getOppositeFace(); + } + + /** + * Set the direction the stair part of the block is facing + */ + public void setFacingDirection(BlockFace face) { + byte data; + + switch (face) { + case NORTH: + data = 0x3; + break; + + case SOUTH: + data = 0x2; + break; + + case EAST: + default: + data = 0x0; + break; + + case WEST: + data = 0x1; + break; + } + + setData((byte) ((getData() & 0xC) | data)); + } + + /** + * @return the direction the stair part of the block is facing + */ + public BlockFace getFacing() { + return getDescendingDirection(); + } + + /** + * Test if step is inverted + * + * @return true if inverted (top half), false if normal (bottom half) + */ + public boolean isInverted() { + return ((getData() & 0x4) != 0); + } + + /** + * Set step inverted state + * + * @param inv - true if step is inverted (top half), false if step is + * normal (bottom half) + */ + public void setInverted(boolean inv) { + int dat = getData() & 0x3; + if (inv) { + dat |= 0x4; + } + setData((byte) dat); + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing() + (isInverted()?" inverted":""); + } + + @Override + public Stairs clone() { + return (Stairs) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Step.java b/vspigot-api/src/main/java/org/bukkit/material/Step.java new file mode 100644 index 0000000..605f817 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Step.java @@ -0,0 +1,119 @@ +package org.bukkit.material; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; + +/** + * Represents the different types of steps. + */ +public class Step extends TexturedMaterial { + private static final List textures = new ArrayList(); + static { + textures.add(Material.STONE); + textures.add(Material.SANDSTONE); + textures.add(Material.WOOD); + textures.add(Material.COBBLESTONE); + textures.add(Material.BRICK); + textures.add(Material.SMOOTH_BRICK); + textures.add(Material.NETHER_BRICK); + textures.add(Material.QUARTZ_BLOCK); + } + + public Step() { + super(Material.STEP); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Step(final int type) { + super(type); + } + + public Step(final Material type) { + super((textures.contains(type)) ? Material.STEP : type); + if (textures.contains(type)) { + setMaterial(type); + } + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Step(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Step(final Material type, final byte data) { + super(type, data); + } + + @Override + public List getTextures() { + return textures; + } + + /** + * Test if step is inverted + * + * @return true if inverted (top half), false if normal (bottom half) + */ + public boolean isInverted() { + return ((getData() & 0x8) != 0); + } + + /** + * Set step inverted state + * + * @param inv - true if step is inverted (top half), false if step is + * normal (bottom half) + */ + public void setInverted(boolean inv) { + int dat = getData() & 0x7; + if (inv) { + dat |= 0x8; + } + setData((byte) dat); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + @Override + protected int getTextureIndex() { + return getData() & 0x7; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + @Override + protected void setTextureIndex(int idx) { + setData((byte) ((getData() & 0x8) | idx)); + } + + @Override + public Step clone() { + return (Step) super.clone(); + } + + @Override + public String toString() { + return super.toString() + (isInverted()?"inverted":""); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/TexturedMaterial.java b/vspigot-api/src/main/java/org/bukkit/material/TexturedMaterial.java new file mode 100644 index 0000000..cfd971b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/TexturedMaterial.java @@ -0,0 +1,110 @@ +package org.bukkit.material; + +import java.util.List; + +import org.bukkit.Material; + +/** + * Represents textured materials like steps and smooth bricks + */ +public abstract class TexturedMaterial extends MaterialData { + + public TexturedMaterial(Material m) { + super(m); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TexturedMaterial(int type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TexturedMaterial(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TexturedMaterial(final Material type, final byte data) { + super(type, data); + } + + /** + * Retrieve a list of possible textures. The first element of the list + * will be used as a default. + * + * @return a list of possible textures for this block + */ + public abstract List getTextures(); + + /** + * Gets the current Material this block is made of + * + * @return Material of this block + */ + public Material getMaterial() { + int n = getTextureIndex(); + if (n > getTextures().size() - 1) { + n = 0; + } + + return getTextures().get(n); + } + + /** + * Sets the material this block is made of + * + * @param material + * New material of this block + */ + public void setMaterial(Material material) { + if (getTextures().contains(material)) { + setTextureIndex(getTextures().indexOf(material)); + } else { + setTextureIndex(0x0); + } + } + + /** + * Get material index from data + * + * @return index of data in textures list + * @deprecated Magic value + */ + @Deprecated + protected int getTextureIndex() { + return getData(); // Default to using all bits - override for other mappings + } + + /** + * Set material index + * + * @param idx - index of data in textures list + * @deprecated Magic value + */ + @Deprecated + protected void setTextureIndex(int idx) { + setData((byte) idx); // Defult to using all bits - override for other mappings + } + + @Override + public String toString() { + return getMaterial() + " " + super.toString(); + } + + @Override + public TexturedMaterial clone() { + return (TexturedMaterial) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Torch.java b/vspigot-api/src/main/java/org/bukkit/material/Torch.java new file mode 100644 index 0000000..f03b3cf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Torch.java @@ -0,0 +1,104 @@ +package org.bukkit.material; + +import org.bukkit.block.BlockFace; +import org.bukkit.Material; + +/** + * MaterialData for torches + */ +public class Torch extends SimpleAttachableMaterialData { + public Torch() { + super(Material.TORCH); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Torch(final int type) { + super(type); + } + + public Torch(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Torch(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Torch(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the face that this block is attached on + * + * @return BlockFace attached to + */ + public BlockFace getAttachedFace() { + byte data = getData(); + + switch (data) { + case 0x1: + return BlockFace.WEST; + + case 0x2: + return BlockFace.EAST; + + case 0x3: + return BlockFace.NORTH; + + case 0x4: + return BlockFace.SOUTH; + + case 0x5: + default: + return BlockFace.DOWN; + } + } + + public void setFacingDirection(BlockFace face) { + byte data; + + switch (face) { + case EAST: + data = 0x1; + break; + + case WEST: + data = 0x2; + break; + + case SOUTH: + data = 0x3; + break; + + case NORTH: + data = 0x4; + break; + + case UP: + default: + data = 0x5; + } + + setData(data); + } + + @Override + public Torch clone() { + return (Torch) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/TrapDoor.java b/vspigot-api/src/main/java/org/bukkit/material/TrapDoor.java new file mode 100644 index 0000000..bd4bcc2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/TrapDoor.java @@ -0,0 +1,131 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a trap door + */ +public class TrapDoor extends SimpleAttachableMaterialData implements Openable { + public TrapDoor() { + super(Material.TRAP_DOOR); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TrapDoor(final int type) { + super(type); + } + + public TrapDoor(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TrapDoor(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TrapDoor(final Material type, final byte data) { + super(type, data); + } + + public boolean isOpen() { + return ((getData() & 0x4) == 0x4); + } + + public void setOpen(boolean isOpen) { + byte data = getData(); + + if (isOpen) { + data |= 0x4; + } else { + data &= ~0x4; + } + + setData(data); + } + + /** + * Test if trapdoor is inverted + * + * @return true if inverted (top half), false if normal (bottom half) + */ + public boolean isInverted() { + return ((getData() & 0x8) != 0); + } + + /** + * Set trapdoor inverted state + * + * @param inv - true if inverted (top half), false if normal (bottom half) + */ + public void setInverted(boolean inv) { + int dat = getData() & 0x7; + if (inv) { + dat |= 0x8; + } + setData((byte) dat); + } + + public BlockFace getAttachedFace() { + byte data = (byte) (getData() & 0x3); + + switch (data) { + case 0x0: + return BlockFace.SOUTH; + + case 0x1: + return BlockFace.NORTH; + + case 0x2: + return BlockFace.EAST; + + case 0x3: + return BlockFace.WEST; + } + + return null; + + } + + public void setFacingDirection(BlockFace face) { + byte data = (byte) (getData() & 0xC); + + switch (face) { + case SOUTH: + data |= 0x1; + break; + case WEST: + data |= 0x2; + break; + case EAST: + data |= 0x3; + break; + } + + setData(data); + } + + @Override + public String toString() { + return (isOpen() ? "OPEN " : "CLOSED ") + super.toString() + " with hinges set " + getAttachedFace() + (isInverted() ? " inverted" : ""); + } + + @Override + public TrapDoor clone() { + return (TrapDoor) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Tree.java b/vspigot-api/src/main/java/org/bukkit/material/Tree.java new file mode 100644 index 0000000..fe56872 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Tree.java @@ -0,0 +1,136 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.TreeSpecies; +import org.bukkit.block.BlockFace; + +/** + * Represents the different types of Trees. + */ +public class Tree extends MaterialData { + public Tree() { + super(Material.LOG); + } + + public Tree(TreeSpecies species) { + this(); + setSpecies(species); + } + + public Tree(TreeSpecies species, BlockFace dir) { + this(); + setSpecies(species); + setDirection(dir); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Tree(final int type) { + super(type); + } + + public Tree(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Tree(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Tree(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current species of this tree + * + * @return TreeSpecies of this tree + */ + public TreeSpecies getSpecies() { + return TreeSpecies.getByData((byte) (getData() & 0x3)); + } + + /** + * Sets the species of this tree + * + * @param species New species of this tree + */ + public void setSpecies(TreeSpecies species) { + setData((byte) ((getData() & 0xC) | species.getData())); + } + + /** + * Get direction of the log + * + * @return one of: + *

            + *
          • BlockFace.TOP for upright (default) + *
          • BlockFace.NORTH (east-west) + *
          • BlockFace.WEST (north-south) + *
          • BlockFace.SELF (directionless) + *
          + */ + public BlockFace getDirection() { + switch ((getData() >> 2) & 0x3) { + case 0: // Up-down + default: + return BlockFace.UP; + case 1: // North-south + return BlockFace.WEST; + case 2: // East-west + return BlockFace.NORTH; + case 3: // Directionless (bark on all sides) + return BlockFace.SELF; + } + } + /** + * Set direction of the log + * + * @param dir - direction of end of log (BlockFace.SELF for no direction) + */ + public void setDirection(BlockFace dir) { + int dat; + switch (dir) { + case UP: + case DOWN: + default: + dat = 0; + break; + case WEST: + case EAST: + dat = 1; + break; + case NORTH: + case SOUTH: + dat = 2; + break; + case SELF: + dat = 3; + break; + } + setData((byte) ((getData() & 0x3) | (dat << 2))); + } + + @Override + public String toString() { + return getSpecies() + " " + getDirection() + " " + super.toString(); + } + + @Override + public Tree clone() { + return (Tree) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Tripwire.java b/vspigot-api/src/main/java/org/bukkit/material/Tripwire.java new file mode 100644 index 0000000..583860b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Tripwire.java @@ -0,0 +1,85 @@ +package org.bukkit.material; + +import org.bukkit.Material; + +/** + * Represents the tripwire + */ +public class Tripwire extends MaterialData { + + public Tripwire() { + super(Material.TRIPWIRE); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Tripwire(final int type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Tripwire(final int type, final byte data) { + super(type, data); + } + + /** + * Test if tripwire is currently activated + * + * @return true if activated, false if not + */ + public boolean isActivated() { + return (getData() & 0x4) != 0; + } + + /** + * Set tripwire activated state + * + * @param act - true if activated, false if not + */ + public void setActivated(boolean act) { + int dat = getData() & (0x8 | 0x3); + if (act) { + dat |= 0x4; + } + setData((byte) dat); + } + + /** + * Test if object triggering this tripwire directly + * + * @return true if object activating tripwire, false if not + */ + public boolean isObjectTriggering() { + return (getData() & 0x1) != 0; + } + + /** + * Set object triggering state for this tripwire + * + * @param trig - true if object activating tripwire, false if not + */ + public void setObjectTriggering(boolean trig) { + int dat = getData() & 0xE; + if (trig) { + dat |= 0x1; + } + setData((byte) dat); + } + + @Override + public Tripwire clone() { + return (Tripwire) super.clone(); + } + + @Override + public String toString() { + return super.toString() + (isActivated()?" Activated":"") + (isObjectTriggering()?" Triggered":""); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/TripwireHook.java b/vspigot-api/src/main/java/org/bukkit/material/TripwireHook.java new file mode 100644 index 0000000..7ad2d2a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/TripwireHook.java @@ -0,0 +1,128 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents the tripwire hook + */ +public class TripwireHook extends SimpleAttachableMaterialData implements Redstone { + + public TripwireHook() { + super(Material.TRIPWIRE_HOOK); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TripwireHook(final int type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public TripwireHook(final int type, final byte data) { + super(type, data); + } + + public TripwireHook(BlockFace dir) { + this(); + setFacingDirection(dir); + } + + /** + * Test if tripwire is connected + * + * @return true if connected, false if not + */ + public boolean isConnected() { + return (getData() & 0x4) != 0; + } + + /** + * Set tripwire connection state + * + * @param connected - true if connected, false if not + */ + public void setConnected(boolean connected) { + int dat = getData() & (0x8 | 0x3); + if (connected) { + dat |= 0x4; + } + setData((byte) dat); + } + + /** + * Test if hook is currently activated + * + * @return true if activated, false if not + */ + public boolean isActivated() { + return (getData() & 0x8) != 0; + } + + /** + * Set hook activated state + * + * @param act - true if activated, false if not + */ + public void setActivated(boolean act) { + int dat = getData() & (0x4 | 0x3); + if (act) { + dat |= 0x8; + } + setData((byte) dat); + } + + public void setFacingDirection(BlockFace face) { + int dat = getData() & 0xC; + switch (face) { + case WEST: + dat |= 0x1; + break; + case NORTH: + dat |= 0x2; + break; + case EAST: + dat |= 0x3; + break; + case SOUTH: + default: + break; + } + setData((byte) dat); + } + + public BlockFace getAttachedFace() { + switch (getData() & 0x3) { + case 0: + return BlockFace.NORTH; + case 1: + return BlockFace.EAST; + case 2: + return BlockFace.SOUTH; + case 3: + return BlockFace.WEST; + } + return null; + } + + public boolean isPowered() { + return isActivated(); + } + + @Override + public TripwireHook clone() { + return (TripwireHook) super.clone(); + } + + @Override + public String toString() { + return super.toString() + " facing " + getFacing() + (isActivated()?" Activated":"") + (isConnected()?" Connected":""); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Vine.java b/vspigot-api/src/main/java/org/bukkit/material/Vine.java new file mode 100644 index 0000000..a4f7ad5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Vine.java @@ -0,0 +1,196 @@ +package org.bukkit.material; + +import java.util.Arrays; +import java.util.EnumSet; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; + +/** + * Represents a vine + */ +public class Vine extends MaterialData { + private static final int VINE_NORTH = 0x4; + private static final int VINE_EAST = 0x8; + private static final int VINE_WEST = 0x2; + private static final int VINE_SOUTH = 0x1; + EnumSet possibleFaces = EnumSet.of(BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST); + + public Vine() { + super(Material.VINE); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Vine(int type, byte data){ + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Vine(byte data) { + super(Material.VINE, data); + } + + public Vine(BlockFace... faces) { + this(EnumSet.copyOf(Arrays.asList(faces))); + } + + public Vine(EnumSet faces) { + this((byte) 0); + faces.retainAll(possibleFaces); + + byte data = 0; + + if (faces.contains(BlockFace.WEST)) { + data |= VINE_WEST; + } + + if (faces.contains(BlockFace.NORTH)) { + data |= VINE_NORTH; + } + + if (faces.contains(BlockFace.SOUTH)) { + data |= VINE_SOUTH; + } + + if (faces.contains(BlockFace.EAST)) { + data |= VINE_EAST; + } + + setData(data); + } + + /** + * Check if the vine is attached to the specified face of an adjacent + * block. You can check two faces at once by passing e.g. {@link + * BlockFace#NORTH_EAST}. + * + * @param face The face to check. + * @return Whether it is attached to that face. + */ + public boolean isOnFace(BlockFace face) { + switch (face) { + case WEST: + return (getData() & VINE_WEST) == VINE_WEST; + case NORTH: + return (getData() & VINE_NORTH) == VINE_NORTH; + case SOUTH: + return (getData() & VINE_SOUTH) == VINE_SOUTH; + case EAST: + return (getData() & VINE_EAST) == VINE_EAST; + case NORTH_EAST: + return isOnFace(BlockFace.EAST) && isOnFace(BlockFace.NORTH); + case NORTH_WEST: + return isOnFace(BlockFace.WEST) && isOnFace(BlockFace.NORTH); + case SOUTH_EAST: + return isOnFace(BlockFace.EAST) && isOnFace(BlockFace.SOUTH); + case SOUTH_WEST: + return isOnFace(BlockFace.WEST) && isOnFace(BlockFace.SOUTH); + case UP: // It's impossible to be accurate with this since it's contextual + return true; + default: + return false; + } + } + + /** + * Attach the vine to the specified face of an adjacent block. + * + * @param face The face to attach. + */ + public void putOnFace(BlockFace face) { + switch(face) { + case WEST: + setData((byte) (getData() | VINE_WEST)); + break; + case NORTH: + setData((byte) (getData() | VINE_NORTH)); + break; + case SOUTH: + setData((byte) (getData() | VINE_SOUTH)); + break; + case EAST: + setData((byte) (getData() | VINE_EAST)); + break; + case NORTH_WEST: + putOnFace(BlockFace.WEST); + putOnFace(BlockFace.NORTH); + break; + case SOUTH_WEST: + putOnFace(BlockFace.WEST); + putOnFace(BlockFace.SOUTH); + break; + case NORTH_EAST: + putOnFace(BlockFace.EAST); + putOnFace(BlockFace.NORTH); + break; + case SOUTH_EAST: + putOnFace(BlockFace.EAST); + putOnFace(BlockFace.SOUTH); + break; + case UP: + break; + default: + throw new IllegalArgumentException("Vines can't go on face " + face.toString()); + } + } + + /** + * Detach the vine from the specified face of an adjacent block. + * + * @param face The face to detach. + */ + public void removeFromFace(BlockFace face) { + switch(face) { + case WEST: + setData((byte) (getData() & ~VINE_WEST)); + break; + case NORTH: + setData((byte) (getData() & ~VINE_NORTH)); + break; + case SOUTH: + setData((byte) (getData() & ~VINE_SOUTH)); + break; + case EAST: + setData((byte) (getData() & ~VINE_EAST)); + break; + case NORTH_WEST: + removeFromFace(BlockFace.WEST); + removeFromFace(BlockFace.NORTH); + break; + case SOUTH_WEST: + removeFromFace(BlockFace.WEST); + removeFromFace(BlockFace.SOUTH); + break; + case NORTH_EAST: + removeFromFace(BlockFace.EAST); + removeFromFace(BlockFace.NORTH); + break; + case SOUTH_EAST: + removeFromFace(BlockFace.EAST); + removeFromFace(BlockFace.SOUTH); + break; + case UP: + break; + default: + throw new IllegalArgumentException("Vines can't go on face " + face.toString()); + } + } + + @Override + public String toString() { + return "VINE"; + } + + @Override + public Vine clone() { + return (Vine) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/WoodenStep.java b/vspigot-api/src/main/java/org/bukkit/material/WoodenStep.java new file mode 100644 index 0000000..9584e25 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/WoodenStep.java @@ -0,0 +1,103 @@ +package org.bukkit.material; + +import org.bukkit.Material; +import org.bukkit.TreeSpecies; + +/** + * Represents the different types of wooden steps. + */ +public class WoodenStep extends MaterialData { + + public WoodenStep() { + super(Material.WOOD_STEP); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public WoodenStep(final int type) { + super(type); + } + + public WoodenStep(TreeSpecies species) { + this(); + setSpecies(species); + } + + public WoodenStep(TreeSpecies species, boolean inv) { + this(); + setSpecies(species); + setInverted(inv); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public WoodenStep(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public WoodenStep(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current species of this tree + * + * @return TreeSpecies of this tree + */ + public TreeSpecies getSpecies() { + return TreeSpecies.getByData((byte) (getData() & 0x3)); + } + + /** + * Sets the species of this tree + * + * @param species New species of this tree + */ + public void setSpecies(TreeSpecies species) { + setData((byte) ((getData() & 0xC) | species.getData())); + } + + /** + * Test if step is inverted + * + * @return true if inverted (top half), false if normal (bottom half) + */ + public boolean isInverted() { + return ((getData() & 0x8) != 0); + } + + /** + * Set step inverted state + * + * @param inv - true if step is inverted (top half), false if step is + * normal (bottom half) + */ + public void setInverted(boolean inv) { + int dat = getData() & 0x7; + if (inv) { + dat |= 0x8; + } + setData((byte) dat); + } + + @Override + public WoodenStep clone() { + return (WoodenStep) super.clone(); + } + + @Override + public String toString() { + return super.toString() + " " + getSpecies() + (isInverted()?" inverted":""); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/material/Wool.java b/vspigot-api/src/main/java/org/bukkit/material/Wool.java new file mode 100644 index 0000000..8115bdc --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/material/Wool.java @@ -0,0 +1,77 @@ +package org.bukkit.material; + +import org.bukkit.DyeColor; +import org.bukkit.Material; + +/** + * Represents a Wool/Cloth block + */ +public class Wool extends MaterialData implements Colorable { + public Wool() { + super(Material.WOOL); + } + + public Wool(DyeColor color) { + this(); + setColor(color); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Wool(final int type) { + super(type); + } + + public Wool(final Material type) { + super(type); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Wool(final int type, final byte data) { + super(type, data); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public Wool(final Material type, final byte data) { + super(type, data); + } + + /** + * Gets the current color of this dye + * + * @return DyeColor of this dye + */ + public DyeColor getColor() { + return DyeColor.getByWoolData(getData()); + } + + /** + * Sets the color of this dye + * + * @param color New color of this dye + */ + public void setColor(DyeColor color) { + setData(color.getWoolData()); + } + + @Override + public String toString() { + return getColor() + " " + super.toString(); + } + + @Override + public Wool clone() { + return (Wool) super.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/FixedMetadataValue.java b/vspigot-api/src/main/java/org/bukkit/metadata/FixedMetadataValue.java new file mode 100644 index 0000000..bce6f00 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/FixedMetadataValue.java @@ -0,0 +1,43 @@ +package org.bukkit.metadata; + +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.Callable; + +/** + * A FixedMetadataValue is a special case metadata item that contains the same + * value forever after initialization. Invalidating a FixedMetadataValue has + * no effect. + *

          + * This class extends LazyMetadataValue for historical reasons, even though it + * overrides all the implementation methods. it is possible that in the future + * that the inheritance hierarchy may change. + */ +public class FixedMetadataValue extends LazyMetadataValue { + + /** + * Store the internal value that is represented by this fixed value. + */ + private final Object internalValue; + + /** + * Initializes a FixedMetadataValue with an Object + * + * @param owningPlugin the {@link Plugin} that created this metadata value + * @param value the value assigned to this metadata value + */ + public FixedMetadataValue(Plugin owningPlugin, final Object value) { + super(owningPlugin); + this.internalValue = value; + } + + @Override + public void invalidate() { + + } + + @Override + public Object value() { + return internalValue; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/LazyMetadataValue.java b/vspigot-api/src/main/java/org/bukkit/metadata/LazyMetadataValue.java new file mode 100644 index 0000000..a9546e5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/LazyMetadataValue.java @@ -0,0 +1,120 @@ +package org.bukkit.metadata; + +import java.lang.ref.SoftReference; +import java.util.concurrent.Callable; + +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.Plugin; + +/** + * The LazyMetadataValue class implements a type of metadata that is not + * computed until another plugin asks for it. + *

          + * By making metadata values lazy, no computation is done by the providing + * plugin until absolutely necessary (if ever). Additionally, + * LazyMetadataValue objects cache their values internally unless overridden + * by a {@link CacheStrategy} or invalidated at the individual or plugin + * level. Once invalidated, the LazyMetadataValue will recompute its value + * when asked. + */ +public class LazyMetadataValue extends MetadataValueAdapter implements MetadataValue { + private Callable lazyValue; + private CacheStrategy cacheStrategy; + private SoftReference internalValue; + private static final Object ACTUALLY_NULL = new Object(); + + /** + * Initialized a LazyMetadataValue object with the default + * CACHE_AFTER_FIRST_EVAL cache strategy. + * + * @param owningPlugin the {@link Plugin} that created this metadata + * value. + * @param lazyValue the lazy value assigned to this metadata value. + */ + public LazyMetadataValue(Plugin owningPlugin, Callable lazyValue) { + this(owningPlugin, CacheStrategy.CACHE_AFTER_FIRST_EVAL, lazyValue); + } + + /** + * Initializes a LazyMetadataValue object with a specific cache strategy. + * + * @param owningPlugin the {@link Plugin} that created this metadata + * value. + * @param cacheStrategy determines the rules for caching this metadata + * value. + * @param lazyValue the lazy value assigned to this metadata value. + */ + public LazyMetadataValue(Plugin owningPlugin, CacheStrategy cacheStrategy, Callable lazyValue) { + super(owningPlugin); + Validate.notNull(cacheStrategy, "cacheStrategy cannot be null"); + Validate.notNull(lazyValue, "lazyValue cannot be null"); + this.internalValue = new SoftReference(null); + this.lazyValue = lazyValue; + this.cacheStrategy = cacheStrategy; + } + + /** + * Protected special constructor used by FixedMetadataValue to bypass + * standard setup. + */ + protected LazyMetadataValue(Plugin owningPlugin) { + super(owningPlugin); + } + + public Object value() { + eval(); + Object value = internalValue.get(); + if (value == ACTUALLY_NULL) { + return null; + } + return value; + } + + /** + * Lazily evaluates the value of this metadata item. + * + * @throws MetadataEvaluationException if computing the metadata value + * fails. + */ + private synchronized void eval() throws MetadataEvaluationException { + if (cacheStrategy == CacheStrategy.NEVER_CACHE || internalValue.get() == null) { + try { + Object value = lazyValue.call(); + if (value == null) { + value = ACTUALLY_NULL; + } + internalValue = new SoftReference(value); + } catch (Exception e) { + throw new MetadataEvaluationException(e); + } + } + } + + public synchronized void invalidate() { + if (cacheStrategy != CacheStrategy.CACHE_ETERNALLY) { + internalValue.clear(); + } + } + + /** + * Describes possible caching strategies for metadata. + */ + public enum CacheStrategy { + /** + * Once the metadata value has been evaluated, do not re-evaluate the + * value until it is manually invalidated. + */ + CACHE_AFTER_FIRST_EVAL, + + /** + * Re-evaluate the metadata item every time it is requested + */ + NEVER_CACHE, + + /** + * Once the metadata value has been evaluated, do not re-evaluate the + * value in spite of manual invalidation. + */ + CACHE_ETERNALLY + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/MetadataConversionException.java b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataConversionException.java new file mode 100644 index 0000000..a3def46 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataConversionException.java @@ -0,0 +1,13 @@ +package org.bukkit.metadata; + +/** + * A MetadataConversionException is thrown any time a {@link + * LazyMetadataValue} attempts to convert a metadata value to an inappropriate + * data type. + */ +@SuppressWarnings("serial") +public class MetadataConversionException extends RuntimeException { + MetadataConversionException(String message) { + super(message); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/MetadataEvaluationException.java b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataEvaluationException.java new file mode 100644 index 0000000..918e7c8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataEvaluationException.java @@ -0,0 +1,13 @@ +package org.bukkit.metadata; + +/** + * A MetadataEvaluationException is thrown any time a {@link + * LazyMetadataValue} fails to evaluate its value due to an exception. The + * originating exception will be included as this exception's cause. + */ +@SuppressWarnings("serial") +public class MetadataEvaluationException extends RuntimeException { + MetadataEvaluationException(Throwable cause) { + super(cause); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/MetadataStore.java b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataStore.java new file mode 100644 index 0000000..700d0bf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataStore.java @@ -0,0 +1,60 @@ +package org.bukkit.metadata; + +import org.bukkit.plugin.Plugin; + +import java.util.List; + +public interface MetadataStore { + /** + * Adds a metadata value to an object. + * + * @param subject The object receiving the metadata. + * @param metadataKey A unique key to identify this metadata. + * @param newMetadataValue The metadata value to apply. + * @throws IllegalArgumentException If value is null, or the owning plugin + * is null + */ + public void setMetadata(T subject, String metadataKey, MetadataValue newMetadataValue); + + /** + * Returns all metadata values attached to an object. If multiple plugins + * have attached metadata, each will value will be included. + * + * @param subject the object being interrogated. + * @param metadataKey the unique metadata key being sought. + * @return A list of values, one for each plugin that has set the + * requested value. + */ + public List getMetadata(T subject, String metadataKey); + + /** + * Tests to see if a metadata attribute has been set on an object. + * + * @param subject the object upon which the has-metadata test is + * performed. + * @param metadataKey the unique metadata key being queried. + * @return the existence of the metadataKey within subject. + */ + public boolean hasMetadata(T subject, String metadataKey); + + /** + * Removes a metadata item owned by a plugin from a subject. + * + * @param subject the object to remove the metadata from. + * @param metadataKey the unique metadata key identifying the metadata to + * remove. + * @param owningPlugin the plugin attempting to remove a metadata item. + * @throws IllegalArgumentException If plugin is null + */ + public void removeMetadata(T subject, String metadataKey, Plugin owningPlugin); + + /** + * Invalidates all metadata in the metadata store that originates from the + * given plugin. Doing this will force each invalidated metadata item to + * be recalculated the next time it is accessed. + * + * @param owningPlugin the plugin requesting the invalidation. + * @throws IllegalArgumentException If plugin is null + */ + public void invalidateAll(Plugin owningPlugin); +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataStoreBase.java new file mode 100644 index 0000000..093c144 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataStoreBase.java @@ -0,0 +1,136 @@ +package org.bukkit.metadata; + +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.Plugin; + +import java.util.*; + +public abstract class MetadataStoreBase { + private Map> metadataMap = new HashMap>(); + + /** + * Adds a metadata value to an object. Each metadata value is owned by a + * specific {@link Plugin}. If a plugin has already added a metadata value + * to an object, that value will be replaced with the value of {@code + * newMetadataValue}. Multiple plugins can set independent values for the + * same {@code metadataKey} without conflict. + *

          + * Implementation note: I considered using a {@link + * java.util.concurrent.locks.ReadWriteLock} for controlling access to + * {@code metadataMap}, but decided that the added overhead wasn't worth + * the finer grained access control. + *

          + * Bukkit is almost entirely single threaded so locking overhead shouldn't + * pose a problem. + * + * @param subject The object receiving the metadata. + * @param metadataKey A unique key to identify this metadata. + * @param newMetadataValue The metadata value to apply. + * @see MetadataStore#setMetadata(Object, String, MetadataValue) + * @throws IllegalArgumentException If value is null, or the owning plugin + * is null + */ + public synchronized void setMetadata(T subject, String metadataKey, MetadataValue newMetadataValue) { + Validate.notNull(newMetadataValue, "Value cannot be null"); + Plugin owningPlugin = newMetadataValue.getOwningPlugin(); + Validate.notNull(owningPlugin, "Plugin cannot be null"); + String key = disambiguate(subject, metadataKey); + Map entry = metadataMap.get(key); + if (entry == null) { + entry = new WeakHashMap(1); + metadataMap.put(key, entry); + } + entry.put(owningPlugin, newMetadataValue); + } + + /** + * Returns all metadata values attached to an object. If multiple + * have attached metadata, each will value will be included. + * + * @param subject the object being interrogated. + * @param metadataKey the unique metadata key being sought. + * @return A list of values, one for each plugin that has set the + * requested value. + * @see MetadataStore#getMetadata(Object, String) + */ + public synchronized List getMetadata(T subject, String metadataKey) { + String key = disambiguate(subject, metadataKey); + if (metadataMap.containsKey(key)) { + Collection values = metadataMap.get(key).values(); + return Collections.unmodifiableList(new ArrayList(values)); + } else { + return Collections.emptyList(); + } + } + + /** + * Tests to see if a metadata attribute has been set on an object. + * + * @param subject the object upon which the has-metadata test is + * performed. + * @param metadataKey the unique metadata key being queried. + * @return the existence of the metadataKey within subject. + */ + public synchronized boolean hasMetadata(T subject, String metadataKey) { + String key = disambiguate(subject, metadataKey); + return metadataMap.containsKey(key); + } + + /** + * Removes a metadata item owned by a plugin from a subject. + * + * @param subject the object to remove the metadata from. + * @param metadataKey the unique metadata key identifying the metadata to + * remove. + * @param owningPlugin the plugin attempting to remove a metadata item. + * @see MetadataStore#removeMetadata(Object, String, + * org.bukkit.plugin.Plugin) + * @throws IllegalArgumentException If plugin is null + */ + public synchronized void removeMetadata(T subject, String metadataKey, Plugin owningPlugin) { + Validate.notNull(owningPlugin, "Plugin cannot be null"); + String key = disambiguate(subject, metadataKey); + Map entry = metadataMap.get(key); + if (entry == null) { + return; + } + + entry.remove(owningPlugin); + if (entry.isEmpty()) { + metadataMap.remove(key); + } + } + + /** + * Invalidates all metadata in the metadata store that originates from the + * given plugin. Doing this will force each invalidated metadata item to + * be recalculated the next time it is accessed. + * + * @param owningPlugin the plugin requesting the invalidation. + * @see MetadataStore#invalidateAll(org.bukkit.plugin.Plugin) + * @throws IllegalArgumentException If plugin is null + */ + public synchronized void invalidateAll(Plugin owningPlugin) { + Validate.notNull(owningPlugin, "Plugin cannot be null"); + for (Map values : metadataMap.values()) { + if (values.containsKey(owningPlugin)) { + values.get(owningPlugin).invalidate(); + } + } + } + + /** + * Creates a unique name for the object receiving metadata by combining + * unique data from the subject with a metadataKey. + *

          + * The name created must be globally unique for the given object and any + * two equivalent objects must generate the same unique name. For example, + * two Player objects must generate the same string if they represent the + * same player, even if the objects would fail a reference equality test. + * + * @param subject The object for which this key is being generated. + * @param metadataKey The name identifying the metadata value. + * @return a unique metadata key for the given subject. + */ + protected abstract String disambiguate(T subject, String metadataKey); +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/MetadataValue.java b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataValue.java new file mode 100644 index 0000000..eded8c0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataValue.java @@ -0,0 +1,83 @@ +package org.bukkit.metadata; + +import org.bukkit.plugin.Plugin; + +public interface MetadataValue { + + /** + * Fetches the value of this metadata item. + * + * @return the metadata value. + */ + public Object value(); + + /** + * Attempts to convert the value of this metadata item into an int. + * + * @return the value as an int. + */ + public int asInt(); + + /** + * Attempts to convert the value of this metadata item into a float. + * + * @return the value as a float. + */ + public float asFloat(); + + /** + * Attempts to convert the value of this metadata item into a double. + * + * @return the value as a double. + */ + public double asDouble(); + + /** + * Attempts to convert the value of this metadata item into a long. + * + * @return the value as a long. + */ + public long asLong(); + + /** + * Attempts to convert the value of this metadata item into a short. + * + * @return the value as a short. + */ + public short asShort(); + + /** + * Attempts to convert the value of this metadata item into a byte. + * + * @return the value as a byte. + */ + public byte asByte(); + + /** + * Attempts to convert the value of this metadata item into a boolean. + * + * @return the value as a boolean. + */ + public boolean asBoolean(); + + /** + * Attempts to convert the value of this metadata item into a string. + * + * @return the value as a string. + */ + public String asString(); + + /** + * Returns the {@link Plugin} that created this metadata item. + * + * @return the plugin that owns this metadata value. This should never be + * null. + */ + public Plugin getOwningPlugin(); + + /** + * Invalidates this metadata item, forcing it to recompute when next + * accessed. + */ + public void invalidate(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/MetadataValueAdapter.java b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataValueAdapter.java new file mode 100644 index 0000000..bbc3da8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/MetadataValueAdapter.java @@ -0,0 +1,78 @@ +package org.bukkit.metadata; + +import java.lang.ref.WeakReference; + +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.NumberConversions; + +/** + * Optional base class for facilitating MetadataValue implementations. + *

          + * This provides all the conversion functions for MetadataValue so that + * writing an implementation of MetadataValue is as simple as implementing + * value() and invalidate(). + */ +public abstract class MetadataValueAdapter implements MetadataValue { + protected final WeakReference owningPlugin; + + protected MetadataValueAdapter(Plugin owningPlugin) { + Validate.notNull(owningPlugin, "owningPlugin cannot be null"); + this.owningPlugin = new WeakReference(owningPlugin); + } + + public Plugin getOwningPlugin() { + return owningPlugin.get(); + } + + public int asInt() { + return NumberConversions.toInt(value()); + } + + public float asFloat() { + return NumberConversions.toFloat(value()); + } + + public double asDouble() { + return NumberConversions.toDouble(value()); + } + + public long asLong() { + return NumberConversions.toLong(value()); + } + + public short asShort() { + return NumberConversions.toShort(value()); + } + + public byte asByte() { + return NumberConversions.toByte(value()); + } + + public boolean asBoolean() { + Object value = value(); + if (value instanceof Boolean) { + return (Boolean) value; + } + + if (value instanceof Number) { + return ((Number) value).intValue() != 0; + } + + if (value instanceof String) { + return Boolean.parseBoolean((String) value); + } + + return value != null; + } + + public String asString() { + Object value = value(); + + if (value == null) { + return ""; + } + return value.toString(); + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/metadata/Metadatable.java b/vspigot-api/src/main/java/org/bukkit/metadata/Metadatable.java new file mode 100644 index 0000000..b47cf2b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/metadata/Metadatable.java @@ -0,0 +1,52 @@ +package org.bukkit.metadata; + +import org.bukkit.plugin.Plugin; + +import java.util.List; + +/** + * This interface is implemented by all objects that can provide metadata + * about themselves. + */ +public interface Metadatable { + /** + * Sets a metadata value in the implementing object's metadata store. + * + * @param metadataKey A unique key to identify this metadata. + * @param newMetadataValue The metadata value to apply. + * @throws IllegalArgumentException If value is null, or the owning plugin + * is null + */ + public void setMetadata(String metadataKey, MetadataValue newMetadataValue); + + /** + * Returns a list of previously set metadata values from the implementing + * object's metadata store. + * + * @param metadataKey the unique metadata key being sought. + * @return A list of values, one for each plugin that has set the + * requested value. + */ + public List getMetadata(String metadataKey); + + /** + * Tests to see whether the implementing object contains the given + * metadata value in its metadata store. + * + * @param metadataKey the unique metadata key being queried. + * @return the existence of the metadataKey within subject. + */ + public boolean hasMetadata(String metadataKey); + + /** + * Removes the given metadata value from the implementing object's + * metadata store. + * + * @param metadataKey the unique metadata key identifying the metadata to + * remove. + * @param owningPlugin This plugin's metadata value will be removed. All + * other values will be left untouched. + * @throws IllegalArgumentException If plugin is null + */ + public void removeMetadata(String metadataKey, Plugin owningPlugin); +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/Permissible.java b/vspigot-api/src/main/java/org/bukkit/permissions/Permissible.java new file mode 100644 index 0000000..5cd3cff --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/Permissible.java @@ -0,0 +1,122 @@ +package org.bukkit.permissions; + +import java.util.Set; +import org.bukkit.plugin.Plugin; + +/** + * Represents an object that may be assigned permissions + */ +public interface Permissible extends ServerOperator { + + /** + * Checks if this object contains an override for the specified + * permission, by fully qualified name + * + * @param name Name of the permission + * @return true if the permission is set, otherwise false + */ + public boolean isPermissionSet(String name); + + /** + * Checks if this object contains an override for the specified {@link + * Permission} + * + * @param perm Permission to check + * @return true if the permission is set, otherwise false + */ + public boolean isPermissionSet(Permission perm); + + /** + * Gets the value of the specified permission, if set. + *

          + * If a permission override is not set on this object, the default value + * of the permission will be returned. + * + * @param name Name of the permission + * @return Value of the permission + */ + public boolean hasPermission(String name); + + /** + * Gets the value of the specified permission, if set. + *

          + * If a permission override is not set on this object, the default value + * of the permission will be returned + * + * @param perm Permission to get + * @return Value of the permission + */ + public boolean hasPermission(Permission perm); + + /** + * Adds a new {@link PermissionAttachment} with a single permission by + * name and value + * + * @param plugin Plugin responsible for this attachment, may not be null + * or disabled + * @param name Name of the permission to attach + * @param value Value of the permission + * @return The PermissionAttachment that was just created + */ + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value); + + /** + * Adds a new empty {@link PermissionAttachment} to this object + * + * @param plugin Plugin responsible for this attachment, may not be null + * or disabled + * @return The PermissionAttachment that was just created + */ + public PermissionAttachment addAttachment(Plugin plugin); + + /** + * Temporarily adds a new {@link PermissionAttachment} with a single + * permission by name and value + * + * @param plugin Plugin responsible for this attachment, may not be null + * or disabled + * @param name Name of the permission to attach + * @param value Value of the permission + * @param ticks Amount of ticks to automatically remove this attachment + * after + * @return The PermissionAttachment that was just created + */ + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks); + + /** + * Temporarily adds a new empty {@link PermissionAttachment} to this + * object + * + * @param plugin Plugin responsible for this attachment, may not be null + * or disabled + * @param ticks Amount of ticks to automatically remove this attachment + * after + * @return The PermissionAttachment that was just created + */ + public PermissionAttachment addAttachment(Plugin plugin, int ticks); + + /** + * Removes the given {@link PermissionAttachment} from this object + * + * @param attachment Attachment to remove + * @throws IllegalArgumentException Thrown when the specified attachment + * isn't part of this object + */ + public void removeAttachment(PermissionAttachment attachment); + + /** + * Recalculates the permissions for this object, if the attachments have + * changed values. + *

          + * This should very rarely need to be called from a plugin. + */ + public void recalculatePermissions(); + + /** + * Gets a set containing all of the permissions currently in effect by + * this object + * + * @return Set of currently effective permissions + */ + public Set getEffectivePermissions(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/PermissibleBase.java b/vspigot-api/src/main/java/org/bukkit/permissions/PermissibleBase.java new file mode 100644 index 0000000..3b95061 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/PermissibleBase.java @@ -0,0 +1,246 @@ +package org.bukkit.permissions; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +/** + * Base Permissible for use in any Permissible object via proxy or extension + */ +public class PermissibleBase implements Permissible { + private ServerOperator opable = null; + private Permissible parent = this; + private final List attachments = new LinkedList(); + private final Map permissions = new HashMap(); + + public PermissibleBase(ServerOperator opable) { + this.opable = opable; + + if (opable instanceof Permissible) { + this.parent = (Permissible) opable; + } + + recalculatePermissions(); + } + + public boolean isOp() { + if (opable == null) { + return false; + } else { + return opable.isOp(); + } + } + + public void setOp(boolean value) { + if (opable == null) { + throw new UnsupportedOperationException("Cannot change op value as no ServerOperator is set"); + } else { + opable.setOp(value); + } + } + + public boolean isPermissionSet(String name) { + if (name == null) { + throw new IllegalArgumentException("Permission name cannot be null"); + } + + return permissions.containsKey(name.toLowerCase()); + } + + public boolean isPermissionSet(Permission perm) { + if (perm == null) { + throw new IllegalArgumentException("Permission cannot be null"); + } + + return isPermissionSet(perm.getName()); + } + + public boolean hasPermission(String inName) { + if (inName == null) { + throw new IllegalArgumentException("Permission name cannot be null"); + } + + String name = inName.toLowerCase(); + + if (isPermissionSet(name)) { + return permissions.get(name).getValue(); + } else { + Permission perm = Bukkit.getServer().getPluginManager().getPermission(name); + + if (perm != null) { + return perm.getDefault().getValue(isOp()); + } else { + return Permission.DEFAULT_PERMISSION.getValue(isOp()); + } + } + } + + public boolean hasPermission(Permission perm) { + if (perm == null) { + throw new IllegalArgumentException("Permission cannot be null"); + } + + String name = perm.getName().toLowerCase(); + + if (isPermissionSet(name)) { + return permissions.get(name).getValue(); + } + return perm.getDefault().getValue(isOp()); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + if (name == null) { + throw new IllegalArgumentException("Permission name cannot be null"); + } else if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } else if (!plugin.isEnabled()) { + throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is disabled"); + } + + PermissionAttachment result = addAttachment(plugin); + result.setPermission(name, value); + + recalculatePermissions(); + + return result; + } + + public PermissionAttachment addAttachment(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } else if (!plugin.isEnabled()) { + throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is disabled"); + } + + PermissionAttachment result = new PermissionAttachment(plugin, parent); + + attachments.add(result); + recalculatePermissions(); + + return result; + } + + public void removeAttachment(PermissionAttachment attachment) { + if (attachment == null) { + throw new IllegalArgumentException("Attachment cannot be null"); + } + + if (attachments.contains(attachment)) { + attachments.remove(attachment); + PermissionRemovedExecutor ex = attachment.getRemovalCallback(); + + if (ex != null) { + ex.attachmentRemoved(attachment); + } + + recalculatePermissions(); + } else { + throw new IllegalArgumentException("Given attachment is not part of Permissible object " + parent); + } + } + + public void recalculatePermissions() { + clearPermissions(); + Set defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(isOp()); + Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(isOp(), parent); + + for (Permission perm : defaults) { + String name = perm.getName().toLowerCase(); + permissions.put(name, new PermissionAttachmentInfo(parent, name, null, true)); + Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent); + calculateChildPermissions(perm.getChildren(), false, null); + } + + for (PermissionAttachment attachment : attachments) { + calculateChildPermissions(attachment.getPermissions(), false, attachment); + } + } + + public synchronized void clearPermissions() { + Set perms = permissions.keySet(); + + for (String name : perms) { + Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, parent); + } + + Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(false, parent); + Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(true, parent); + + permissions.clear(); + } + + private void calculateChildPermissions(Map children, boolean invert, PermissionAttachment attachment) { + Set keys = children.keySet(); + + for (String name : keys) { + Permission perm = Bukkit.getServer().getPluginManager().getPermission(name); + boolean value = children.get(name) ^ invert; + String lname = name.toLowerCase(); + + permissions.put(lname, new PermissionAttachmentInfo(parent, lname, attachment, value)); + Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent); + + if (perm != null) { + calculateChildPermissions(perm.getChildren(), !value, attachment); + } + } + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + if (name == null) { + throw new IllegalArgumentException("Permission name cannot be null"); + } else if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } else if (!plugin.isEnabled()) { + throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is disabled"); + } + + PermissionAttachment result = addAttachment(plugin, ticks); + + if (result != null) { + result.setPermission(name, value); + } + + return result; + } + + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } else if (!plugin.isEnabled()) { + throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is disabled"); + } + + PermissionAttachment result = addAttachment(plugin); + + if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new RemoveAttachmentRunnable(result), ticks) == -1) { + Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1"); + result.remove(); + return null; + } else { + return result; + } + } + + public Set getEffectivePermissions() { + return new HashSet(permissions.values()); + } + + private class RemoveAttachmentRunnable implements Runnable { + private PermissionAttachment attachment; + + public RemoveAttachmentRunnable(PermissionAttachment attachment) { + this.attachment = attachment; + } + + public void run() { + attachment.remove(); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/Permission.java b/vspigot-api/src/main/java/org/bukkit/permissions/Permission.java new file mode 100644 index 0000000..26f6f2b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/Permission.java @@ -0,0 +1,343 @@ +package org.bukkit.permissions; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.plugin.PluginManager; + +/** + * Represents a unique permission that may be attached to a {@link + * Permissible} + */ +public class Permission { + public static final PermissionDefault DEFAULT_PERMISSION = PermissionDefault.OP; + + private final String name; + private final Map children = new LinkedHashMap(); + private PermissionDefault defaultValue = DEFAULT_PERMISSION; + private String description; + + public Permission(String name) { + this(name, null, null, null); + } + + public Permission(String name, String description) { + this(name, description, null, null); + } + + public Permission(String name, PermissionDefault defaultValue) { + this(name, null, defaultValue, null); + } + + public Permission(String name, String description, PermissionDefault defaultValue) { + this(name, description, defaultValue, null); + } + + public Permission(String name, Map children) { + this(name, null, null, children); + } + + public Permission(String name, String description, Map children) { + this(name, description, null, children); + } + + public Permission(String name, PermissionDefault defaultValue, Map children) { + this(name, null, defaultValue, children); + } + + public Permission(String name, String description, PermissionDefault defaultValue, Map children) { + this.name = name; + this.description = (description == null) ? "" : description; + + if (defaultValue != null) { + this.defaultValue = defaultValue; + } + + if (children != null) { + this.children.putAll(children); + } + + recalculatePermissibles(); + } + + /** + * Returns the unique fully qualified name of this Permission + * + * @return Fully qualified name + */ + public String getName() { + return name; + } + + /** + * Gets the children of this permission. + *

          + * If you change this map in any form, you must call {@link + * #recalculatePermissibles()} to recalculate all {@link Permissible}s + * + * @return Permission children + */ + public Map getChildren() { + return children; + } + + /** + * Gets the default value of this permission. + * + * @return Default value of this permission. + */ + public PermissionDefault getDefault() { + return defaultValue; + } + + /** + * Sets the default value of this permission. + *

          + * This will not be saved to disk, and is a temporary operation until the + * server reloads permissions. Changing this default will cause all {@link + * Permissible}s that contain this permission to recalculate their + * permissions + * + * @param value The new default to set + */ + public void setDefault(PermissionDefault value) { + if (defaultValue == null) { + throw new IllegalArgumentException("Default value cannot be null"); + } + + defaultValue = value; + recalculatePermissibles(); + } + + /** + * Gets a brief description of this permission, if set + * + * @return Brief description of this permission + */ + public String getDescription() { + return description; + } + + /** + * Sets the description of this permission. + *

          + * This will not be saved to disk, and is a temporary operation until the + * server reloads permissions. + * + * @param value The new description to set + */ + public void setDescription(String value) { + if (value == null) { + description = ""; + } else { + description = value; + } + } + + /** + * Gets a set containing every {@link Permissible} that has this + * permission. + *

          + * This set cannot be modified. + * + * @return Set containing permissibles with this permission + */ + public Set getPermissibles() { + return Bukkit.getServer().getPluginManager().getPermissionSubscriptions(name); + } + + /** + * Recalculates all {@link Permissible}s that contain this permission. + *

          + * This should be called after modifying the children, and is + * automatically called after modifying the default value + */ + public void recalculatePermissibles() { + Set perms = getPermissibles(); + + Bukkit.getServer().getPluginManager().recalculatePermissionDefaults(this); + + for (Permissible p : perms) { + p.recalculatePermissions(); + } + } + + /** + * Adds this permission to the specified parent permission. + *

          + * If the parent permission does not exist, it will be created and + * registered. + * + * @param name Name of the parent permission + * @param value The value to set this permission to + * @return Parent permission it created or loaded + */ + public Permission addParent(String name, boolean value) { + PluginManager pm = Bukkit.getServer().getPluginManager(); + String lname = name.toLowerCase(); + + Permission perm = pm.getPermission(lname); + + if (perm == null) { + perm = new Permission(lname); + pm.addPermission(perm); + } + + addParent(perm, value); + + return perm; + } + + /** + * Adds this permission to the specified parent permission. + * + * @param perm Parent permission to register with + * @param value The value to set this permission to + */ + public void addParent(Permission perm, boolean value) { + perm.getChildren().put(getName(), value); + perm.recalculatePermissibles(); + } + + /** + * Loads a list of Permissions from a map of data, usually used from + * retrieval from a yaml file. + *

          + * The data may contain a list of name:data, where the data contains the + * following keys: + *

            + *
          • default: Boolean true or false. If not specified, false. + *
          • children: Map of child permissions. If not + * specified, empty list. + *
          • description: Short string containing a very small description of + * this description. If not specified, empty string. + *
          + * + * @param data Map of permissions + * @param error An error message to show if a permission is invalid. + * @param def Default permission value to use if missing + * @return Permission object + */ + public static List loadPermissions(Map data, String error, PermissionDefault def) { + List result = new ArrayList(); + + for (Map.Entry entry : data.entrySet()) { + try { + result.add(Permission.loadPermission(entry.getKey().toString(), (Map) entry.getValue(), def, result)); + } catch (Throwable ex) { + Bukkit.getServer().getLogger().log(Level.SEVERE, String.format(error, entry.getKey()), ex); + } + } + + return result; + } + + /** + * Loads a Permission from a map of data, usually used from retrieval from + * a yaml file. + *

          + * The data may contain the following keys: + *

            + *
          • default: Boolean true or false. If not specified, false. + *
          • children: Map of child permissions. If not + * specified, empty list. + *
          • description: Short string containing a very small description of + * this description. If not specified, empty string. + * + * @param name Name of the permission + * @param data Map of keys + * @return Permission object + */ + public static Permission loadPermission(String name, Map data) { + return loadPermission(name, data, DEFAULT_PERMISSION, null); + } + + /** + * Loads a Permission from a map of data, usually used from retrieval from + * a yaml file. + *

            + * The data may contain the following keys: + *

              + *
            • default: Boolean true or false. If not specified, false. + *
            • children: Map of child permissions. If not + * specified, empty list. + *
            • description: Short string containing a very small description of + * this description. If not specified, empty string. + *
            + * + * @param name Name of the permission + * @param data Map of keys + * @param def Default permission value to use if not set + * @param output A list to append any created child-Permissions to, may be null + * @return Permission object + */ + public static Permission loadPermission(String name, Map data, PermissionDefault def, List output) { + Validate.notNull(name, "Name cannot be null"); + Validate.notNull(data, "Data cannot be null"); + + String desc = null; + Map children = null; + + if (data.get("default") != null) { + PermissionDefault value = PermissionDefault.getByName(data.get("default").toString()); + if (value != null) { + def = value; + } else { + throw new IllegalArgumentException("'default' key contained unknown value"); + } + } + + if (data.get("children") != null) { + Object childrenNode = data.get("children"); + if (childrenNode instanceof Iterable) { + children = new LinkedHashMap(); + for (Object child : (Iterable) childrenNode) { + if (child != null) { + children.put(child.toString(), Boolean.TRUE); + } + } + } else if (childrenNode instanceof Map) { + children = extractChildren((Map) childrenNode, name, def, output); + } else { + throw new IllegalArgumentException("'children' key is of wrong type"); + } + } + + if (data.get("description") != null) { + desc = data.get("description").toString(); + } + + return new Permission(name, desc, def, children); + } + + private static Map extractChildren(Map input, String name, PermissionDefault def, List output) { + Map children = new LinkedHashMap(); + + for (Map.Entry entry : input.entrySet()) { + if ((entry.getValue() instanceof Boolean)) { + children.put(entry.getKey().toString(), (Boolean) entry.getValue()); + } else if ((entry.getValue() instanceof Map)) { + try { + Permission perm = loadPermission(entry.getKey().toString(), (Map) entry.getValue(), def, output); + children.put(perm.getName(), Boolean.TRUE); + + if (output != null) { + output.add(perm); + } + } catch (Throwable ex) { + throw new IllegalArgumentException("Permission node '" + entry.getKey().toString() + "' in child of " + name + " is invalid", ex); + } + } else { + throw new IllegalArgumentException("Child '" + entry.getKey().toString() + "' contains invalid value"); + } + } + + return children; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/PermissionAttachment.java b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionAttachment.java new file mode 100644 index 0000000..b2a44d5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionAttachment.java @@ -0,0 +1,139 @@ +package org.bukkit.permissions; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.bukkit.plugin.Plugin; + +/** + * Holds information about a permission attachment on a {@link Permissible} + * object + */ +public class PermissionAttachment { + private PermissionRemovedExecutor removed; + private final Map permissions = new LinkedHashMap(); + private final Permissible permissible; + private final Plugin plugin; + + public PermissionAttachment(Plugin plugin, Permissible Permissible) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } else if (!plugin.isEnabled()) { + throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is disabled"); + } + + this.permissible = Permissible; + this.plugin = plugin; + } + + /** + * Gets the plugin responsible for this attachment + * + * @return Plugin responsible for this permission attachment + */ + public Plugin getPlugin() { + return plugin; + } + + /** + * Sets an object to be called for when this attachment is removed from a + * {@link Permissible}. May be null. + * + * @param ex Object to be called when this is removed + */ + public void setRemovalCallback(PermissionRemovedExecutor ex) { + removed = ex; + } + + /** + * Gets the class that was previously set to be called when this + * attachment was removed from a {@link Permissible}. May be null. + * + * @return Object to be called when this is removed + */ + public PermissionRemovedExecutor getRemovalCallback() { + return removed; + } + + /** + * Gets the Permissible that this is attached to + * + * @return Permissible containing this attachment + */ + public Permissible getPermissible() { + return permissible; + } + + /** + * Gets a copy of all set permissions and values contained within this + * attachment. + *

            + * This map may be modified but will not affect the attachment, as it is a + * copy. + * + * @return Copy of all permissions and values expressed by this attachment + */ + public Map getPermissions() { + return new LinkedHashMap(permissions); + } + + /** + * Sets a permission to the given value, by its fully qualified name + * + * @param name Name of the permission + * @param value New value of the permission + */ + public void setPermission(String name, boolean value) { + permissions.put(name.toLowerCase(), value); + permissible.recalculatePermissions(); + } + + /** + * Sets a permission to the given value + * + * @param perm Permission to set + * @param value New value of the permission + */ + public void setPermission(Permission perm, boolean value) { + setPermission(perm.getName(), value); + } + + /** + * Removes the specified permission from this attachment. + *

            + * If the permission does not exist in this attachment, nothing will + * happen. + * + * @param name Name of the permission to remove + */ + public void unsetPermission(String name) { + permissions.remove(name.toLowerCase()); + permissible.recalculatePermissions(); + } + + /** + * Removes the specified permission from this attachment. + *

            + * If the permission does not exist in this attachment, nothing will + * happen. + * + * @param perm Permission to remove + */ + public void unsetPermission(Permission perm) { + unsetPermission(perm.getName()); + } + + /** + * Removes this attachment from its registered {@link Permissible} + * + * @return true if the permissible was removed successfully, false if it + * did not exist + */ + public boolean remove() { + try { + permissible.removeAttachment(this); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/PermissionAttachmentInfo.java b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionAttachmentInfo.java new file mode 100644 index 0000000..8e8e335 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionAttachmentInfo.java @@ -0,0 +1,62 @@ +package org.bukkit.permissions; + +/** + * Holds information on a permission and which {@link PermissionAttachment} + * provides it + */ +public class PermissionAttachmentInfo { + private final Permissible permissible; + private final String permission; + private final PermissionAttachment attachment; + private final boolean value; + + public PermissionAttachmentInfo(Permissible permissible, String permission, PermissionAttachment attachment, boolean value) { + if (permissible == null) { + throw new IllegalArgumentException("Permissible may not be null"); + } else if (permission == null) { + throw new IllegalArgumentException("Permissions may not be null"); + } + + this.permissible = permissible; + this.permission = permission; + this.attachment = attachment; + this.value = value; + } + + /** + * Gets the permissible this is attached to + * + * @return Permissible this permission is for + */ + public Permissible getPermissible() { + return permissible; + } + + /** + * Gets the permission being set + * + * @return Name of the permission + */ + public String getPermission() { + return permission; + } + + /** + * Gets the attachment providing this permission. This may be null for + * default permissions (usually parent permissions). + * + * @return Attachment + */ + public PermissionAttachment getAttachment() { + return attachment; + } + + /** + * Gets the value of this permission + * + * @return Value of the permission + */ + public boolean getValue() { + return value; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/PermissionDefault.java b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionDefault.java new file mode 100644 index 0000000..045e733 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionDefault.java @@ -0,0 +1,66 @@ +package org.bukkit.permissions; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents the possible default values for permissions + */ +public enum PermissionDefault { + TRUE("true"), + FALSE("false"), + OP("op", "isop", "operator", "isoperator", "admin", "isadmin"), + NOT_OP("!op", "notop", "!operator", "notoperator", "!admin", "notadmin"); + + private final String[] names; + private final static Map lookup = new HashMap(); + + private PermissionDefault(String... names) { + this.names = names; + } + + /** + * Calculates the value of this PermissionDefault for the given operator + * value + * + * @param op If the target is op + * @return True if the default should be true, or false + */ + public boolean getValue(boolean op) { + switch (this) { + case TRUE: + return true; + case FALSE: + return false; + case OP: + return op; + case NOT_OP: + return !op; + default: + return false; + } + } + + /** + * Looks up a PermissionDefault by name + * + * @param name Name of the default + * @return Specified value, or null if not found + */ + public static PermissionDefault getByName(String name) { + return lookup.get(name.toLowerCase().replaceAll("[^a-z!]", "")); + } + + @Override + public String toString() { + return names[0]; + } + + static { + for (PermissionDefault value : values()) { + for (String name : value.names) { + lookup.put(name, value); + } + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/PermissionRemovedExecutor.java b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionRemovedExecutor.java new file mode 100644 index 0000000..b13d008 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/PermissionRemovedExecutor.java @@ -0,0 +1,16 @@ +package org.bukkit.permissions; + +/** + * Represents a class which is to be notified when a {@link + * PermissionAttachment} is removed from a {@link Permissible} + */ +public interface PermissionRemovedExecutor { + + /** + * Called when a {@link PermissionAttachment} is removed from a {@link + * Permissible} + * + * @param attachment Attachment which was removed + */ + public void attachmentRemoved(PermissionAttachment attachment); +} diff --git a/vspigot-api/src/main/java/org/bukkit/permissions/ServerOperator.java b/vspigot-api/src/main/java/org/bukkit/permissions/ServerOperator.java new file mode 100644 index 0000000..26ed243 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/permissions/ServerOperator.java @@ -0,0 +1,24 @@ +package org.bukkit.permissions; + +import org.bukkit.entity.Player; + +/** + * Represents an object that may become a server operator, such as a {@link + * Player} + */ +public interface ServerOperator { + + /** + * Checks if this object is a server operator + * + * @return true if this is an operator, otherwise false + */ + public boolean isOp(); + + /** + * Sets the operator status of this object + * + * @param value New operator value + */ + public void setOp(boolean value); +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/AuthorNagException.java b/vspigot-api/src/main/java/org/bukkit/plugin/AuthorNagException.java new file mode 100644 index 0000000..6565a44 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/AuthorNagException.java @@ -0,0 +1,20 @@ +package org.bukkit.plugin; + +@SuppressWarnings("serial") +public class AuthorNagException extends RuntimeException { + private final String message; + + /** + * Constructs a new AuthorNagException based on the given Exception + * + * @param message Brief message explaining the cause of the exception + */ + public AuthorNagException(final String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/EventExecutor.java b/vspigot-api/src/main/java/org/bukkit/plugin/EventExecutor.java new file mode 100644 index 0000000..3b2c99e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/EventExecutor.java @@ -0,0 +1,12 @@ +package org.bukkit.plugin; + +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.Listener; + +/** + * Interface which defines the class for event call backs to plugins + */ +public interface EventExecutor { + public void execute(Listener listener, Event event) throws EventException; +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/IllegalPluginAccessException.java b/vspigot-api/src/main/java/org/bukkit/plugin/IllegalPluginAccessException.java new file mode 100644 index 0000000..b25447d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/IllegalPluginAccessException.java @@ -0,0 +1,25 @@ +package org.bukkit.plugin; + +/** + * Thrown when a plugin attempts to interact with the server when it is not + * enabled + */ +@SuppressWarnings("serial") +public class IllegalPluginAccessException extends RuntimeException { + + /** + * Creates a new instance of IllegalPluginAccessException + * without detail message. + */ + public IllegalPluginAccessException() {} + + /** + * Constructs an instance of IllegalPluginAccessException + * with the specified detail message. + * + * @param msg the detail message. + */ + public IllegalPluginAccessException(String msg) { + super(msg); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/InvalidDescriptionException.java b/vspigot-api/src/main/java/org/bukkit/plugin/InvalidDescriptionException.java new file mode 100644 index 0000000..0a77c2e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/InvalidDescriptionException.java @@ -0,0 +1,45 @@ +package org.bukkit.plugin; + +/** + * Thrown when attempting to load an invalid PluginDescriptionFile + */ +public class InvalidDescriptionException extends Exception { + private static final long serialVersionUID = 5721389122281775896L; + + /** + * Constructs a new InvalidDescriptionException based on the given + * Exception + * + * @param message Brief message explaining the cause of the exception + * @param cause Exception that triggered this Exception + */ + public InvalidDescriptionException(final Throwable cause, final String message) { + super(message, cause); + } + + /** + * Constructs a new InvalidDescriptionException based on the given + * Exception + * + * @param cause Exception that triggered this Exception + */ + public InvalidDescriptionException(final Throwable cause) { + super("Invalid plugin.yml", cause); + } + + /** + * Constructs a new InvalidDescriptionException with the given message + * + * @param message Brief message explaining the cause of the exception + */ + public InvalidDescriptionException(final String message) { + super(message); + } + + /** + * Constructs a new InvalidDescriptionException + */ + public InvalidDescriptionException() { + super("Invalid plugin.yml"); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/InvalidPluginException.java b/vspigot-api/src/main/java/org/bukkit/plugin/InvalidPluginException.java new file mode 100644 index 0000000..7ddf7b6 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/InvalidPluginException.java @@ -0,0 +1,49 @@ +package org.bukkit.plugin; + +/** + * Thrown when attempting to load an invalid Plugin file + */ +public class InvalidPluginException extends Exception { + private static final long serialVersionUID = -8242141640709409544L; + + /** + * Constructs a new InvalidPluginException based on the given Exception + * + * @param cause Exception that triggered this Exception + */ + public InvalidPluginException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new InvalidPluginException + */ + public InvalidPluginException() { + + } + + /** + * Constructs a new InvalidPluginException with the specified detail + * message and cause. + * + * @param message the detail message (which is saved for later retrieval + * by the getMessage() method). + * @param cause the cause (which is saved for later retrieval by the + * getCause() method). (A null value is permitted, and indicates that + * the cause is nonexistent or unknown.) + */ + public InvalidPluginException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new InvalidPluginException with the specified detail + * message + * + * @param message TThe detail message is saved for later retrieval by the + * getMessage() method. + */ + public InvalidPluginException(final String message) { + super(message); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/Plugin.java b/vspigot-api/src/main/java/org/bukkit/plugin/Plugin.java new file mode 100644 index 0000000..7bdc809 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/Plugin.java @@ -0,0 +1,189 @@ +package org.bukkit.plugin; + +import java.io.File; +import java.io.InputStream; +import java.util.logging.Logger; + +import org.bukkit.Server; +import org.bukkit.command.TabExecutor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.ChunkGenerator; + +import com.avaje.ebean.EbeanServer; + +/** + * Represents a Plugin + *

            + * The use of {@link PluginBase} is recommended for actual Implementation + */ +public interface Plugin extends TabExecutor { + /** + * Returns the folder that the plugin data's files are located in. The + * folder may not yet exist. + * + * @return The folder + */ + public File getDataFolder(); + + /** + * Returns the plugin.yaml file containing the details for this plugin + * + * @return Contents of the plugin.yaml file + */ + public PluginDescriptionFile getDescription(); + + /** + * Gets a {@link FileConfiguration} for this plugin, read through + * "config.yml" + *

            + * If there is a default config.yml embedded in this plugin, it will be + * provided as a default for this Configuration. + * + * @return Plugin configuration + */ + public FileConfiguration getConfig(); + + /** + * Gets an embedded resource in this plugin + * + * @param filename Filename of the resource + * @return File if found, otherwise null + */ + public InputStream getResource(String filename); + + /** + * Saves the {@link FileConfiguration} retrievable by {@link #getConfig()}. + */ + public void saveConfig(); + + /** + * Saves the raw contents of the default config.yml file to the location + * retrievable by {@link #getConfig()}. If there is no default config.yml + * embedded in the plugin, an empty config.yml file is saved. This should + * fail silently if the config.yml already exists. + */ + public void saveDefaultConfig(); + + /** + * Saves the raw contents of any resource embedded with a plugin's .jar + * file assuming it can be found using {@link #getResource(String)}. + *

            + * The resource is saved into the plugin's data folder using the same + * hierarchy as the .jar file (subdirectories are preserved). + * + * @param resourcePath the embedded resource path to look for within the + * plugin's .jar file. (No preceding slash). + * @param replace if true, the embedded resource will overwrite the + * contents of an existing file. + * @throws IllegalArgumentException if the resource path is null, empty, + * or points to a nonexistent resource. + */ + public void saveResource(String resourcePath, boolean replace); + + /** + * Discards any data in {@link #getConfig()} and reloads from disk. + */ + public void reloadConfig(); + + /** + * Gets the associated PluginLoader responsible for this plugin + * + * @return PluginLoader that controls this plugin + */ + public PluginLoader getPluginLoader(); + + /** + * Returns the Server instance currently running this plugin + * + * @return Server running this plugin + */ + public Server getServer(); + + /** + * Returns a value indicating whether or not this plugin is currently + * enabled + * + * @return true if this plugin is enabled, otherwise false + */ + public boolean isEnabled(); + + /** + * Called when this plugin is disabled + */ + public void onDisable(); + + /** + * Called after a plugin is loaded but before it has been enabled. + *

            + * When mulitple plugins are loaded, the onLoad() for all plugins is + * called before any onEnable() is called. + */ + public void onLoad(); + + /** + * Called when this plugin is enabled + */ + public void onEnable(); + + /** + * Simple boolean if we can still nag to the logs about things + * + * @return boolean whether we can nag + */ + public boolean isNaggable(); + + /** + * Set naggable state + * + * @param canNag is this plugin still naggable? + */ + public void setNaggable(boolean canNag); + + /** + * Gets the {@link EbeanServer} tied to this plugin. This will only be + * available if enabled in the {@link + * PluginDescriptionFile#isDatabaseEnabled()} + *

            + * For more information on the use of + * Avaje Ebeans ORM, see Avaje Ebeans + * Documentation + *

            + * For an example using Ebeans ORM, see Bukkit's Homebukkit Plugin + * + * + * @return ebean server instance or null if not enabled + */ + public EbeanServer getDatabase(); + + /** + * Gets a {@link ChunkGenerator} for use in a default world, as specified + * in the server configuration + * + * @param worldName Name of the world that this will be applied to + * @param id Unique ID, if any, that was specified to indicate which + * generator was requested + * @return ChunkGenerator for use in the default world generation + */ + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id); + + /** + * Returns the plugin logger associated with this server's logger. The + * returned logger automatically tags all log messages with the plugin's + * name. + * + * @return Logger associated with this plugin + */ + public Logger getLogger(); + + /** + * Returns the name of the plugin. + *

            + * This should return the bare name of the plugin and should be used for + * comparison. + * + * @return name of the plugin + */ + public String getName(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/PluginAwareness.java b/vspigot-api/src/main/java/org/bukkit/plugin/PluginAwareness.java new file mode 100644 index 0000000..ddb47b7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/PluginAwareness.java @@ -0,0 +1,29 @@ +package org.bukkit.plugin; + +import java.util.Set; + +import org.bukkit.plugin.java.JavaPlugin; + +/** + * Represents a concept that a plugin is aware of. + *

            + * The internal representation may be singleton, or be a parameterized + * instance, but must be immutable. + */ +public interface PluginAwareness { + /** + * Each entry here represents a particular plugin's awareness. These can + * be checked by using {@link PluginDescriptionFile#getAwareness()}.{@link + * Set#contains(Object) contains(flag)}. + */ + public enum Flags implements PluginAwareness { + /** + * This specifies that all (text) resources stored in a plugin's jar + * use UTF-8 encoding. + * + * @see JavaPlugin#getTextResource(String) + */ + UTF8, + ; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/PluginBase.java b/vspigot-api/src/main/java/org/bukkit/plugin/PluginBase.java new file mode 100644 index 0000000..6031af1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/PluginBase.java @@ -0,0 +1,32 @@ +package org.bukkit.plugin; + +/** + * Represents a base {@link Plugin} + *

            + * Extend this class if your plugin is not a {@link + * org.bukkit.plugin.java.JavaPlugin} + */ +public abstract class PluginBase implements Plugin { + @Override + public final int hashCode() { + return getName().hashCode(); + } + + @Override + public final boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Plugin)) { + return false; + } + return getName().equals(((Plugin) obj).getName()); + } + + public final String getName() { + return getDescription().getName(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java b/vspigot-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java new file mode 100644 index 0000000..0fd966c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java @@ -0,0 +1,1110 @@ +package org.bukkit.plugin; + +import java.io.InputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.permissions.Permissible; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.AbstractConstruct; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.Tag; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * This type is the runtime-container for the information in the plugin.yml. + * All plugins must have a respective plugin.yml. For plugins written in java + * using the standard plugin loader, this file must be in the root of the jar + * file. + *

            + * When Bukkit loads a plugin, it needs to know some basic information about + * it. It reads this information from a YAML file, 'plugin.yml'. This file + * consists of a set of attributes, each defined on a new line and with no + * indentation. + *

            + * Every (almost* every) method corresponds with a specific entry in the + * plugin.yml. These are the required entries for every plugin.yml: + *

              + *
            • {@link #getName()} - name + *
            • {@link #getVersion()} - version + *
            • {@link #getMain()} - main + *
            + *

            + * Failing to include any of these items will throw an exception and cause the + * server to ignore your plugin. + *

            + * This is a list of the possible yaml keys, with specific details included in + * the respective method documentations: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
            NodeMethodSummary
            name{@link #getName()}The unique name of plugin
            version{@link #getVersion()}A plugin revision identifier
            main{@link #getMain()}The plugin's initial class file
            author
            authors
            {@link #getAuthors()}The plugin contributors
            description{@link #getDescription()}Human readable plugin summary
            website{@link #getWebsite()}The URL to the plugin's site
            prefix{@link #getPrefix()}The token to prefix plugin log entries
            database{@link #isDatabaseEnabled()}Indicator to enable database support
            load{@link #getLoad()}The phase of server-startup this plugin will load during
            depend{@link #getDepend()}Other required plugins
            softdepend{@link #getSoftDepend()}Other plugins that add functionality
            loadbefore{@link #getLoadBefore()}The inverse softdepend
            commands{@link #getCommands()}The commands the plugin will register
            permissions{@link #getPermissions()}The permissions the plugin will register
            default-permission{@link #getPermissionDefault()}The default {@link Permission#getDefault() default} permission + * state for defined {@link #getPermissions() permissions} the plugin + * will register
            awareness{@link #getAwareness()}The concepts that the plugin acknowledges
            + *

            + * A plugin.yml example:

            + *name: Inferno
            + *version: 1.4.1
            + *description: This plugin is so 31337. You can set yourself on fire.
            + *# We could place every author in the authors list, but chose not to for illustrative purposes
            + *# Also, having an author distinguishes that person as the project lead, and ensures their
            + *# name is displayed first
            + *author: CaptainInflamo
            + *authors: [Cogito, verrier, EvilSeph]
            + *website: http://www.curse.com/server-mods/minecraft/myplugin
            + *
            + *main: com.captaininflamo.bukkit.inferno.Inferno
            + *database: false
            + *depend: [NewFire, FlameWire]
            + *
            + *commands:
            + *  flagrate:
            + *    description: Set yourself on fire.
            + *    aliases: [combust_me, combustMe]
            + *    permission: inferno.flagrate
            + *    usage: Syntax error! Simply type /<command> to ignite yourself.
            + *  burningdeaths:
            + *    description: List how many times you have died by fire.
            + *    aliases: [burning_deaths, burningDeaths]
            + *    permission: inferno.burningdeaths
            + *    usage: |
            + *      /<command> [player]
            + *      Example: /<command> - see how many times you have burned to death
            + *      Example: /<command> CaptainIce - see how many times CaptainIce has burned to death
            + *
            + *permissions:
            + *  inferno.*:
            + *    description: Gives access to all Inferno commands
            + *    children:
            + *      inferno.flagrate: true
            + *      inferno.burningdeaths: true
            + *      inferno.burningdeaths.others: true
            + *  inferno.flagrate:
            + *    description: Allows you to ignite yourself
            + *    default: true
            + *  inferno.burningdeaths:
            + *    description: Allows you to see how many times you have burned to death
            + *    default: true
            + *  inferno.burningdeaths.others:
            + *    description: Allows you to see how many times others have burned to death
            + *    default: op
            + *    children:
            + *      inferno.burningdeaths: true
            + *
            + */ +public final class PluginDescriptionFile { + private static final ThreadLocal YAML = new ThreadLocal() { + @Override + protected Yaml initialValue() { + return new Yaml(new SafeConstructor() { + { + yamlConstructors.put(null, new AbstractConstruct() { + @Override + public Object construct(final Node node) { + if (!node.getTag().startsWith("!@")) { + // Unknown tag - will fail + return SafeConstructor.undefinedConstructor.construct(node); + } + // Unknown awareness - provide a graceful substitution + return new PluginAwareness() { + @Override + public String toString() { + return node.toString(); + } + }; + } + }); + for (final PluginAwareness.Flags flag : PluginAwareness.Flags.values()) { + yamlConstructors.put(new Tag("!@" + flag.name()), new AbstractConstruct() { + @Override + public PluginAwareness.Flags construct(final Node node) { + return flag; + } + }); + } + } + }); + } + }; + String rawName = null; + private String name = null; + private String main = null; + private String classLoaderOf = null; + private List depend = ImmutableList.of(); + private List softDepend = ImmutableList.of(); + private List loadBefore = ImmutableList.of(); + private String version = null; + private Map> commands = null; + private String description = null; + private List authors = null; + private String website = null; + private String prefix = null; + private boolean database = false; + private PluginLoadOrder order = PluginLoadOrder.POSTWORLD; + private List permissions = null; + private Map lazyPermissions = null; + private PermissionDefault defaultPerm = PermissionDefault.OP; + private Set awareness = ImmutableSet.of(); + + public PluginDescriptionFile(final InputStream stream) throws InvalidDescriptionException { + loadMap(asMap(YAML.get().load(stream))); + } + + /** + * Loads a PluginDescriptionFile from the specified reader + * + * @param reader The reader + * @throws InvalidDescriptionException If the PluginDescriptionFile is + * invalid + */ + public PluginDescriptionFile(final Reader reader) throws InvalidDescriptionException { + loadMap(asMap(YAML.get().load(reader))); + } + + /** + * Creates a new PluginDescriptionFile with the given detailed + * + * @param pluginName Name of this plugin + * @param pluginVersion Version of this plugin + * @param mainClass Full location of the main class of this plugin + */ + public PluginDescriptionFile(final String pluginName, final String pluginVersion, final String mainClass) { + name = pluginName.replace(' ', '_'); + version = pluginVersion; + main = mainClass; + } + + /** + * Gives the name of the plugin. This name is a unique identifier for + * plugins. + *
              + *
            • Must consist of all alphanumeric characters, underscores, hyphon, + * and period (a-z,A-Z,0-9, _.-). Any other character will cause the + * plugin.yml to fail loading. + *
            • Used to determine the name of the plugin's data folder. Data + * folders are placed in the ./plugins/ directory by default, but this + * behavior should not be relied on. {@link Plugin#getDataFolder()} + * should be used to reference the data folder. + *
            • It is good practice to name your jar the same as this, for example + * 'MyPlugin.jar'. + *
            • Case sensitive. + *
            • The is the token referenced in {@link #getDepend()}, {@link + * #getSoftDepend()}, and {@link #getLoadBefore()}. + *
            • Using spaces in the plugin's name is deprecated. + *
            + *

            + * In the plugin.yml, this entry is named name. + *

            + * Example:

            name: MyPlugin
            + * + * @return the name of the plugin + */ + public String getName() { + return name; + } + + /** + * Gives the version of the plugin. + *
              + *
            • Version is an arbitrary string, however the most common format is + * MajorRelease.MinorRelease.Build (eg: 1.4.1). + *
            • Typically you will increment this every time you release a new + * feature or bug fix. + *
            • Displayed when a user types /version PluginName + *
            + *

            + * In the plugin.yml, this entry is named version. + *

            + * Example:

            version: 1.4.1
            + * + * @return the version of the plugin + */ + public String getVersion() { + return version; + } + + /** + * Gives the fully qualified name of the main class for a plugin. The + * format should follow the {@link ClassLoader#loadClass(String)} syntax + * to successfully be resolved at runtime. For most plugins, this is the + * class that extends {@link JavaPlugin}. + *
              + *
            • This must contain the full namespace including the class file + * itself. + *
            • If your namespace is org.bukkit.plugin, and your class + * file is called MyPlugin then this must be + * org.bukkit.plugin.MyPlugin + *
            • No plugin can use org.bukkit. as a base package for + * any class, including the main class. + *
            + *

            + * In the plugin.yml, this entry is named main. + *

            + * Example: + *

            main: org.bukkit.plugin.MyPlugin
            + * + * @return the fully qualified main class for the plugin + */ + public String getMain() { + return main; + } + + /** + * Gives a human-friendly description of the functionality the plugin + * provides. + *
              + *
            • The description can have multiple lines. + *
            • Displayed when a user types /version PluginName + *
            + *

            + * In the plugin.yml, this entry is named description. + *

            + * Example: + *

            description: This plugin is so 31337. You can set yourself on fire.
            + * + * @return description of this plugin, or null if not specified + */ + public String getDescription() { + return description; + } + + /** + * Gives the phase of server startup that the plugin should be loaded. + *
              + *
            • Possible values are in {@link PluginLoadOrder}. + *
            • Defaults to {@link PluginLoadOrder#POSTWORLD}. + *
            • Certain caveats apply to each phase. + *
            • When different, {@link #getDepend()}, {@link #getSoftDepend()}, and + * {@link #getLoadBefore()} become relative in order loaded per-phase. + * If a plugin loads at STARTUP, but a dependency loads + * at POSTWORLD, the dependency will not be loaded before + * the plugin is loaded. + *
            + *

            + * In the plugin.yml, this entry is named load. + *

            + * Example:

            load: STARTUP
            + * + * @return the phase when the plugin should be loaded + */ + public PluginLoadOrder getLoad() { + return order; + } + + /** + * Gives the list of authors for the plugin. + *
              + *
            • Gives credit to the developer. + *
            • Used in some server error messages to provide helpful feedback on + * who to contact when an error occurs. + *
            • A bukkit.org forum handle or email address is recommended. + *
            • Is displayed when a user types /version PluginName + *
            • authors must be in YAML list + * format. + *
            + *

            + * In the plugin.yml, this has two entries, author and + * authors. + *

            + * Single author example: + *

            author: CaptainInflamo
            + * Multiple author example: + *
            authors: [Cogito, verrier, EvilSeph]
            + * When both are specified, author will be the first entry in the list, so + * this example: + *
            author: Grum
            +     *authors:
            +     *- feildmaster
            +     *- amaranth
            + * Is equivilant to this example: + *
            authors: [Grum, feildmaster, aramanth]
            + * + * @return an immutable list of the plugin's authors + */ + public List getAuthors() { + return authors; + } + + /** + * Gives the plugin's or plugin's author's website. + *
              + *
            • A link to the Curse page that includes documentation and downloads + * is highly recommended. + *
            • Displayed when a user types /version PluginName + *
            + *

            + * In the plugin.yml, this entry is named website. + *

            + * Example: + *

            website: http://www.curse.com/server-mods/minecraft/myplugin
            + * + * @return description of this plugin, or null if not specified + */ + public String getWebsite() { + return website; + } + + /** + * Gives if the plugin uses a database. + *
              + *
            • Using a database is non-trivial. + *
            • Valid values include true and false + *
            + *

            + * In the plugin.yml, this entry is named database. + *

            + * Example: + *

            database: false
            + * + * @return if this plugin requires a database + * @see Plugin#getDatabase() + */ + public boolean isDatabaseEnabled() { + return database; + } + + /** + * Gives a list of other plugins that the plugin requires. + *
              + *
            • Use the value in the {@link #getName()} of the target plugin to + * specify the dependency. + *
            • If any plugin listed here is not found, your plugin will fail to + * load at startup. + *
            • If multiple plugins list each other in depend, + * creating a network with no individual plugin does not list another + * plugin in the network, + * all plugins in that network will fail. + *
            • depend must be in must be in YAML list + * format. + *
            + *

            + * In the plugin.yml, this entry is named depend. + *

            + * Example: + *

            depend:
            +     *- OnePlugin
            +     *- AnotherPlugin
            + * + * @return immutable list of the plugin's dependencies + */ + public List getDepend() { + return depend; + } + + /** + * Gives a list of other plugins that the plugin requires for full + * functionality. The {@link PluginManager} will make best effort to treat + * all entries here as if they were a {@link #getDepend() dependency}, but + * will never fail because of one of these entries. + *
              + *
            • Use the value in the {@link #getName()} of the target plugin to + * specify the dependency. + *
            • When an unresolvable plugin is listed, it will be ignored and does + * not affect load order. + *
            • When a circular dependency occurs (a network of plugins depending + * or soft-dependending each other), it will arbitrarily choose a + * plugin that can be resolved when ignoring soft-dependencies. + *
            • softdepend must be in YAML list + * format. + *
            + *

            + * In the plugin.yml, this entry is named softdepend. + *

            + * Example: + *

            softdepend: [OnePlugin, AnotherPlugin]
            + * + * @return immutable list of the plugin's preferred dependencies + */ + public List getSoftDepend() { + return softDepend; + } + + /** + * Gets the list of plugins that should consider this plugin a + * soft-dependency. + *
              + *
            • Use the value in the {@link #getName()} of the target plugin to + * specify the dependency. + *
            • The plugin should load before any other plugins listed here. + *
            • Specifying another plugin here is strictly equivalent to having the + * specified plugin's {@link #getSoftDepend()} include {@link + * #getName() this plugin}. + *
            • loadbefore must be in YAML list + * format. + *
            + *

            + * In the plugin.yml, this entry is named loadbefore. + *

            + * Example: + *

            loadbefore:
            +     *- OnePlugin
            +     *- AnotherPlugin
            + * + * @return immutable list of plugins that should consider this plugin a + * soft-dependency + */ + public List getLoadBefore() { + return loadBefore; + } + + /** + * Gives the token to prefix plugin-specific logging messages with. + *
              + *
            • This includes all messages using {@link Plugin#getLogger()}. + *
            • If not specified, the server uses the plugin's {@link #getName() + * name}. + *
            • This should clearly indicate what plugin is being logged. + *
            + *

            + * In the plugin.yml, this entry is named prefix. + *

            + * Example:

            prefix: ex-why-zee
            + * + * @return the prefixed logging token, or null if not specified + */ + public String getPrefix() { + return prefix; + } + + /** + * Gives the map of command-name to command-properties. Each entry in this + * map corresponds to a single command and the respective values are the + * properties of the command. Each property, with the exception of + * aliases, can be defined at runtime using methods in {@link + * PluginCommand} and are defined here only as a convenience. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
            NodeMethodTypeDescriptionExample
            description{@link PluginCommand#setDescription(String)}StringA user-friendly description for a command. It is useful for + * documentation purposes as well as in-game help.
            description: Set yourself on fire
            aliases{@link PluginCommand#setAliases(List)}String or List of + * stringsAlternative command names, with special usefulness for commands + * that are already registered. Aliases are not effective when + * defined at runtime, so the plugin description file is the + * only way to have them properly defined. + *

            + * Note: Command aliases may not have a colon in them.

            Single alias format: + *
            aliases: combust_me
            or + * multiple alias format: + *
            aliases: [combust_me, combustMe]
            permission{@link PluginCommand#setPermission(String)}StringThe name of the {@link Permission} required to use the command. + * A user without the permission will receive the specified + * message (see {@linkplain + * PluginCommand#setPermissionMessage(String) below}), or a + * standard one if no specific message is defined. Without the + * permission node, no {@link + * PluginCommand#setExecutor(CommandExecutor) CommandExecutor} or + * {@link PluginCommand#setTabCompleter(TabCompleter) + * TabCompleter} will be called.
            permission: inferno.flagrate
            permission-message{@link PluginCommand#setPermissionMessage(String)}String
              + *
            • Displayed to a player that attempts to use a command, but + * does not have the required permission. See {@link + * PluginCommand#getPermission() above}. + *
            • <permission> is a macro that is replaced with the + * permission node required to use the command. + *
            • Using empty quotes is a valid way to indicate nothing + * should be displayed to a player. + *
            permission-message: You do not have /<permission>
            usage{@link PluginCommand#setUsage(String)}StringThis message is displayed to a player when the {@link + * PluginCommand#setExecutor(CommandExecutor)} {@linkplain + * CommandExecutor#onCommand(CommandSender,Command,String,String[]) + * returns false}. <command> is a macro that is replaced + * the command issued.
            usage: Syntax error! Perhaps you meant /<command> PlayerName?
            + * It is worth noting that to use a colon in a yaml, like + * `usage: Usage: /god [player]', you need to + * surround + * the message with double-quote: + *
            usage: "Usage: /god [player]"
            + * The commands are structured as a hiearchy of nested mappings. + * The primary (top-level, no intendentation) node is + * `commands', while each individual command name is + * indented, indicating it maps to some value (in our case, the + * properties of the table above). + *

            + * Here is an example bringing together the piecemeal examples above, as + * well as few more definitions:

            +     *commands:
            +     *  flagrate:
            +     *    description: Set yourself on fire.
            +     *    aliases: [combust_me, combustMe]
            +     *    permission: inferno.flagrate
            +     *    permission-message: You do not have /<permission>
            +     *    usage: Syntax error! Perhaps you meant /<command> PlayerName?
            +     *  burningdeaths:
            +     *    description: List how many times you have died by fire.
            +     *    aliases:
            +     *    - burning_deaths
            +     *    - burningDeaths
            +     *    permission: inferno.burningdeaths
            +     *    usage: |
            +     *      /<command> [player]
            +     *      Example: /<command> - see how many times you have burned to death
            +     *      Example: /<command> CaptainIce - see how many times CaptainIce has burned to death
            +     *  # The next command has no description, aliases, etc. defined, but is still valid
            +     *  # Having an empty declaration is useful for defining the description, permission, and messages from a configuration dynamically
            +     *  apocalypse:
            +     *
            + * Note: Command names may not have a colon in their name. + * + * @return the commands this plugin will register + */ + public Map> getCommands() { + return commands; + } + + /** + * Gives the list of permissions the plugin will register at runtime, + * immediately proceding enabling. The format for defining permissions is + * a map from permission name to properties. To represent a map without + * any specific property, empty curly-braces ( + * {} ) may be used (as a null value is not + * accepted, unlike the {@link #getCommands() commands} above). + *

            + * A list of optional properties for permissions: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
            NodeDescriptionExample
            descriptionPlaintext (user-friendly) description of what the permission + * is for.
            description: Allows you to set yourself on fire
            defaultThe default state for the permission, as defined by {@link + * Permission#getDefault()}. If not defined, it will be set to + * the value of {@link PluginDescriptionFile#getPermissionDefault()}. + *

            + * For reference:

              + *
            • true - Represents a positive assignment to + * {@link Permissible permissibles}. + *
            • false - Represents no assignment to {@link + * Permissible permissibles}. + *
            • op - Represents a positive assignment to + * {@link Permissible#isOp() operator permissibles}. + *
            • notop - Represents a positive assignment to + * {@link Permissible#isOp() non-operator permissibiles}. + *
            default: true
            childrenAllows other permissions to be set as a {@linkplain + * Permission#getChildren() relation} to the parent permission. + * When a parent permissions is assigned, child permissions are + * respectively assigned as well. + *
              + *
            • When a parent permission is assigned negatively, child + * permissions are assigned based on an inversion of their + * association. + *
            • When a parent permission is assigned positively, child + * permissions are assigned based on their association. + *
            + *

            + * Child permissions may be defined in a number of ways:

              + *
            • Children may be defined as a list of + * names. Using a list will treat all children associated + * positively to their parent. + *
            • Children may be defined as a map. Each permission name maps + * to either a boolean (representing the association), or a + * nested permission definition (just as another permission). + * Using a nested definition treats the child as a positive + * association. + *
            • A nested permission definition must be a map of these same + * properties. To define a valid nested permission without + * defining any specific property, empty curly-braces ( + * {} ) must be used. + *
            • A nested permission may carry it's own nested permissions + * as children, as they may also have nested permissions, and + * so forth. There is no direct limit to how deep the + * permission tree is defined. + *
            As a list: + *
            children: [inferno.flagrate, inferno.burningdeaths]
            + * Or as a mapping: + *
            children:
            +     *  inferno.flagrate: true
            +     *  inferno.burningdeaths: true
            + * An additional example showing basic nested values can be seen + * here. + *
            + * The permissions are structured as a hiearchy of nested mappings. + * The primary (top-level, no intendentation) node is + * `permissions', while each individual permission name is + * indented, indicating it maps to some value (in our case, the + * properties of the table above). + *

            + * Here is an example using some of the properties:

            +     *permissions:
            +     *  inferno.*:
            +     *    description: Gives access to all Inferno commands
            +     *    children:
            +     *      inferno.flagrate: true
            +     *      inferno.burningdeaths: true
            +     *  inferno.flagate:
            +     *    description: Allows you to ignite yourself
            +     *    default: true
            +     *  inferno.burningdeaths:
            +     *    description: Allows you to see how many times you have burned to death
            +     *    default: true
            +     *
            + * Another example, with nested definitions, can be found here. + */ + public List getPermissions() { + if (permissions == null) { + if (lazyPermissions == null) { + permissions = ImmutableList.of(); + } else { + permissions = ImmutableList.copyOf(Permission.loadPermissions(lazyPermissions, "Permission node '%s' in plugin description file for " + getFullName() + " is invalid", defaultPerm)); + lazyPermissions = null; + } + } + return permissions; + } + + /** + * Gives the default {@link Permission#getDefault() default} state of + * {@link #getPermissions() permissions} registered for the plugin. + *
              + *
            • If not specified, it will be {@link PermissionDefault#OP}. + *
            • It is matched using {@link PermissionDefault#getByName(String)} + *
            • It only affects permissions that do not define the + * default node. + *
            • It may be any value in {@link PermissionDefault}. + *
            + *

            + * In the plugin.yml, this entry is named default-permission. + *

            + * Example:

            default-permission: NOT_OP
            + * + * @return the default value for the plugin's permissions + */ + public PermissionDefault getPermissionDefault() { + return defaultPerm; + } + + /** + * Gives a set of every {@link PluginAwareness} for a plugin. An awareness + * dictates something that a plugin developer acknowledges when the plugin + * is compiled. Some implementions may define extra awarenesses that are + * not included in the API. Any unrecognized + * awareness (one unsupported or in a future version) will cause a dummy + * object to be created instead of failing. + *

            + *

              + *
            • Currently only supports the enumerated values in {@link + * PluginAwareness.Flags}. + *
            • Each awareness starts the identifier with bang-at + * (!@). + *
            • Unrecognized (future / unimplemented) entries are quietly replaced + * by a generic object that implements PluginAwareness. + *
            • A type of awareness must be defined by the runtime and acknowledged + * by the API, effectively discluding any derived type from any + * plugin's classpath. + *
            • awareness must be in YAML list + * format. + *
            + *

            + * In the plugin.yml, this entry is named awareness. + *

            + * Example:

            awareness:
            +     *- !@UTF8
            + *

            + * Note: Although unknown versions of some future awareness are + * gracefully substituted, previous versions of Bukkit (ones prior to the + * first implementation of awareness) will fail to load a plugin that + * defines any awareness. + * + * @return a set containing every awareness for the plugin + */ + public Set getAwareness() { + return awareness; + } + + /** + * Returns the name of a plugin, including the version. This method is + * provided for convenience; it uses the {@link #getName()} and {@link + * #getVersion()} entries. + * + * @return a descriptive name of the plugin and respective version + */ + public String getFullName() { + return name + " v" + version; + } + + /** + * @deprecated unused + */ + @Deprecated + public String getClassLoaderOf() { + return classLoaderOf; + } + + public void setDatabaseEnabled(boolean database) { + this.database = database; + } + + /** + * Saves this PluginDescriptionFile to the given writer + * + * @param writer Writer to output this file to + */ + public void save(Writer writer) { + YAML.get().dump(saveMap(), writer); + } + + private void loadMap(Map map) throws InvalidDescriptionException { + try { + name = rawName = map.get("name").toString(); + + if (!name.matches("^[A-Za-z0-9 _.-]+$")) { + throw new InvalidDescriptionException("name '" + name + "' contains invalid characters."); + } + name = name.replace(' ', '_'); + } catch (NullPointerException ex) { + throw new InvalidDescriptionException(ex, "name is not defined"); + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "name is of wrong type"); + } + + try { + version = map.get("version").toString(); + } catch (NullPointerException ex) { + throw new InvalidDescriptionException(ex, "version is not defined"); + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "version is of wrong type"); + } + + try { + main = map.get("main").toString(); + if (main.startsWith("org.bukkit.")) { + throw new InvalidDescriptionException("main may not be within the org.bukkit namespace"); + } + } catch (NullPointerException ex) { + throw new InvalidDescriptionException(ex, "main is not defined"); + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "main is of wrong type"); + } + + if (map.get("commands") != null) { + ImmutableMap.Builder> commandsBuilder = ImmutableMap.>builder(); + try { + for (Map.Entry command : ((Map) map.get("commands")).entrySet()) { + ImmutableMap.Builder commandBuilder = ImmutableMap.builder(); + if (command.getValue() != null) { + for (Map.Entry commandEntry : ((Map) command.getValue()).entrySet()) { + if (commandEntry.getValue() instanceof Iterable) { + // This prevents internal alias list changes + ImmutableList.Builder commandSubList = ImmutableList.builder(); + for (Object commandSubListItem : (Iterable) commandEntry.getValue()) { + if (commandSubListItem != null) { + commandSubList.add(commandSubListItem); + } + } + commandBuilder.put(commandEntry.getKey().toString(), commandSubList.build()); + } else if (commandEntry.getValue() != null) { + commandBuilder.put(commandEntry.getKey().toString(), commandEntry.getValue()); + } + } + } + commandsBuilder.put(command.getKey().toString(), commandBuilder.build()); + } + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "commands are of wrong type"); + } + commands = commandsBuilder.build(); + } + + if (map.get("class-loader-of") != null) { + classLoaderOf = map.get("class-loader-of").toString(); + } + + depend = makePluginNameList(map, "depend"); + softDepend = makePluginNameList(map, "softdepend"); + loadBefore = makePluginNameList(map, "loadbefore"); + + if (map.get("database") != null) { + try { + database = (Boolean) map.get("database"); + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "database is of wrong type"); + } + } + + if (map.get("website") != null) { + website = map.get("website").toString(); + } + + if (map.get("description") != null) { + description = map.get("description").toString(); + } + + if (map.get("load") != null) { + try { + order = PluginLoadOrder.valueOf(((String) map.get("load")).toUpperCase().replaceAll("\\W", "")); + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "load is of wrong type"); + } catch (IllegalArgumentException ex) { + throw new InvalidDescriptionException(ex, "load is not a valid choice"); + } + } + + if (map.get("authors") != null) { + ImmutableList.Builder authorsBuilder = ImmutableList.builder(); + if (map.get("author") != null) { + authorsBuilder.add(map.get("author").toString()); + } + try { + for (Object o : (Iterable) map.get("authors")) { + authorsBuilder.add(o.toString()); + } + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "authors are of wrong type"); + } catch (NullPointerException ex) { + throw new InvalidDescriptionException(ex, "authors are improperly defined"); + } + authors = authorsBuilder.build(); + } else if (map.get("author") != null) { + authors = ImmutableList.of(map.get("author").toString()); + } else { + authors = ImmutableList.of(); + } + + if (map.get("default-permission") != null) { + try { + defaultPerm = PermissionDefault.getByName(map.get("default-permission").toString()); + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "default-permission is of wrong type"); + } catch (IllegalArgumentException ex) { + throw new InvalidDescriptionException(ex, "default-permission is not a valid choice"); + } + } + + if (map.get("awareness") instanceof Iterable) { + Set awareness = new HashSet(); + try { + for (Object o : (Iterable) map.get("awareness")) { + awareness.add((PluginAwareness) o); + } + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "awareness has wrong type"); + } + this.awareness = ImmutableSet.copyOf(awareness); + } + + try { + lazyPermissions = (Map) map.get("permissions"); + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, "permissions are of the wrong type"); + } + + if (map.get("prefix") != null) { + prefix = map.get("prefix").toString(); + } + } + + private static List makePluginNameList(final Map map, final String key) throws InvalidDescriptionException { + final Object value = map.get(key); + if (value == null) { + return ImmutableList.of(); + } + + final ImmutableList.Builder builder = ImmutableList.builder(); + try { + for (final Object entry : (Iterable) value) { + builder.add(entry.toString().replace(' ', '_')); + } + } catch (ClassCastException ex) { + throw new InvalidDescriptionException(ex, key + " is of wrong type"); + } catch (NullPointerException ex) { + throw new InvalidDescriptionException(ex, "invalid " + key + " format"); + } + return builder.build(); + } + + private Map saveMap() { + Map map = new HashMap(); + + map.put("name", name); + map.put("main", main); + map.put("version", version); + map.put("database", database); + map.put("order", order.toString()); + map.put("default-permission", defaultPerm.toString()); + + if (commands != null) { + map.put("command", commands); + } + if (depend != null) { + map.put("depend", depend); + } + if (softDepend != null) { + map.put("softdepend", softDepend); + } + if (website != null) { + map.put("website", website); + } + if (description != null) { + map.put("description", description); + } + + if (authors.size() == 1) { + map.put("author", authors.get(0)); + } else if (authors.size() > 1) { + map.put("authors", authors); + } + + if (classLoaderOf != null) { + map.put("class-loader-of", classLoaderOf); + } + + if (prefix != null) { + map.put("prefix", prefix); + } + + return map; + } + + private Map asMap(Object object) throws InvalidDescriptionException { + if (object instanceof Map) { + return (Map) object; + } + throw new InvalidDescriptionException(object + " is not properly structured."); + } + + /** + * @deprecated Internal use + */ + @Deprecated + public String getRawName() { + return rawName; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/PluginLoadOrder.java b/vspigot-api/src/main/java/org/bukkit/plugin/PluginLoadOrder.java new file mode 100644 index 0000000..b77436f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/PluginLoadOrder.java @@ -0,0 +1,17 @@ +package org.bukkit.plugin; + +/** + * Represents the order in which a plugin should be initialized and enabled + */ +public enum PluginLoadOrder { + + /** + * Indicates that the plugin will be loaded at startup + */ + STARTUP, + /** + * Indicates that the plugin will be loaded after the first/default world + * was created + */ + POSTWORLD +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/PluginLoader.java b/vspigot-api/src/main/java/org/bukkit/plugin/PluginLoader.java new file mode 100644 index 0000000..e7981a1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/PluginLoader.java @@ -0,0 +1,76 @@ +package org.bukkit.plugin; + +import java.io.File; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.bukkit.event.Event; +import org.bukkit.event.Listener; + +/** + * Represents a plugin loader, which handles direct access to specific types + * of plugins + */ +public interface PluginLoader { + + /** + * Loads the plugin contained in the specified file + * + * @param file File to attempt to load + * @return Plugin that was contained in the specified file, or null if + * unsuccessful + * @throws InvalidPluginException Thrown when the specified file is not a + * plugin + * @throws UnknownDependencyException If a required dependency could not + * be found + */ + public Plugin loadPlugin(File file) throws InvalidPluginException, UnknownDependencyException; + + /** + * Loads a PluginDescriptionFile from the specified file + * + * @param file File to attempt to load from + * @return A new PluginDescriptionFile loaded from the plugin.yml in the + * specified file + * @throws InvalidDescriptionException If the plugin description file + * could not be created + */ + public PluginDescriptionFile getPluginDescription(File file) throws InvalidDescriptionException; + + /** + * Returns a list of all filename filters expected by this PluginLoader + * + * @return The filters + */ + public Pattern[] getPluginFileFilters(); + + /** + * Creates and returns registered listeners for the event classes used in + * this listener + * + * @param listener The object that will handle the eventual call back + * @param plugin The plugin to use when creating registered listeners + * @return The registered listeners. + */ + public Map, Set> createRegisteredListeners(Listener listener, Plugin plugin); + + /** + * Enables the specified plugin + *

            + * Attempting to enable a plugin that is already enabled will have no + * effect + * + * @param plugin Plugin to enable + */ + public void enablePlugin(Plugin plugin); + + /** + * Disables the specified plugin + *

            + * Attempting to disable a plugin that is not enabled will have no effect + * + * @param plugin Plugin to disable + */ + public void disablePlugin(Plugin plugin); +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/PluginLogger.java b/vspigot-api/src/main/java/org/bukkit/plugin/PluginLogger.java new file mode 100644 index 0000000..f43c10b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/PluginLogger.java @@ -0,0 +1,36 @@ +package org.bukkit.plugin; + +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * The PluginLogger class is a modified {@link Logger} that prepends all + * logging calls with the name of the plugin doing the logging. The API for + * PluginLogger is exactly the same as {@link Logger}. + * + * @see Logger + */ +public class PluginLogger extends Logger { + private String pluginName; + + /** + * Creates a new PluginLogger that extracts the name from a plugin. + * + * @param context A reference to the plugin + */ + public PluginLogger(Plugin context) { + super(context.getClass().getCanonicalName(), null); + String prefix = context.getDescription().getPrefix(); + pluginName = prefix != null ? new StringBuilder().append("[").append(prefix).append("] ").toString() : "[" + context.getDescription().getName() + "] "; + setParent(context.getServer().getLogger()); + setLevel(Level.ALL); + } + + @Override + public void log(LogRecord logRecord) { + logRecord.setMessage(pluginName + logRecord.getMessage()); + super.log(logRecord); + } + +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/PluginManager.java b/vspigot-api/src/main/java/org/bukkit/plugin/PluginManager.java new file mode 100644 index 0000000..e5638d5 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/PluginManager.java @@ -0,0 +1,294 @@ +package org.bukkit.plugin; + +import java.io.File; +import java.util.Set; + +import org.bukkit.event.Event; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.permissions.Permissible; +import org.bukkit.permissions.Permission; + +/** + * Handles all plugin management from the Server + */ +public interface PluginManager { + + /** + * Registers the specified plugin loader + * + * @param loader Class name of the PluginLoader to register + * @throws IllegalArgumentException Thrown when the given Class is not a + * valid PluginLoader + */ + public void registerInterface(Class loader) throws IllegalArgumentException; + + /** + * Checks if the given plugin is loaded and returns it when applicable + *

            + * Please note that the name of the plugin is case-sensitive + * + * @param name Name of the plugin to check + * @return Plugin if it exists, otherwise null + */ + public Plugin getPlugin(String name); + + /** + * Gets a list of all currently loaded plugins + * + * @return Array of Plugins + */ + public Plugin[] getPlugins(); + + /** + * Checks if the given plugin is enabled or not + *

            + * Please note that the name of the plugin is case-sensitive. + * + * @param name Name of the plugin to check + * @return true if the plugin is enabled, otherwise false + */ + public boolean isPluginEnabled(String name); + + /** + * Checks if the given plugin is enabled or not + * + * @param plugin Plugin to check + * @return true if the plugin is enabled, otherwise false + */ + public boolean isPluginEnabled(Plugin plugin); + + /** + * Loads the plugin in the specified file + *

            + * File must be valid according to the current enabled Plugin interfaces + * + * @param file File containing the plugin to load + * @return The Plugin loaded, or null if it was invalid + * @throws InvalidPluginException Thrown when the specified file is not a + * valid plugin + * @throws InvalidDescriptionException Thrown when the specified file + * contains an invalid description + * @throws UnknownDependencyException If a required dependency could not + * be resolved + */ + public Plugin loadPlugin(File file) throws InvalidPluginException, InvalidDescriptionException, UnknownDependencyException; + + /** + * Loads the plugins contained within the specified directory + * + * @param directory Directory to check for plugins + * @return A list of all plugins loaded + */ + public Plugin[] loadPlugins(File directory); + + /** + * Disables all the loaded plugins + */ + public void disablePlugins(); + + /** + * Disables and removes all plugins + */ + public void clearPlugins(); + + /** + * Calls an event with the given details + * + * @param event Event details + * @throws IllegalStateException Thrown when an asynchronous event is + * fired from synchronous code. + *

            + * Note: This is best-effort basis, and should not be used to test + * synchronized state. This is an indicator for flawed flow logic. + */ + public void callEvent(Event event) throws IllegalStateException; + + /** + * Registers all the events in the given listener class + * + * @param listener Listener to register + * @param plugin Plugin to register + */ + public void registerEvents(Listener listener, Plugin plugin); + + /** + * Registers the specified executor to the given event class + * + * @param event Event type to register + * @param listener Listener to register + * @param priority Priority to register this event at + * @param executor EventExecutor to register + * @param plugin Plugin to register + */ + public void registerEvent(Class event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin); + + /** + * Registers the specified executor to the given event class + * + * @param event Event type to register + * @param listener Listener to register + * @param priority Priority to register this event at + * @param executor EventExecutor to register + * @param plugin Plugin to register + * @param ignoreCancelled Whether to pass cancelled events or not + */ + public void registerEvent(Class event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin, boolean ignoreCancelled); + + /** + * Enables the specified plugin + *

            + * Attempting to enable a plugin that is already enabled will have no + * effect + * + * @param plugin Plugin to enable + */ + public void enablePlugin(Plugin plugin); + + /** + * Disables the specified plugin + *

            + * Attempting to disable a plugin that is not enabled will have no effect + * + * @param plugin Plugin to disable + */ + public void disablePlugin(Plugin plugin); + + /** + * Gets a {@link Permission} from its fully qualified name + * + * @param name Name of the permission + * @return Permission, or null if none + */ + public Permission getPermission(String name); + + /** + * Adds a {@link Permission} to this plugin manager. + *

            + * If a permission is already defined with the given name of the new + * permission, an exception will be thrown. + * + * @param perm Permission to add + * @throws IllegalArgumentException Thrown when a permission with the same + * name already exists + */ + public void addPermission(Permission perm); + + /** + * Removes a {@link Permission} registration from this plugin manager. + *

            + * If the specified permission does not exist in this plugin manager, + * nothing will happen. + *

            + * Removing a permission registration will not remove the + * permission from any {@link Permissible}s that have it. + * + * @param perm Permission to remove + */ + public void removePermission(Permission perm); + + /** + * Removes a {@link Permission} registration from this plugin manager. + *

            + * If the specified permission does not exist in this plugin manager, + * nothing will happen. + *

            + * Removing a permission registration will not remove the + * permission from any {@link Permissible}s that have it. + * + * @param name Permission to remove + */ + public void removePermission(String name); + + /** + * Gets the default permissions for the given op status + * + * @param op Which set of default permissions to get + * @return The default permissions + */ + public Set getDefaultPermissions(boolean op); + + /** + * Recalculates the defaults for the given {@link Permission}. + *

            + * This will have no effect if the specified permission is not registered + * here. + * + * @param perm Permission to recalculate + */ + public void recalculatePermissionDefaults(Permission perm); + + /** + * Subscribes the given Permissible for information about the requested + * Permission, by name. + *

            + * If the specified Permission changes in any form, the Permissible will + * be asked to recalculate. + * + * @param permission Permission to subscribe to + * @param permissible Permissible subscribing + */ + public void subscribeToPermission(String permission, Permissible permissible); + + /** + * Unsubscribes the given Permissible for information about the requested + * Permission, by name. + * + * @param permission Permission to unsubscribe from + * @param permissible Permissible subscribing + */ + public void unsubscribeFromPermission(String permission, Permissible permissible); + + /** + * Gets a set containing all subscribed {@link Permissible}s to the given + * permission, by name + * + * @param permission Permission to query for + * @return Set containing all subscribed permissions + */ + public Set getPermissionSubscriptions(String permission); + + /** + * Subscribes to the given Default permissions by operator status + *

            + * If the specified defaults change in any form, the Permissible will be + * asked to recalculate. + * + * @param op Default list to subscribe to + * @param permissible Permissible subscribing + */ + public void subscribeToDefaultPerms(boolean op, Permissible permissible); + + /** + * Unsubscribes from the given Default permissions by operator status + * + * @param op Default list to unsubscribe from + * @param permissible Permissible subscribing + */ + public void unsubscribeFromDefaultPerms(boolean op, Permissible permissible); + + /** + * Gets a set containing all subscribed {@link Permissible}s to the given + * default list, by op status + * + * @param op Default list to query for + * @return Set containing all subscribed permissions + */ + public Set getDefaultPermSubscriptions(boolean op); + + /** + * Gets a set of all registered permissions. + *

            + * This set is a copy and will not be modified live. + * + * @return Set containing all current registered permissions + */ + public Set getPermissions(); + + /** + * Returns whether or not timing code should be used for event calls + * + * @return True if event timings are to be used + */ + public boolean useTimings(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/RegisteredListener.java b/vspigot-api/src/main/java/org/bukkit/plugin/RegisteredListener.java new file mode 100644 index 0000000..9dd0b7a --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/RegisteredListener.java @@ -0,0 +1,73 @@ +package org.bukkit.plugin; + +import org.bukkit.event.*; + +/** + * Stores relevant information for plugin listeners + */ +public class RegisteredListener { + private final Listener listener; + private final EventPriority priority; + private final Plugin plugin; + private final EventExecutor executor; + private final boolean ignoreCancelled; + + public RegisteredListener(final Listener listener, final EventExecutor executor, final EventPriority priority, final Plugin plugin, final boolean ignoreCancelled) { + this.listener = listener; + this.priority = priority; + this.plugin = plugin; + this.executor = executor; + this.ignoreCancelled = ignoreCancelled; + } + + /** + * Gets the listener for this registration + * + * @return Registered Listener + */ + public Listener getListener() { + return listener; + } + + /** + * Gets the plugin for this registration + * + * @return Registered Plugin + */ + public Plugin getPlugin() { + return plugin; + } + + /** + * Gets the priority for this registration + * + * @return Registered Priority + */ + public EventPriority getPriority() { + return priority; + } + + /** + * Calls the event executor + * + * @param event The event + * @throws EventException If an event handler throws an exception. + */ + public void callEvent(final Event event) throws EventException { + if (event instanceof Cancellable){ + if (((Cancellable) event).isCancelled() && isIgnoringCancelled()){ + return; + } + } + executor.execute(listener, event); + } + + /** + * Whether this listener accepts cancelled events + * + * @return True when ignoring cancelled events + */ + public boolean isIgnoringCancelled() { + return ignoreCancelled; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/RegisteredServiceProvider.java b/vspigot-api/src/main/java/org/bukkit/plugin/RegisteredServiceProvider.java new file mode 100644 index 0000000..ba3ff15 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/RegisteredServiceProvider.java @@ -0,0 +1,46 @@ +package org.bukkit.plugin; + +/** + * A registered service provider. + * + * @param Service + */ +public class RegisteredServiceProvider implements Comparable> { + + private Class service; + private Plugin plugin; + private T provider; + private ServicePriority priority; + + public RegisteredServiceProvider(Class service, T provider, ServicePriority priority, Plugin plugin) { + + this.service = service; + this.plugin = plugin; + this.provider = provider; + this.priority = priority; + } + + public Class getService() { + return service; + } + + public Plugin getPlugin() { + return plugin; + } + + public T getProvider() { + return provider; + } + + public ServicePriority getPriority() { + return priority; + } + + public int compareTo(RegisteredServiceProvider other) { + if (priority.ordinal() == other.getPriority().ordinal()) { + return 0; + } else { + return priority.ordinal() < other.getPriority().ordinal() ? 1 : -1; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/ServicePriority.java b/vspigot-api/src/main/java/org/bukkit/plugin/ServicePriority.java new file mode 100644 index 0000000..4afe0fb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/ServicePriority.java @@ -0,0 +1,12 @@ +package org.bukkit.plugin; + +/** + * Represents various priorities of a provider. + */ +public enum ServicePriority { + Lowest, + Low, + Normal, + High, + Highest +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/ServicesManager.java b/vspigot-api/src/main/java/org/bukkit/plugin/ServicesManager.java new file mode 100644 index 0000000..5d45ffb --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/ServicesManager.java @@ -0,0 +1,106 @@ +package org.bukkit.plugin; + +import java.util.Collection; +import java.util.List; + +/** + * Manages services and service providers. Services are an interface + * specifying a list of methods that a provider must implement. Providers are + * implementations of these services. A provider can be queried from the + * services manager in order to use a service (if one is available). If + * multiple plugins register a service, then the service with the highest + * priority takes precedence. + */ +public interface ServicesManager { + + /** + * Register a provider of a service. + * + * @param Provider + * @param service service class + * @param provider provider to register + * @param plugin plugin with the provider + * @param priority priority of the provider + */ + public void register(Class service, T provider, Plugin plugin, ServicePriority priority); + + /** + * Unregister all the providers registered by a particular plugin. + * + * @param plugin The plugin + */ + public void unregisterAll(Plugin plugin); + + /** + * Unregister a particular provider for a particular service. + * + * @param service The service interface + * @param provider The service provider implementation + */ + public void unregister(Class service, Object provider); + + /** + * Unregister a particular provider. + * + * @param provider The service provider implementation + */ + public void unregister(Object provider); + + /** + * Queries for a provider. This may return if no provider has been + * registered for a service. The highest priority provider is returned. + * + * @param The service interface + * @param service The service interface + * @return provider or null + */ + public T load(Class service); + + /** + * Queries for a provider registration. This may return if no provider + * has been registered for a service. + * + * @param The service interface + * @param service The service interface + * @return provider registration or null + */ + public RegisteredServiceProvider getRegistration(Class service); + + /** + * Get registrations of providers for a plugin. + * + * @param plugin The plugin + * @return provider registration or null + */ + public List> getRegistrations(Plugin plugin); + + /** + * Get registrations of providers for a service. The returned list is + * unmodifiable. + * + * @param The service interface + * @param service The service interface + * @return list of registrations + */ + public Collection> getRegistrations(Class service); + + /** + * Get a list of known services. A service is known if it has registered + * providers for it. + * + * @return list of known services + */ + public Collection> getKnownServices(); + + /** + * Returns whether a provider has been registered for a service. Do not + * check this first only to call load(service) later, as that + * would be a non-thread safe situation. + * + * @param service + * @param service service to check + * @return whether there has been a registered provider + */ + public boolean isProvidedFor(Class service); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/vspigot-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java new file mode 100644 index 0000000..0b53084 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -0,0 +1,743 @@ +package org.bukkit.plugin; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.Validate; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.PluginCommandYamlParser; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.event.Event; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.permissions.Permissible; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.util.FileUtil; + +import com.google.common.collect.ImmutableSet; + +/** + * Handles all plugin management from the Server + */ +public final class SimplePluginManager implements PluginManager { + private final Server server; + private final Map fileAssociations = new HashMap(); + private final List plugins = new ArrayList(); + private final Map lookupNames = new HashMap(); + private static File updateDirectory = null; + private final SimpleCommandMap commandMap; + private final Map permissions = new HashMap(); + private final Map> defaultPerms = new LinkedHashMap>(); + private final Map> permSubs = new HashMap>(); + private final Map> defSubs = new HashMap>(); + private boolean useTimings = false; + + public SimplePluginManager(Server instance, SimpleCommandMap commandMap) { + server = instance; + this.commandMap = commandMap; + + defaultPerms.put(true, new HashSet()); + defaultPerms.put(false, new HashSet()); + } + + /** + * Registers the specified plugin loader + * + * @param loader Class name of the PluginLoader to register + * @throws IllegalArgumentException Thrown when the given Class is not a + * valid PluginLoader + */ + public void registerInterface(Class loader) throws IllegalArgumentException { + PluginLoader instance; + + if (PluginLoader.class.isAssignableFrom(loader)) { + Constructor constructor; + + try { + constructor = loader.getConstructor(Server.class); + instance = constructor.newInstance(server); + } catch (NoSuchMethodException ex) { + String className = loader.getName(); + + throw new IllegalArgumentException(String.format("Class %s does not have a public %s(Server) constructor", className, className), ex); + } catch (Exception ex) { + throw new IllegalArgumentException(String.format("Unexpected exception %s while attempting to construct a new instance of %s", ex.getClass().getName(), loader.getName()), ex); + } + } else { + throw new IllegalArgumentException(String.format("Class %s does not implement interface PluginLoader", loader.getName())); + } + + Pattern[] patterns = instance.getPluginFileFilters(); + + synchronized (this) { + for (Pattern pattern : patterns) { + fileAssociations.put(pattern, instance); + } + } + } + + /** + * Loads the plugins contained within the specified directory + * + * @param directory Directory to check for plugins + * @return A list of all plugins loaded + */ + public Plugin[] loadPlugins(File directory) { + Validate.notNull(directory, "Directory cannot be null"); + Validate.isTrue(directory.isDirectory(), "Directory must be a directory"); + + List result = new ArrayList(); + Set filters = fileAssociations.keySet(); + + if (!(server.getUpdateFolder().equals(""))) { + updateDirectory = new File(directory, server.getUpdateFolder()); + } + + Map plugins = new HashMap(); + Set loadedPlugins = new HashSet(); + Map> dependencies = new HashMap>(); + Map> softDependencies = new HashMap>(); + + // This is where it figures out all possible plugins + for (File file : directory.listFiles()) { + PluginLoader loader = null; + for (Pattern filter : filters) { + Matcher match = filter.matcher(file.getName()); + if (match.find()) { + loader = fileAssociations.get(filter); + } + } + + if (loader == null) continue; + + PluginDescriptionFile description = null; + try { + description = loader.getPluginDescription(file); + String name = description.getName(); + // Spigot Start + if ( name.equalsIgnoreCase( "Orebfuscator" ) ) + { + server.getLogger().log( Level.WARNING, "Skipping loading of Orebfuscator as it does not work with Spigot 1.8 builds!" ); + continue; + } + if ( name.equalsIgnoreCase( "EchoPet" ) ) + { + server.getLogger().log( Level.WARNING, "Skipping loading of EchoPet as it does not work with Spigot 1.8 builds!" ); + continue; + } + // Spigot End + if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang")) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': Restricted Name"); + continue; + } else if (description.rawName.indexOf(' ') != -1) { + server.getLogger().warning(String.format( + "Plugin `%s' uses the space-character (0x20) in its name `%s' - this is discouraged", + description.getFullName(), + description.rawName + )); + } + } catch (InvalidDescriptionException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex); + continue; + } + + File replacedFile = plugins.put(description.getName(), file); + if (replacedFile != null) { + server.getLogger().severe(String.format( + "Ambiguous plugin name `%s' for files `%s' and `%s' in `%s'", + description.getName(), + file.getPath(), + replacedFile.getPath(), + directory.getPath() + )); + } + + Collection softDependencySet = description.getSoftDepend(); + if (softDependencySet != null && !softDependencySet.isEmpty()) { + if (softDependencies.containsKey(description.getName())) { + // Duplicates do not matter, they will be removed together if applicable + softDependencies.get(description.getName()).addAll(softDependencySet); + } else { + softDependencies.put(description.getName(), new LinkedList(softDependencySet)); + } + } + + Collection dependencySet = description.getDepend(); + if (dependencySet != null && !dependencySet.isEmpty()) { + dependencies.put(description.getName(), new LinkedList(dependencySet)); + } + + Collection loadBeforeSet = description.getLoadBefore(); + if (loadBeforeSet != null && !loadBeforeSet.isEmpty()) { + for (String loadBeforeTarget : loadBeforeSet) { + if (softDependencies.containsKey(loadBeforeTarget)) { + softDependencies.get(loadBeforeTarget).add(description.getName()); + } else { + // softDependencies is never iterated, so 'ghost' plugins aren't an issue + Collection shortSoftDependency = new LinkedList(); + shortSoftDependency.add(description.getName()); + softDependencies.put(loadBeforeTarget, shortSoftDependency); + } + } + } + } + + while (!plugins.isEmpty()) { + boolean missingDependency = true; + Iterator pluginIterator = plugins.keySet().iterator(); + + while (pluginIterator.hasNext()) { + String plugin = pluginIterator.next(); + + if (dependencies.containsKey(plugin)) { + Iterator dependencyIterator = dependencies.get(plugin).iterator(); + + while (dependencyIterator.hasNext()) { + String dependency = dependencyIterator.next(); + + // Dependency loaded + if (loadedPlugins.contains(dependency)) { + dependencyIterator.remove(); + + // We have a dependency not found + } else if (!plugins.containsKey(dependency)) { + missingDependency = false; + File file = plugins.get(plugin); + pluginIterator.remove(); + softDependencies.remove(plugin); + dependencies.remove(plugin); + + server.getLogger().log( + Level.SEVERE, + "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", + new UnknownDependencyException(dependency)); + break; + } + } + + if (dependencies.containsKey(plugin) && dependencies.get(plugin).isEmpty()) { + dependencies.remove(plugin); + } + } + if (softDependencies.containsKey(plugin)) { + Iterator softDependencyIterator = softDependencies.get(plugin).iterator(); + + while (softDependencyIterator.hasNext()) { + String softDependency = softDependencyIterator.next(); + + // Soft depend is no longer around + if (!plugins.containsKey(softDependency)) { + softDependencyIterator.remove(); + } + } + + if (softDependencies.get(plugin).isEmpty()) { + softDependencies.remove(plugin); + } + } + if (!(dependencies.containsKey(plugin) || softDependencies.containsKey(plugin)) && plugins.containsKey(plugin)) { + // We're clear to load, no more soft or hard dependencies left + File file = plugins.get(plugin); + pluginIterator.remove(); + missingDependency = false; + + try { + result.add(loadPlugin(file)); + loadedPlugins.add(plugin); + continue; + } catch (InvalidPluginException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex); + } + } + } + + if (missingDependency) { + // We now iterate over plugins until something loads + // This loop will ignore soft dependencies + pluginIterator = plugins.keySet().iterator(); + + while (pluginIterator.hasNext()) { + String plugin = pluginIterator.next(); + + if (!dependencies.containsKey(plugin)) { + softDependencies.remove(plugin); + missingDependency = false; + File file = plugins.get(plugin); + pluginIterator.remove(); + + try { + result.add(loadPlugin(file)); + loadedPlugins.add(plugin); + break; + } catch (InvalidPluginException ex) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex); + } + } + } + // We have no plugins left without a depend + if (missingDependency) { + softDependencies.clear(); + dependencies.clear(); + Iterator failedPluginIterator = plugins.values().iterator(); + + while (failedPluginIterator.hasNext()) { + File file = failedPluginIterator.next(); + failedPluginIterator.remove(); + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected"); + } + } + } + } + + org.bukkit.command.defaults.TimingsCommand.timingStart = System.nanoTime(); // Spigot + return result.toArray(new Plugin[result.size()]); + } + + /** + * Loads the plugin in the specified file + *

            + * File must be valid according to the current enabled Plugin interfaces + * + * @param file File containing the plugin to load + * @return The Plugin loaded, or null if it was invalid + * @throws InvalidPluginException Thrown when the specified file is not a + * valid plugin + * @throws UnknownDependencyException If a required dependency could not + * be found + */ + public synchronized Plugin loadPlugin(File file) throws InvalidPluginException, UnknownDependencyException { + Validate.notNull(file, "File cannot be null"); + + checkUpdate(file); + + Set filters = fileAssociations.keySet(); + Plugin result = null; + + for (Pattern filter : filters) { + String name = file.getName(); + Matcher match = filter.matcher(name); + + if (match.find()) { + PluginLoader loader = fileAssociations.get(filter); + + result = loader.loadPlugin(file); + } + } + + if (result != null) { + plugins.add(result); + lookupNames.put(result.getDescription().getName(), result); + } + + return result; + } + + private void checkUpdate(File file) { + if (updateDirectory == null || !updateDirectory.isDirectory()) { + return; + } + + File updateFile = new File(updateDirectory, file.getName()); + if (updateFile.isFile() && FileUtil.copy(updateFile, file)) { + updateFile.delete(); + } + } + + /** + * Checks if the given plugin is loaded and returns it when applicable + *

            + * Please note that the name of the plugin is case-sensitive + * + * @param name Name of the plugin to check + * @return Plugin if it exists, otherwise null + */ + public synchronized Plugin getPlugin(String name) { + return lookupNames.get(name.replace(' ', '_')); + } + + public synchronized Plugin[] getPlugins() { + return plugins.toArray(new Plugin[0]); + } + + /** + * Checks if the given plugin is enabled or not + *

            + * Please note that the name of the plugin is case-sensitive. + * + * @param name Name of the plugin to check + * @return true if the plugin is enabled, otherwise false + */ + public boolean isPluginEnabled(String name) { + Plugin plugin = getPlugin(name); + + return isPluginEnabled(plugin); + } + + /** + * Checks if the given plugin is enabled or not + * + * @param plugin Plugin to check + * @return true if the plugin is enabled, otherwise false + */ + public boolean isPluginEnabled(Plugin plugin) { + if ((plugin != null) && (plugins.contains(plugin))) { + return plugin.isEnabled(); + } else { + return false; + } + } + + public void enablePlugin(final Plugin plugin) { + if (!plugin.isEnabled()) { + List pluginCommands = PluginCommandYamlParser.parse(plugin); + + if (!pluginCommands.isEmpty()) { + commandMap.registerAll(plugin.getDescription().getName(), pluginCommands); + } + + try { + plugin.getPluginLoader().enablePlugin(plugin); + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + HandlerList.bakeAll(); + } + } + + public void disablePlugins() { + Plugin[] plugins = getPlugins(); + for (int i = plugins.length - 1; i >= 0; i--) { + disablePlugin(plugins[i]); + } + } + + public void disablePlugin(final Plugin plugin) { + if (plugin.isEnabled()) { + try { + plugin.getPluginLoader().disablePlugin(plugin); + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + try { + server.getScheduler().cancelTasks(plugin); + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while cancelling tasks for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + try { + server.getServicesManager().unregisterAll(plugin); + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering services for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + try { + HandlerList.unregisterAll(plugin); + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering events for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + try { + server.getMessenger().unregisterIncomingPluginChannel(plugin); + server.getMessenger().unregisterOutgoingPluginChannel(plugin); + } catch(Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering plugin channels for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + } + } + + public void clearPlugins() { + synchronized (this) { + disablePlugins(); + plugins.clear(); + lookupNames.clear(); + HandlerList.unregisterAll(); + fileAssociations.clear(); + permissions.clear(); + defaultPerms.get(true).clear(); + defaultPerms.get(false).clear(); + } + } + + /** + * Calls an event with the given details. + *

            + * This method only synchronizes when the event is not asynchronous. + * + * @param event Event details + */ + public void callEvent(Event event) { + if (event.isAsynchronous()) { + if (Thread.holdsLock(this)) { + throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code."); + } + if (server.isPrimaryThread()) { + throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from primary server thread."); + } + fireEvent(event); + } else { + synchronized (this) { + fireEvent(event); + } + } + } + + private void fireEvent(Event event) { + HandlerList handlers = event.getHandlers(); + RegisteredListener[] listeners = handlers.getRegisteredListeners(); + + for (RegisteredListener registration : listeners) { + if (!registration.getPlugin().isEnabled()) { + continue; + } + + try { + registration.callEvent(event); + } catch (AuthorNagException ex) { + Plugin plugin = registration.getPlugin(); + + if (plugin.isNaggable()) { + plugin.setNaggable(false); + + server.getLogger().log(Level.SEVERE, String.format( + "Nag author(s): '%s' of '%s' about the following: %s", + plugin.getDescription().getAuthors(), + plugin.getDescription().getFullName(), + ex.getMessage() + )); + } + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(), ex); + } + } + } + + public void registerEvents(Listener listener, Plugin plugin) { + if (!plugin.isEnabled()) { + throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled"); + } + + for (Map.Entry, Set> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()) { + getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue()); + } + + } + + public void registerEvent(Class event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin) { + registerEvent(event, listener, priority, executor, plugin, false); + } + + /** + * Registers the given event to the specified listener using a directly + * passed EventExecutor + * + * @param event Event class to register + * @param listener PlayerListener to register + * @param priority Priority of this event + * @param executor EventExecutor to register + * @param plugin Plugin to register + * @param ignoreCancelled Do not call executor if event was already + * cancelled + */ + public void registerEvent(Class event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin, boolean ignoreCancelled) { + Validate.notNull(listener, "Listener cannot be null"); + Validate.notNull(priority, "Priority cannot be null"); + Validate.notNull(executor, "Executor cannot be null"); + Validate.notNull(plugin, "Plugin cannot be null"); + + if (!plugin.isEnabled()) { + throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled"); + } + + if (useTimings) { + getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); + } else { + getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); + } + } + + private HandlerList getEventListeners(Class type) { + try { + Method method = getRegistrationClass(type).getDeclaredMethod("getHandlerList"); + method.setAccessible(true); + return (HandlerList) method.invoke(null); + } catch (Exception e) { + throw new IllegalPluginAccessException(e.toString()); + } + } + + private Class getRegistrationClass(Class clazz) { + try { + clazz.getDeclaredMethod("getHandlerList"); + return clazz; + } catch (NoSuchMethodException e) { + if (clazz.getSuperclass() != null + && !clazz.getSuperclass().equals(Event.class) + && Event.class.isAssignableFrom(clazz.getSuperclass())) { + return getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class)); + } else { + throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName()); + } + } + } + + public Permission getPermission(String name) { + return permissions.get(name.toLowerCase()); + } + + public void addPermission(Permission perm) { + String name = perm.getName().toLowerCase(); + + if (permissions.containsKey(name)) { + throw new IllegalArgumentException("The permission " + name + " is already defined!"); + } + + permissions.put(name, perm); + calculatePermissionDefault(perm); + } + + public Set getDefaultPermissions(boolean op) { + return ImmutableSet.copyOf(defaultPerms.get(op)); + } + + public void removePermission(Permission perm) { + removePermission(perm.getName()); + } + + public void removePermission(String name) { + permissions.remove(name.toLowerCase()); + } + + public void recalculatePermissionDefaults(Permission perm) { + if (permissions.containsValue(perm)) { + defaultPerms.get(true).remove(perm); + defaultPerms.get(false).remove(perm); + + calculatePermissionDefault(perm); + } + } + + private void calculatePermissionDefault(Permission perm) { + if ((perm.getDefault() == PermissionDefault.OP) || (perm.getDefault() == PermissionDefault.TRUE)) { + defaultPerms.get(true).add(perm); + dirtyPermissibles(true); + } + if ((perm.getDefault() == PermissionDefault.NOT_OP) || (perm.getDefault() == PermissionDefault.TRUE)) { + defaultPerms.get(false).add(perm); + dirtyPermissibles(false); + } + } + + private void dirtyPermissibles(boolean op) { + Set permissibles = getDefaultPermSubscriptions(op); + + for (Permissible p : permissibles) { + p.recalculatePermissions(); + } + } + + public void subscribeToPermission(String permission, Permissible permissible) { + String name = permission.toLowerCase(); + Map map = permSubs.get(name); + + if (map == null) { + map = new WeakHashMap(); + permSubs.put(name, map); + } + + map.put(permissible, true); + } + + public void unsubscribeFromPermission(String permission, Permissible permissible) { + String name = permission.toLowerCase(); + Map map = permSubs.get(name); + + if (map != null) { + map.remove(permissible); + + if (map.isEmpty()) { + permSubs.remove(name); + } + } + } + + public Set getPermissionSubscriptions(String permission) { + String name = permission.toLowerCase(); + Map map = permSubs.get(name); + + if (map == null) { + return ImmutableSet.of(); + } else { + return ImmutableSet.copyOf(map.keySet()); + } + } + + public void subscribeToDefaultPerms(boolean op, Permissible permissible) { + Map map = defSubs.get(op); + + if (map == null) { + map = new WeakHashMap(); + defSubs.put(op, map); + } + + map.put(permissible, true); + } + + public void unsubscribeFromDefaultPerms(boolean op, Permissible permissible) { + Map map = defSubs.get(op); + + if (map != null) { + map.remove(permissible); + + if (map.isEmpty()) { + defSubs.remove(op); + } + } + } + + public Set getDefaultPermSubscriptions(boolean op) { + Map map = defSubs.get(op); + + if (map == null) { + return ImmutableSet.of(); + } else { + return ImmutableSet.copyOf(map.keySet()); + } + } + + public Set getPermissions() { + return new HashSet(permissions.values()); + } + + public boolean useTimings() { + return useTimings; + } + + /** + * Sets whether or not per event timing code should be used + * + * @param use True if per event timing code should be used + */ + public void useTimings(boolean use) { + useTimings = use; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/SimpleServicesManager.java b/vspigot-api/src/main/java/org/bukkit/plugin/SimpleServicesManager.java new file mode 100644 index 0000000..4e17711 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/SimpleServicesManager.java @@ -0,0 +1,306 @@ +package org.bukkit.plugin; + +import org.bukkit.Bukkit; +import org.bukkit.event.server.ServiceRegisterEvent; +import org.bukkit.event.server.ServiceUnregisterEvent; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * A simple services manager. + */ +public class SimpleServicesManager implements ServicesManager { + + /** + * Map of providers. + */ + private final Map, List>> providers = new HashMap, List>>(); + + /** + * Register a provider of a service. + * + * @param Provider + * @param service service class + * @param provider provider to register + * @param plugin plugin with the provider + * @param priority priority of the provider + */ + public void register(Class service, T provider, Plugin plugin, ServicePriority priority) { + RegisteredServiceProvider registeredProvider = null; + synchronized (providers) { + List> registered = providers.get(service); + if (registered == null) { + registered = new ArrayList>(); + providers.put(service, registered); + } + + registeredProvider = new RegisteredServiceProvider(service, provider, priority, plugin); + + // Insert the provider into the collection, much more efficient big O than sort + int position = Collections.binarySearch(registered, registeredProvider); + if (position < 0) { + registered.add(-(position + 1), registeredProvider); + } else { + registered.add(position, registeredProvider); + } + + } + Bukkit.getServer().getPluginManager().callEvent(new ServiceRegisterEvent(registeredProvider)); + } + + /** + * Unregister all the providers registered by a particular plugin. + * + * @param plugin The plugin + */ + public void unregisterAll(Plugin plugin) { + ArrayList unregisteredEvents = new ArrayList(); + synchronized (providers) { + Iterator, List>>> it = providers.entrySet().iterator(); + + try { + while (it.hasNext()) { + Map.Entry, List>> entry = it.next(); + Iterator> it2 = entry.getValue().iterator(); + + try { + // Removed entries that are from this plugin + + while (it2.hasNext()) { + RegisteredServiceProvider registered = it2.next(); + + if (registered.getPlugin().equals(plugin)) { + it2.remove(); + unregisteredEvents.add(new ServiceUnregisterEvent(registered)); + } + } + } catch (NoSuchElementException e) { // Why does Java suck + } + + // Get rid of the empty list + if (entry.getValue().size() == 0) { + it.remove(); + } + } + } catch (NoSuchElementException e) {} + } + for (ServiceUnregisterEvent event : unregisteredEvents) { + Bukkit.getServer().getPluginManager().callEvent(event); + } + } + + /** + * Unregister a particular provider for a particular service. + * + * @param service The service interface + * @param provider The service provider implementation + */ + public void unregister(Class service, Object provider) { + ArrayList unregisteredEvents = new ArrayList(); + synchronized (providers) { + Iterator, List>>> it = providers.entrySet().iterator(); + + try { + while (it.hasNext()) { + Map.Entry, List>> entry = it.next(); + + // We want a particular service + if (entry.getKey() != service) { + continue; + } + + Iterator> it2 = entry.getValue().iterator(); + + try { + // Removed entries that are from this plugin + + while (it2.hasNext()) { + RegisteredServiceProvider registered = it2.next(); + + if (registered.getProvider() == provider) { + it2.remove(); + unregisteredEvents.add(new ServiceUnregisterEvent(registered)); + } + } + } catch (NoSuchElementException e) { // Why does Java suck + } + + // Get rid of the empty list + if (entry.getValue().size() == 0) { + it.remove(); + } + } + } catch (NoSuchElementException e) {} + } + for (ServiceUnregisterEvent event : unregisteredEvents) { + Bukkit.getServer().getPluginManager().callEvent(event); + } + } + + /** + * Unregister a particular provider. + * + * @param provider The service provider implementation + */ + public void unregister(Object provider) { + ArrayList unregisteredEvents = new ArrayList(); + synchronized (providers) { + Iterator, List>>> it = providers.entrySet().iterator(); + + try { + while (it.hasNext()) { + Map.Entry, List>> entry = it.next(); + Iterator> it2 = entry.getValue().iterator(); + + try { + // Removed entries that are from this plugin + + while (it2.hasNext()) { + RegisteredServiceProvider registered = it2.next(); + + if (registered.getProvider().equals(provider)) { + it2.remove(); + unregisteredEvents.add(new ServiceUnregisterEvent(registered)); + } + } + } catch (NoSuchElementException e) { // Why does Java suck + } + + // Get rid of the empty list + if (entry.getValue().size() == 0) { + it.remove(); + } + } + } catch (NoSuchElementException e) {} + } + for (ServiceUnregisterEvent event : unregisteredEvents) { + Bukkit.getServer().getPluginManager().callEvent(event); + } + } + + /** + * Queries for a provider. This may return if no provider has been + * registered for a service. The highest priority provider is returned. + * + * @param The service interface + * @param service The service interface + * @return provider or null + */ + public T load(Class service) { + synchronized (providers) { + List> registered = providers.get(service); + + if (registered == null) { + return null; + } + + // This should not be null! + return service.cast(registered.get(0).getProvider()); + } + } + + /** + * Queries for a provider registration. This may return if no provider + * has been registered for a service. + * + * @param The service interface + * @param service The service interface + * @return provider registration or null + */ + @SuppressWarnings("unchecked") + public RegisteredServiceProvider getRegistration(Class service) { + synchronized (providers) { + List> registered = providers.get(service); + + if (registered == null) { + return null; + } + + // This should not be null! + return (RegisteredServiceProvider) registered.get(0); + } + } + + /** + * Get registrations of providers for a plugin. + * + * @param plugin The plugin + * @return provider registration or null + */ + public List> getRegistrations(Plugin plugin) { + ImmutableList.Builder> ret = ImmutableList.>builder(); + synchronized (providers) { + for (List> registered : providers.values()) { + for (RegisteredServiceProvider provider : registered) { + if (provider.getPlugin().equals(plugin)) { + ret.add(provider); + } + } + } + } + return ret.build(); + } + + /** + * Get registrations of providers for a service. The returned list is + * an unmodifiable copy. + * + * @param The service interface + * @param service The service interface + * @return a copy of the list of registrations + */ + @SuppressWarnings("unchecked") + public List> getRegistrations(Class service) { + ImmutableList.Builder> ret; + synchronized (providers) { + List> registered = providers.get(service); + + if (registered == null) { + return ImmutableList.>of(); + } + + ret = ImmutableList.>builder(); + + for (RegisteredServiceProvider provider : registered) { + ret.add((RegisteredServiceProvider) provider); + } + + } + return ret.build(); + } + + /** + * Get a list of known services. A service is known if it has registered + * providers for it. + * + * @return a copy of the set of known services + */ + public Set> getKnownServices() { + synchronized (providers) { + return ImmutableSet.>copyOf(providers.keySet()); + } + } + + /** + * Returns whether a provider has been registered for a service. + * + * @param service + * @param service service to check + * @return true if and only if there are registered providers + */ + public boolean isProvidedFor(Class service) { + synchronized (providers) { + return providers.containsKey(service); + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/TimedRegisteredListener.java b/vspigot-api/src/main/java/org/bukkit/plugin/TimedRegisteredListener.java new file mode 100644 index 0000000..e09234c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/TimedRegisteredListener.java @@ -0,0 +1,97 @@ +package org.bukkit.plugin; + +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +/** + * Extends RegisteredListener to include timing information + */ +public class TimedRegisteredListener extends RegisteredListener { + private int count; + private long totalTime; + private Class eventClass; + private boolean multiple = false; + + public TimedRegisteredListener(final Listener pluginListener, final EventExecutor eventExecutor, final EventPriority eventPriority, final Plugin registeredPlugin, final boolean listenCancelled) { + super(pluginListener, eventExecutor, eventPriority, registeredPlugin, listenCancelled); + } + + @Override + public void callEvent(Event event) throws EventException { + if (event.isAsynchronous()) { + super.callEvent(event); + return; + } + count++; + Class newEventClass = event.getClass(); + if (this.eventClass == null) { + this.eventClass = newEventClass; + } else if (!this.eventClass.equals(newEventClass)) { + multiple = true; + this.eventClass = getCommonSuperclass(newEventClass, this.eventClass).asSubclass(Event.class); + } + long start = System.nanoTime(); + super.callEvent(event); + totalTime += System.nanoTime() - start; + } + + private static Class getCommonSuperclass(Class class1, Class class2) { + while (!class1.isAssignableFrom(class2)) { + class1 = class1.getSuperclass(); + } + return class1; + } + + /** + * Resets the call count and total time for this listener + */ + public void reset() { + count = 0; + totalTime = 0; + } + + /** + * Gets the total times this listener has been called + * + * @return Times this listener has been called + */ + public int getCount() { + return count; + } + + /** + * Gets the total time calls to this listener have taken + * + * @return Total time for all calls of this listener + */ + public long getTotalTime() { + return totalTime; + } + + /** + * Gets the class of the events this listener handled. If it handled + * multiple classes of event, the closest shared superclass will be + * returned, such that for any event this listener has handled, + * this.getEventClass().isAssignableFrom(event.getClass()) + * and no class this.getEventClass().isAssignableFrom(clazz) + * && this.getEventClass() != clazz && + * event.getClass().isAssignableFrom(clazz) for all handled events. + * + * @return the event class handled by this RegisteredListener + */ + public Class getEventClass() { + return eventClass; + } + + /** + * Gets whether this listener has handled multiple events, such that for + * some two events, eventA.getClass() != eventB.getClass(). + * + * @return true if this listener has handled multiple events + */ + public boolean hasMultiple() { + return multiple; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/UnknownDependencyException.java b/vspigot-api/src/main/java/org/bukkit/plugin/UnknownDependencyException.java new file mode 100644 index 0000000..a80251e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/UnknownDependencyException.java @@ -0,0 +1,46 @@ +package org.bukkit.plugin; + +/** + * Thrown when attempting to load an invalid Plugin file + */ +public class UnknownDependencyException extends RuntimeException { + + private static final long serialVersionUID = 5721389371901775895L; + + /** + * Constructs a new UnknownDependencyException based on the given + * Exception + * + * @param throwable Exception that triggered this Exception + */ + public UnknownDependencyException(final Throwable throwable) { + super(throwable); + } + + /** + * Constructs a new UnknownDependencyException with the given message + * + * @param message Brief message explaining the cause of the exception + */ + public UnknownDependencyException(final String message) { + super(message); + } + + /** + * Constructs a new UnknownDependencyException based on the given + * Exception + * + * @param message Brief message explaining the cause of the exception + * @param throwable Exception that triggered this Exception + */ + public UnknownDependencyException(final Throwable throwable, final String message) { + super(message, throwable); + } + + /** + * Constructs a new UnknownDependencyException + */ + public UnknownDependencyException() { + + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/vspigot-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java new file mode 100644 index 0000000..19893f3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java @@ -0,0 +1,546 @@ +package org.bukkit.plugin.java; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang.Validate; +import org.bukkit.Server; +import org.bukkit.Warning.WarningState; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.AuthorNagException; +import org.bukkit.plugin.PluginAwareness; +import org.bukkit.plugin.PluginBase; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.bukkit.plugin.PluginLogger; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.DataSourceConfig; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.ddl.DdlGenerator; +import com.google.common.base.Charsets; +import com.google.common.io.ByteStreams; + +/** + * Represents a Java plugin + */ +public abstract class JavaPlugin extends PluginBase { + private boolean isEnabled = false; + private PluginLoader loader = null; + private Server server = null; + private File file = null; + private PluginDescriptionFile description = null; + private File dataFolder = null; + private ClassLoader classLoader = null; + private boolean naggable = true; + private EbeanServer ebean = null; + private FileConfiguration newConfig = null; + private File configFile = null; + private PluginLogger logger = null; + + public JavaPlugin() { + final ClassLoader classLoader = this.getClass().getClassLoader(); + if (!(classLoader instanceof PluginClassLoader)) { + throw new IllegalStateException("JavaPlugin requires " + PluginClassLoader.class.getName()); + } + ((PluginClassLoader) classLoader).initialize(this); + } + + /** + * @deprecated This method is intended for unit testing purposes when the + * other {@linkplain #JavaPlugin(JavaPluginLoader, + * PluginDescriptionFile, File, File) constructor} cannot be used. + *

            + * Its existence may be temporary. + */ + @Deprecated + protected JavaPlugin(final PluginLoader loader, final Server server, final PluginDescriptionFile description, final File dataFolder, final File file) { + final ClassLoader classLoader = this.getClass().getClassLoader(); + if (classLoader instanceof PluginClassLoader) { + throw new IllegalStateException("Cannot use initialization constructor at runtime"); + } + init(loader, server, description, dataFolder, file, classLoader); + } + + protected JavaPlugin(final JavaPluginLoader loader, final PluginDescriptionFile description, final File dataFolder, final File file) { + final ClassLoader classLoader = this.getClass().getClassLoader(); + if (classLoader instanceof PluginClassLoader) { + throw new IllegalStateException("Cannot use initialization constructor at runtime"); + } + init(loader, loader.server, description, dataFolder, file, classLoader); + } + + /** + * Returns the folder that the plugin data's files are located in. The + * folder may not yet exist. + * + * @return The folder. + */ + @Override + public final File getDataFolder() { + return dataFolder; + } + + /** + * Gets the associated PluginLoader responsible for this plugin + * + * @return PluginLoader that controls this plugin + */ + @Override + public final PluginLoader getPluginLoader() { + return loader; + } + + /** + * Returns the Server instance currently running this plugin + * + * @return Server running this plugin + */ + @Override + public final Server getServer() { + return server; + } + + /** + * Returns a value indicating whether or not this plugin is currently + * enabled + * + * @return true if this plugin is enabled, otherwise false + */ + @Override + public final boolean isEnabled() { + return isEnabled; + } + + /** + * Returns the file which contains this plugin + * + * @return File containing this plugin + */ + protected File getFile() { + return file; + } + + /** + * Returns the plugin.yaml file containing the details for this plugin + * + * @return Contents of the plugin.yaml file + */ + @Override + public final PluginDescriptionFile getDescription() { + return description; + } + + @Override + public FileConfiguration getConfig() { + if (newConfig == null) { + reloadConfig(); + } + return newConfig; + } + + /** + * Provides a reader for a text file located inside the jar. The behavior + * of this method adheres to {@link PluginAwareness.Flags#UTF8}, or if not + * defined, uses UTF8 if {@link FileConfiguration#UTF8_OVERRIDE} is + * specified, or system default otherwise. + * + * @param file the filename of the resource to load + * @return null if {@link #getResource(String)} returns null + * @throws IllegalArgumentException if file is null + * @see ClassLoader#getResourceAsStream(String) + */ + @SuppressWarnings("deprecation") + protected final Reader getTextResource(String file) { + final InputStream in = getResource(file); + + return in == null ? null : new InputStreamReader(in, isStrictlyUTF8() || FileConfiguration.UTF8_OVERRIDE ? Charsets.UTF_8 : Charset.defaultCharset()); + } + + @SuppressWarnings("deprecation") + @Override + public void reloadConfig() { + newConfig = YamlConfiguration.loadConfiguration(configFile); + + final InputStream defConfigStream = getResource("config.yml"); + if (defConfigStream == null) { + return; + } + + final YamlConfiguration defConfig; + if (isStrictlyUTF8() || FileConfiguration.UTF8_OVERRIDE) { + defConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, Charsets.UTF_8)); + } else { + final byte[] contents; + defConfig = new YamlConfiguration(); + try { + contents = ByteStreams.toByteArray(defConfigStream); + } catch (final IOException e) { + getLogger().log(Level.SEVERE, "Unexpected failure reading config.yml", e); + return; + } + + final String text = new String(contents, Charset.defaultCharset()); + if (!text.equals(new String(contents, Charsets.UTF_8))) { + getLogger().warning("Default system encoding may have misread config.yml from plugin jar"); + } + + try { + defConfig.loadFromString(text); + } catch (final InvalidConfigurationException e) { + getLogger().log(Level.SEVERE, "Cannot load configuration from jar", e); + } + } + + newConfig.setDefaults(defConfig); + } + + private boolean isStrictlyUTF8() { + return getDescription().getAwareness().contains(PluginAwareness.Flags.UTF8); + } + + @Override + public void saveConfig() { + try { + getConfig().save(configFile); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Could not save config to " + configFile, ex); + } + } + + @Override + public void saveDefaultConfig() { + if (!configFile.exists()) { + saveResource("config.yml", false); + } + } + + @Override + public void saveResource(String resourcePath, boolean replace) { + if (resourcePath == null || resourcePath.equals("")) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + + resourcePath = resourcePath.replace('\\', '/'); + InputStream in = getResource(resourcePath); + if (in == null) { + throw new IllegalArgumentException("The embedded resource '" + resourcePath + "' cannot be found in " + file); + } + + File outFile = new File(dataFolder, resourcePath); + int lastIndex = resourcePath.lastIndexOf('/'); + File outDir = new File(dataFolder, resourcePath.substring(0, lastIndex >= 0 ? lastIndex : 0)); + + if (!outDir.exists()) { + outDir.mkdirs(); + } + + try { + if (!outFile.exists() || replace) { + OutputStream out = new FileOutputStream(outFile); + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.close(); + in.close(); + } else { + logger.log(Level.WARNING, "Could not save " + outFile.getName() + " to " + outFile + " because " + outFile.getName() + " already exists."); + } + } catch (IOException ex) { + logger.log(Level.SEVERE, "Could not save " + outFile.getName() + " to " + outFile, ex); + } + } + + @Override + public InputStream getResource(String filename) { + if (filename == null) { + throw new IllegalArgumentException("Filename cannot be null"); + } + + try { + URL url = getClassLoader().getResource(filename); + + if (url == null) { + return null; + } + + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + return connection.getInputStream(); + } catch (IOException ex) { + return null; + } + } + + /** + * Returns the ClassLoader which holds this plugin + * + * @return ClassLoader holding this plugin + */ + protected final ClassLoader getClassLoader() { + return classLoader; + } + + /** + * Sets the enabled state of this plugin + * + * @param enabled true if enabled, otherwise false + */ + protected final void setEnabled(final boolean enabled) { + if (isEnabled != enabled) { + isEnabled = enabled; + + if (isEnabled) { + onEnable(); + } else { + onDisable(); + } + } + } + + /** + * @deprecated This method is legacy and will be removed - it must be + * replaced by the specially provided constructor(s). + */ + @Deprecated + protected final void initialize(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) { + if (server.getWarningState() == WarningState.OFF) { + return; + } + getLogger().log(Level.WARNING, getClass().getName() + " is already initialized", server.getWarningState() == WarningState.DEFAULT ? null : new AuthorNagException("Explicit initialization")); + } + + final void init(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) { + this.loader = loader; + this.server = server; + this.file = file; + this.description = description; + this.dataFolder = dataFolder; + this.classLoader = classLoader; + this.configFile = new File(dataFolder, "config.yml"); + this.logger = new PluginLogger(this); + + if (description.isDatabaseEnabled()) { + ServerConfig db = new ServerConfig(); + + db.setDefaultServer(false); + db.setRegister(false); + db.setClasses(getDatabaseClasses()); + db.setName(description.getName()); + server.configureDbConfig(db); + + DataSourceConfig ds = db.getDataSourceConfig(); + + ds.setUrl(replaceDatabaseString(ds.getUrl())); + dataFolder.mkdirs(); + + ClassLoader previous = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader(classLoader); + ebean = EbeanServerFactory.create(db); + Thread.currentThread().setContextClassLoader(previous); + } + } + + /** + * Provides a list of all classes that should be persisted in the database + * + * @return List of Classes that are Ebeans + */ + public List> getDatabaseClasses() { + return new ArrayList>(); + } + + private String replaceDatabaseString(String input) { + input = input.replaceAll("\\{DIR\\}", dataFolder.getPath().replaceAll("\\\\", "/") + "/"); + input = input.replaceAll("\\{NAME\\}", description.getName().replaceAll("[^\\w_-]", "")); + return input; + } + + /** + * Gets the initialization status of this plugin + * + * @return true if this plugin is initialized, otherwise false + * @deprecated This method cannot return false, as {@link + * JavaPlugin} is now initialized in the constructor. + */ + @Deprecated + public final boolean isInitialized() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return null; + } + + /** + * Gets the command with the given name, specific to this plugin. Commands + * need to be registered in the {@link PluginDescriptionFile#getCommands() + * PluginDescriptionFile} to exist at runtime. + * + * @param name name or alias of the command + * @return the plugin command if found, otherwise null + */ + public PluginCommand getCommand(String name) { + String alias = name.toLowerCase(); + PluginCommand command = getServer().getPluginCommand(alias); + + if (command == null || command.getPlugin() != this) { + command = getServer().getPluginCommand(description.getName().toLowerCase() + ":" + alias); + } + + if (command != null && command.getPlugin() == this) { + return command; + } else { + return null; + } + } + + @Override + public void onLoad() {} + + @Override + public void onDisable() {} + + @Override + public void onEnable() {} + + @Override + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + return null; + } + + @Override + public final boolean isNaggable() { + return naggable; + } + + @Override + public final void setNaggable(boolean canNag) { + this.naggable = canNag; + } + + @Override + public EbeanServer getDatabase() { + return ebean; + } + + protected void installDDL() { + SpiEbeanServer serv = (SpiEbeanServer) getDatabase(); + DdlGenerator gen = serv.getDdlGenerator(); + + gen.runScript(false, gen.generateCreateDdl()); + } + + protected void removeDDL() { + SpiEbeanServer serv = (SpiEbeanServer) getDatabase(); + DdlGenerator gen = serv.getDdlGenerator(); + + gen.runScript(true, gen.generateDropDdl()); + } + + @Override + public final Logger getLogger() { + return logger; + } + + @Override + public String toString() { + return description.getFullName(); + } + + /** + * This method provides fast access to the plugin that has {@link + * #getProvidingPlugin(Class) provided} the given plugin class, which is + * usually the plugin that implemented it. + *

            + * An exception to this would be if plugin's jar that contained the class + * does not extend the class, where the intended plugin would have + * resided in a different jar / classloader. + * + * @param clazz the class desired + * @return the plugin that provides and implements said class + * @throws IllegalArgumentException if clazz is null + * @throws IllegalArgumentException if clazz does not extend {@link + * JavaPlugin} + * @throws IllegalStateException if clazz was not provided by a plugin, + * for example, if called with + * JavaPlugin.getPlugin(JavaPlugin.class) + * @throws IllegalStateException if called from the static initializer for + * given JavaPlugin + * @throws ClassCastException if plugin that provided the class does not + * extend the class + */ + public static T getPlugin(Class clazz) { + Validate.notNull(clazz, "Null class cannot have a plugin"); + if (!JavaPlugin.class.isAssignableFrom(clazz)) { + throw new IllegalArgumentException(clazz + " does not extend " + JavaPlugin.class); + } + final ClassLoader cl = clazz.getClassLoader(); + if (!(cl instanceof PluginClassLoader)) { + throw new IllegalArgumentException(clazz + " is not initialized by " + PluginClassLoader.class); + } + JavaPlugin plugin = ((PluginClassLoader) cl).plugin; + if (plugin == null) { + throw new IllegalStateException("Cannot get plugin for " + clazz + " from a static initializer"); + } + return clazz.cast(plugin); + } + + /** + * This method provides fast access to the plugin that has provided the + * given class. + * + * @throws IllegalArgumentException if the class is not provided by a + * JavaPlugin + * @throws IllegalArgumentException if class is null + * @throws IllegalStateException if called from the static initializer for + * given JavaPlugin + */ + public static JavaPlugin getProvidingPlugin(Class clazz) { + Validate.notNull(clazz, "Null class cannot have a plugin"); + final ClassLoader cl = clazz.getClassLoader(); + if (!(cl instanceof PluginClassLoader)) { + throw new IllegalArgumentException(clazz + " is not provided by " + PluginClassLoader.class); + } + JavaPlugin plugin = ((PluginClassLoader) cl).plugin; + if (plugin == null) { + throw new IllegalStateException("Cannot get plugin for " + clazz + " from a static initializer"); + } + return plugin; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/vspigot-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java new file mode 100644 index 0000000..fa1c475 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -0,0 +1,373 @@ +package org.bukkit.plugin.java; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.regex.Pattern; + +import org.apache.commons.lang.Validate; +import org.bukkit.Server; +import org.bukkit.Warning; +import org.bukkit.Warning.WarningState; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.plugin.AuthorNagException; +import org.bukkit.plugin.EventExecutor; +import org.bukkit.plugin.InvalidDescriptionException; +import org.bukkit.plugin.InvalidPluginException; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.bukkit.plugin.RegisteredListener; +import org.bukkit.plugin.TimedRegisteredListener; +import org.bukkit.plugin.UnknownDependencyException; +import org.spigotmc.CustomTimingsHandler; // Spigot +import org.yaml.snakeyaml.error.YAMLException; + +/** + * Represents a Java plugin loader, allowing plugins in the form of .jar + */ +public final class JavaPluginLoader implements PluginLoader { + final Server server; + private final Pattern[] fileFilters = new Pattern[] { Pattern.compile("\\.jar$"), }; + private final Map> classes = new java.util.concurrent.ConcurrentHashMap>(); // Spigot + private final Map loaders = new LinkedHashMap(); + public static final CustomTimingsHandler pluginParentTimer = new CustomTimingsHandler("** Plugins"); // Spigot + + /** + * This class was not meant to be constructed explicitly + */ + @Deprecated + public JavaPluginLoader(Server instance) { + Validate.notNull(instance, "Server cannot be null"); + server = instance; + } + + public Plugin loadPlugin(final File file) throws InvalidPluginException { + Validate.notNull(file, "File cannot be null"); + + if (!file.exists()) { + throw new InvalidPluginException(new FileNotFoundException(file.getPath() + " does not exist")); + } + + final PluginDescriptionFile description; + try { + description = getPluginDescription(file); + } catch (InvalidDescriptionException ex) { + throw new InvalidPluginException(ex); + } + + final File parentFile = file.getParentFile(); + final File dataFolder = new File("config", description.getName()); // MineHQ + @SuppressWarnings("deprecation") + final File oldDataFolder = new File("config", description.getRawName()); // MineHQ + + // Found old data folder + if (dataFolder.equals(oldDataFolder)) { + // They are equal -- nothing needs to be done! + } else if (dataFolder.isDirectory() && oldDataFolder.isDirectory()) { + server.getLogger().warning(String.format( + "While loading %s (%s) found old-data folder: `%s' next to the new one `%s'", + description.getFullName(), + file, + oldDataFolder, + dataFolder + )); + } else if (oldDataFolder.isDirectory() && !dataFolder.exists()) { + if (!oldDataFolder.renameTo(dataFolder)) { + throw new InvalidPluginException("Unable to rename old data folder: `" + oldDataFolder + "' to: `" + dataFolder + "'"); + } + server.getLogger().log(Level.INFO, String.format( + "While loading %s (%s) renamed data folder: `%s' to `%s'", + description.getFullName(), + file, + oldDataFolder, + dataFolder + )); + } + + if (dataFolder.exists() && !dataFolder.isDirectory()) { + throw new InvalidPluginException(String.format( + "Projected datafolder: `%s' for %s (%s) exists and is not a directory", + dataFolder, + description.getFullName(), + file + )); + } + + for (final String pluginName : description.getDepend()) { + if (loaders == null) { + throw new UnknownDependencyException(pluginName); + } + PluginClassLoader current = loaders.get(pluginName); + + if (current == null) { + throw new UnknownDependencyException(pluginName); + } + } + + final PluginClassLoader loader; + try { + loader = new PluginClassLoader(this, getClass().getClassLoader(), description, dataFolder, file); + } catch (InvalidPluginException ex) { + throw ex; + } catch (Throwable ex) { + throw new InvalidPluginException(ex); + } + + loaders.put(description.getName(), loader); + + return loader.plugin; + } + + public PluginDescriptionFile getPluginDescription(File file) throws InvalidDescriptionException { + Validate.notNull(file, "File cannot be null"); + + JarFile jar = null; + InputStream stream = null; + + try { + jar = new JarFile(file); + JarEntry entry = jar.getJarEntry("plugin.yml"); + + if (entry == null) { + throw new InvalidDescriptionException(new FileNotFoundException("Jar does not contain plugin.yml")); + } + + stream = jar.getInputStream(entry); + + return new PluginDescriptionFile(stream); + + } catch (IOException ex) { + throw new InvalidDescriptionException(ex); + } catch (YAMLException ex) { + throw new InvalidDescriptionException(ex); + } finally { + if (jar != null) { + try { + jar.close(); + } catch (IOException e) { + } + } + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + + public Pattern[] getPluginFileFilters() { + return fileFilters.clone(); + } + + Class getClassByName(final String name) { + Class cachedClass = classes.get(name); + + if (cachedClass != null) { + return cachedClass; + } else { + for (String current : loaders.keySet()) { + PluginClassLoader loader = loaders.get(current); + + try { + cachedClass = loader.findClass(name, false); + } catch (ClassNotFoundException cnfe) {} + if (cachedClass != null) { + return cachedClass; + } + } + } + return null; + } + + void setClass(final String name, final Class clazz) { + if (!classes.containsKey(name)) { + classes.put(name, clazz); + + if (ConfigurationSerializable.class.isAssignableFrom(clazz)) { + Class serializable = clazz.asSubclass(ConfigurationSerializable.class); + ConfigurationSerialization.registerClass(serializable); + } + } + } + + private void removeClass(String name) { + Class clazz = classes.remove(name); + + try { + if ((clazz != null) && (ConfigurationSerializable.class.isAssignableFrom(clazz))) { + Class serializable = clazz.asSubclass(ConfigurationSerializable.class); + ConfigurationSerialization.unregisterClass(serializable); + } + } catch (NullPointerException ex) { + // Boggle! + // (Native methods throwing NPEs is not fun when you can't stop it before-hand) + } + } + + public Map, Set> createRegisteredListeners(Listener listener, final Plugin plugin) { + Validate.notNull(plugin, "Plugin can not be null"); + Validate.notNull(listener, "Listener can not be null"); + + boolean useTimings = server.getPluginManager().useTimings(); + Map, Set> ret = new HashMap, Set>(); + Set methods; + try { + Method[] publicMethods = listener.getClass().getMethods(); + methods = new HashSet(publicMethods.length, Float.MAX_VALUE); + for (Method method : publicMethods) { + methods.add(method); + } + for (Method method : listener.getClass().getDeclaredMethods()) { + methods.add(method); + } + } catch (NoClassDefFoundError e) { + plugin.getLogger().severe("Plugin " + plugin.getDescription().getFullName() + " has failed to register events for " + listener.getClass() + " because " + e.getMessage() + " does not exist."); + return ret; + } + + for (final Method method : methods) { + final EventHandler eh = method.getAnnotation(EventHandler.class); + if (eh == null) continue; + final Class checkClass; + if (method.getParameterTypes().length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) { + plugin.getLogger().severe(plugin.getDescription().getFullName() + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass()); + continue; + } + final Class eventClass = checkClass.asSubclass(Event.class); + method.setAccessible(true); + Set eventSet = ret.get(eventClass); + if (eventSet == null) { + eventSet = new HashSet(); + ret.put(eventClass, eventSet); + } + + for (Class clazz = eventClass; Event.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) { + // This loop checks for extending deprecated events + if (clazz.getAnnotation(Deprecated.class) != null) { + Warning warning = clazz.getAnnotation(Warning.class); + WarningState warningState = server.getWarningState(); + if (!warningState.printFor(warning)) { + break; + } + plugin.getLogger().log( + Level.WARNING, + String.format( + "\"%s\" has registered a listener for %s on method \"%s\", but the event is Deprecated." + + " \"%s\"; please notify the authors %s.", + plugin.getDescription().getFullName(), + clazz.getName(), + method.toGenericString(), + (warning != null && warning.reason().length() != 0) ? warning.reason() : "Server performance will be affected", + Arrays.toString(plugin.getDescription().getAuthors().toArray())), + warningState == WarningState.ON ? new AuthorNagException(null) : null); + break; + } + } + + final CustomTimingsHandler timings = new CustomTimingsHandler("Plugin: " + plugin.getDescription().getFullName() + " Event: " + listener.getClass().getName() + "::" + method.getName()+"("+eventClass.getSimpleName()+")", pluginParentTimer); // Spigot + EventExecutor executor = new EventExecutor() { + public void execute(Listener listener, Event event) throws EventException { + try { + if (!eventClass.isAssignableFrom(event.getClass())) { + return; + } + // Spigot start + boolean isAsync = event.isAsynchronous(); + if (!isAsync) timings.startTiming(); + method.invoke(listener, event); + if (!isAsync) timings.stopTiming(); + // Spigot end + } catch (InvocationTargetException ex) { + throw new EventException(ex.getCause()); + } catch (Throwable t) { + throw new EventException(t); + } + } + }; + if (false) { // Spigot - RL handles useTimings check now + eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); + } else { + eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); + } + } + return ret; + } + + public void enablePlugin(final Plugin plugin) { + Validate.isTrue(plugin instanceof JavaPlugin, "Plugin is not associated with this PluginLoader"); + + if (!plugin.isEnabled()) { + plugin.getLogger().info("Enabling " + plugin.getDescription().getFullName()); + + JavaPlugin jPlugin = (JavaPlugin) plugin; + + String pluginName = jPlugin.getDescription().getName(); + + if (!loaders.containsKey(pluginName)) { + loaders.put(pluginName, (PluginClassLoader) jPlugin.getClassLoader()); + } + + try { + jPlugin.setEnabled(true); + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + // Perhaps abort here, rather than continue going, but as it stands, + // an abort is not possible the way it's currently written + server.getPluginManager().callEvent(new PluginEnableEvent(plugin)); + } + } + + public void disablePlugin(Plugin plugin) { + Validate.isTrue(plugin instanceof JavaPlugin, "Plugin is not associated with this PluginLoader"); + + if (plugin.isEnabled()) { + String message = String.format("Disabling %s", plugin.getDescription().getFullName()); + plugin.getLogger().info(message); + + server.getPluginManager().callEvent(new PluginDisableEvent(plugin)); + + JavaPlugin jPlugin = (JavaPlugin) plugin; + ClassLoader cloader = jPlugin.getClassLoader(); + + try { + jPlugin.setEnabled(false); + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + loaders.remove(jPlugin.getDescription().getName()); + + if (cloader instanceof PluginClassLoader) { + PluginClassLoader loader = (PluginClassLoader) cloader; + Set names = loader.getClasses(); + + for (String name : names) { + removeClass(name); + } + } + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/vspigot-api/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java new file mode 100644 index 0000000..4cffa13 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java @@ -0,0 +1,130 @@ +package org.bukkit.plugin.java; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.InvalidPluginException; +import org.bukkit.plugin.PluginDescriptionFile; + +/** + * A ClassLoader for plugins, to allow shared classes across multiple plugins + */ +final class PluginClassLoader extends URLClassLoader { + private final JavaPluginLoader loader; + private final Map> classes = new java.util.concurrent.ConcurrentHashMap>(); // Spigot + private final PluginDescriptionFile description; + private final File dataFolder; + private final File file; + final JavaPlugin plugin; + private JavaPlugin pluginInit; + private IllegalStateException pluginState; + + // Spigot Start + static + { + try + { + java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod( "registerAsParallelCapable" ); + if ( method != null ) + { + boolean oldAccessible = method.isAccessible(); + method.setAccessible( true ); + method.invoke( null ); + method.setAccessible( oldAccessible ); + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.INFO, "Set PluginClassLoader as parallel capable" ); + } + } catch ( NoSuchMethodException ex ) + { + // Ignore + } catch ( Exception ex ) + { + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.WARNING, "Error setting PluginClassLoader as parallel capable", ex ); + } + } + // Spigot End + + PluginClassLoader(final JavaPluginLoader loader, final ClassLoader parent, final PluginDescriptionFile description, final File dataFolder, final File file) throws InvalidPluginException, MalformedURLException { + super(new URL[] {file.toURI().toURL()}, parent); + Validate.notNull(loader, "Loader cannot be null"); + + this.loader = loader; + this.description = description; + this.dataFolder = dataFolder; + this.file = file; + + try { + Class jarClass; + try { + jarClass = Class.forName(description.getMain(), true, this); + } catch (ClassNotFoundException ex) { + throw new InvalidPluginException("Cannot find main class `" + description.getMain() + "'", ex); + } + + Class pluginClass; + try { + pluginClass = jarClass.asSubclass(JavaPlugin.class); + } catch (ClassCastException ex) { + throw new InvalidPluginException("main class `" + description.getMain() + "' does not extend JavaPlugin", ex); + } + + plugin = pluginClass.newInstance(); + } catch (IllegalAccessException ex) { + throw new InvalidPluginException("No public constructor", ex); + } catch (InstantiationException ex) { + throw new InvalidPluginException("Abnormal plugin type", ex); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + return findClass(name, true); + } + + Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { + if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { + throw new ClassNotFoundException(name); + } + Class result = classes.get(name); + + if (result == null) { + if (checkGlobal) { + result = loader.getClassByName(name); + } + + if (result == null) { + result = super.findClass(name); + + if (result != null) { + loader.setClass(name, result); + } + } + + classes.put(name, result); + } + + return result; + } + + Set getClasses() { + return classes.keySet(); + } + + synchronized void initialize(JavaPlugin javaPlugin) { + Validate.notNull(javaPlugin, "Initializing plugin cannot be null"); + Validate.isTrue(javaPlugin.getClass().getClassLoader() == this, "Cannot initialize plugin outside of this class loader"); + if (this.plugin != null || this.pluginInit != null) { + throw new IllegalArgumentException("Plugin already initialized!", pluginState); + } + + pluginState = new IllegalStateException("Initial initialization"); + this.pluginInit = javaPlugin; + + javaPlugin.init(loader, loader.server, description, dataFolder, file, this); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ChannelNameTooLongException.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ChannelNameTooLongException.java new file mode 100644 index 0000000..80ef8a2 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ChannelNameTooLongException.java @@ -0,0 +1,15 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a Plugin Channel is too long. + */ +@SuppressWarnings("serial") +public class ChannelNameTooLongException extends RuntimeException { + public ChannelNameTooLongException() { + super("Attempted to send a Plugin Message to a channel that was too large. The maximum length a channel may be is " + Messenger.MAX_CHANNEL_SIZE + " chars."); + } + + public ChannelNameTooLongException(String channel) { + super("Attempted to send a Plugin Message to a channel that was too large. The maximum length a channel may be is " + Messenger.MAX_CHANNEL_SIZE + " chars (attempted " + channel.length() + " - '" + channel + "."); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ChannelNotRegisteredException.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ChannelNotRegisteredException.java new file mode 100644 index 0000000..2266f17 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ChannelNotRegisteredException.java @@ -0,0 +1,15 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a Plugin attempts to send a message on an unregistered channel. + */ +@SuppressWarnings("serial") +public class ChannelNotRegisteredException extends RuntimeException { + public ChannelNotRegisteredException() { + this("Attempted to send a plugin message through an unregistered channel."); + } + + public ChannelNotRegisteredException(String channel) { + super("Attempted to send a plugin message through the unregistered channel `" + channel + "'."); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/MessageTooLargeException.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/MessageTooLargeException.java new file mode 100644 index 0000000..61af8c4 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/MessageTooLargeException.java @@ -0,0 +1,23 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a Plugin Message is sent that is too large to be sent. + */ +@SuppressWarnings("serial") +public class MessageTooLargeException extends RuntimeException { + public MessageTooLargeException() { + this("Attempted to send a plugin message that was too large. The maximum length a plugin message may be is " + Messenger.MAX_MESSAGE_SIZE + " bytes."); + } + + public MessageTooLargeException(byte[] message) { + this(message.length); + } + + public MessageTooLargeException(int length) { + this("Attempted to send a plugin message that was too large. The maximum length a plugin message may be is " + Messenger.MAX_MESSAGE_SIZE + " bytes (tried to send one that is " + length + " bytes long)."); + } + + public MessageTooLargeException(String msg) { + super(msg); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/Messenger.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/Messenger.java new file mode 100644 index 0000000..aa009fe --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/Messenger.java @@ -0,0 +1,216 @@ +package org.bukkit.plugin.messaging; + +import java.util.Set; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * A class responsible for managing the registrations of plugin channels and + * their listeners. + */ +public interface Messenger { + + /** + * Represents the largest size that an individual Plugin Message may be. + */ + public static final int MAX_MESSAGE_SIZE = 32766; + + /** + * Represents the largest size that a Plugin Channel may be. + */ + public static final int MAX_CHANNEL_SIZE = 16; + + /** + * Checks if the specified channel is a reserved name. + * + * @param channel Channel name to check. + * @return True if the channel is reserved, otherwise false. + * @throws IllegalArgumentException Thrown if channel is null. + */ + public boolean isReservedChannel(String channel); + + /** + * Registers the specific plugin to the requested outgoing plugin channel, + * allowing it to send messages through that channel to any clients. + * + * @param plugin Plugin that wishes to send messages through the channel. + * @param channel Channel to register. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public void registerOutgoingPluginChannel(Plugin plugin, String channel); + + /** + * Unregisters the specific plugin from the requested outgoing plugin + * channel, no longer allowing it to send messages through that channel to + * any clients. + * + * @param plugin Plugin that no longer wishes to send messages through the + * channel. + * @param channel Channel to unregister. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public void unregisterOutgoingPluginChannel(Plugin plugin, String channel); + + /** + * Unregisters the specific plugin from all outgoing plugin channels, no + * longer allowing it to send any plugin messages. + * + * @param plugin Plugin that no longer wishes to send plugin messages. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public void unregisterOutgoingPluginChannel(Plugin plugin); + + /** + * Registers the specific plugin for listening on the requested incoming + * plugin channel, allowing it to act upon any plugin messages. + * + * @param plugin Plugin that wishes to register to this channel. + * @param channel Channel to register. + * @param listener Listener to receive messages on. + * @return The resulting registration that was made as a result of this + * method. + * @throws IllegalArgumentException Thrown if plugin, channel or listener + * is null, or the listener is already registered for this channel. + */ + public PluginMessageListenerRegistration registerIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener); + + /** + * Unregisters the specific plugin's listener from listening on the + * requested incoming plugin channel, no longer allowing it to act upon + * any plugin messages. + * + * @param plugin Plugin that wishes to unregister from this channel. + * @param channel Channel to unregister. + * @param listener Listener to stop receiving messages on. + * @throws IllegalArgumentException Thrown if plugin, channel or listener + * is null. + */ + public void unregisterIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener); + + /** + * Unregisters the specific plugin from listening on the requested + * incoming plugin channel, no longer allowing it to act upon any plugin + * messages. + * + * @param plugin Plugin that wishes to unregister from this channel. + * @param channel Channel to unregister. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public void unregisterIncomingPluginChannel(Plugin plugin, String channel); + + /** + * Unregisters the specific plugin from listening on all plugin channels + * through all listeners. + * + * @param plugin Plugin that wishes to unregister from this channel. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public void unregisterIncomingPluginChannel(Plugin plugin); + + /** + * Gets a set containing all the outgoing plugin channels. + * + * @return List of all registered outgoing plugin channels. + */ + public Set getOutgoingChannels(); + + /** + * Gets a set containing all the outgoing plugin channels that the + * specified plugin is registered to. + * + * @param plugin Plugin to retrieve channels for. + * @return List of all registered outgoing plugin channels that a plugin + * is registered to. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public Set getOutgoingChannels(Plugin plugin); + + /** + * Gets a set containing all the incoming plugin channels. + * + * @return List of all registered incoming plugin channels. + */ + public Set getIncomingChannels(); + + /** + * Gets a set containing all the incoming plugin channels that the + * specified plugin is registered for. + * + * @param plugin Plugin to retrieve channels for. + * @return List of all registered incoming plugin channels that the plugin + * is registered for. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public Set getIncomingChannels(Plugin plugin); + + /** + * Gets a set containing all the incoming plugin channel registrations + * that the specified plugin has. + * + * @param plugin Plugin to retrieve registrations for. + * @return List of all registrations that the plugin has. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public Set getIncomingChannelRegistrations(Plugin plugin); + + /** + * Gets a set containing all the incoming plugin channel registrations + * that are on the requested channel. + * + * @param channel Channel to retrieve registrations for. + * @return List of all registrations that are on the channel. + * @throws IllegalArgumentException Thrown if channel is null. + */ + public Set getIncomingChannelRegistrations(String channel); + + /** + * Gets a set containing all the incoming plugin channel registrations + * that the specified plugin has on the requested channel. + * + * @param plugin Plugin to retrieve registrations for. + * @param channel Channel to filter registrations by. + * @return List of all registrations that the plugin has. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public Set getIncomingChannelRegistrations(Plugin plugin, String channel); + + /** + * Checks if the specified plugin message listener registration is valid. + *

            + * A registration is considered valid if it has not be unregistered and + * that the plugin is still enabled. + * + * @param registration Registration to check. + * @return True if the registration is valid, otherwise false. + */ + public boolean isRegistrationValid(PluginMessageListenerRegistration registration); + + /** + * Checks if the specified plugin has registered to receive incoming + * messages through the requested channel. + * + * @param plugin Plugin to check registration for. + * @param channel Channel to test for. + * @return True if the channel is registered, else false. + */ + public boolean isIncomingChannelRegistered(Plugin plugin, String channel); + + /** + * Checks if the specified plugin has registered to send outgoing messages + * through the requested channel. + * + * @param plugin Plugin to check registration for. + * @param channel Channel to test for. + * @return True if the channel is registered, else false. + */ + public boolean isOutgoingChannelRegistered(Plugin plugin, String channel); + + /** + * Dispatches the specified incoming message to any registered listeners. + * + * @param source Source of the message. + * @param channel Channel that the message was sent by. + * @param message Raw payload of the message. + */ + public void dispatchIncomingMessage(Player source, String channel, byte[] message); +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginChannelDirection.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginChannelDirection.java new file mode 100644 index 0000000..3d7ec2e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginChannelDirection.java @@ -0,0 +1,17 @@ +package org.bukkit.plugin.messaging; + +/** + * Represents the different directions a plugin channel may go. + */ +public enum PluginChannelDirection { + + /** + * The plugin channel is being sent to the server from a client. + */ + INCOMING, + + /** + * The plugin channel is being sent to a client from the server. + */ + OUTGOING +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java new file mode 100644 index 0000000..f1aa080 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java @@ -0,0 +1,20 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.entity.Player; + +/** + * A listener for a specific Plugin Channel, which will receive notifications + * of messages sent from a client. + */ +public interface PluginMessageListener { + + /** + * A method that will be thrown when a PluginMessageSource sends a plugin + * message on a registered channel. + * + * @param channel Channel that the message was sent through. + * @param player Source of the message. + * @param message The raw message that was sent. + */ + public void onPluginMessageReceived(String channel, Player player, byte[] message); +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListenerRegistration.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListenerRegistration.java new file mode 100644 index 0000000..29929bf --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListenerRegistration.java @@ -0,0 +1,104 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.plugin.Plugin; + +/** + * Contains information about a {@link Plugin}s registration to a plugin + * channel. + */ +public final class PluginMessageListenerRegistration { + private final Messenger messenger; + private final Plugin plugin; + private final String channel; + private final PluginMessageListener listener; + + public PluginMessageListenerRegistration(Messenger messenger, Plugin plugin, String channel, PluginMessageListener listener) { + if (messenger == null) { + throw new IllegalArgumentException("Messenger cannot be null!"); + } + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null!"); + } + if (channel == null) { + throw new IllegalArgumentException("Channel cannot be null!"); + } + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null!"); + } + + this.messenger = messenger; + this.plugin = plugin; + this.channel = channel; + this.listener = listener; + } + + /** + * Gets the plugin channel that this registration is about. + * + * @return Plugin channel. + */ + public String getChannel() { + return channel; + } + + /** + * Gets the registered listener described by this registration. + * + * @return Registered listener. + */ + public PluginMessageListener getListener() { + return listener; + } + + /** + * Gets the plugin that this registration is for. + * + * @return Registered plugin. + */ + public Plugin getPlugin() { + return plugin; + } + + /** + * Checks if this registration is still valid. + * + * @return True if this registration is still valid, otherwise false. + */ + public boolean isValid() { + return messenger.isRegistrationValid(this); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PluginMessageListenerRegistration other = (PluginMessageListenerRegistration) obj; + if (this.messenger != other.messenger && (this.messenger == null || !this.messenger.equals(other.messenger))) { + return false; + } + if (this.plugin != other.plugin && (this.plugin == null || !this.plugin.equals(other.plugin))) { + return false; + } + if ((this.channel == null) ? (other.channel != null) : !this.channel.equals(other.channel)) { + return false; + } + if (this.listener != other.listener && (this.listener == null || !this.listener.equals(other.listener))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + (this.messenger != null ? this.messenger.hashCode() : 0); + hash = 53 * hash + (this.plugin != null ? this.plugin.hashCode() : 0); + hash = 53 * hash + (this.channel != null ? this.channel.hashCode() : 0); + hash = 53 * hash + (this.listener != null ? this.listener.hashCode() : 0); + return hash; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java new file mode 100644 index 0000000..e5c5916 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java @@ -0,0 +1,38 @@ +package org.bukkit.plugin.messaging; + +import java.util.Set; +import org.bukkit.plugin.Plugin; + +/** + * Represents a possible recipient for a Plugin Message. + */ +public interface PluginMessageRecipient { + /** + * Sends this recipient a Plugin Message on the specified outgoing + * channel. + *

            + * The message may not be larger than {@link Messenger#MAX_MESSAGE_SIZE} + * bytes, and the plugin must be registered to send messages on the + * specified channel. + * + * @param source The plugin that sent this message. + * @param channel The channel to send this message on. + * @param message The raw message to send. + * @throws IllegalArgumentException Thrown if the source plugin is + * disabled. + * @throws IllegalArgumentException Thrown if source, channel or message + * is null. + * @throws MessageTooLargeException Thrown if the message is too big. + * @throws ChannelNotRegisteredException Thrown if the channel is not + * registered for this plugin. + */ + public void sendPluginMessage(Plugin source, String channel, byte[] message); + + /** + * Gets a set containing all the Plugin Channels that this client is + * listening on. + * + * @return Set containing all the channels that this client may accept. + */ + public Set getListeningPluginChannels(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ReservedChannelException.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ReservedChannelException.java new file mode 100644 index 0000000..0221f04 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/ReservedChannelException.java @@ -0,0 +1,16 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a plugin attempts to register for a reserved channel (such as + * "REGISTER") + */ +@SuppressWarnings("serial") +public class ReservedChannelException extends RuntimeException { + public ReservedChannelException() { + this("Attempted to register for a reserved channel name."); + } + + public ReservedChannelException(String name) { + super("Attempted to register for a reserved channel name ('" + name + "')"); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java new file mode 100644 index 0000000..4c171e8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java @@ -0,0 +1,489 @@ +package org.bukkit.plugin.messaging; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * Standard implementation to {@link Messenger} + */ +public class StandardMessenger implements Messenger { + private final Map> incomingByChannel = new HashMap>(); + private final Map> incomingByPlugin = new HashMap>(); + private final Map> outgoingByChannel = new HashMap>(); + private final Map> outgoingByPlugin = new HashMap>(); + private final Object incomingLock = new Object(); + private final Object outgoingLock = new Object(); + + private void addToOutgoing(Plugin plugin, String channel) { + synchronized (outgoingLock) { + Set plugins = outgoingByChannel.get(channel); + Set channels = outgoingByPlugin.get(plugin); + + if (plugins == null) { + plugins = new HashSet(); + outgoingByChannel.put(channel, plugins); + } + + if (channels == null) { + channels = new HashSet(); + outgoingByPlugin.put(plugin, channels); + } + + plugins.add(plugin); + channels.add(channel); + } + } + + private void removeFromOutgoing(Plugin plugin, String channel) { + synchronized (outgoingLock) { + Set plugins = outgoingByChannel.get(channel); + Set channels = outgoingByPlugin.get(plugin); + + if (plugins != null) { + plugins.remove(plugin); + + if (plugins.isEmpty()) { + outgoingByChannel.remove(channel); + } + } + + if (channels != null) { + channels.remove(channel); + + if (channels.isEmpty()) { + outgoingByChannel.remove(channel); + } + } + } + } + + private void removeFromOutgoing(Plugin plugin) { + synchronized (outgoingLock) { + Set channels = outgoingByPlugin.get(plugin); + + if (channels != null) { + String[] toRemove = channels.toArray(new String[0]); + + outgoingByPlugin.remove(plugin); + + for (String channel : toRemove) { + removeFromOutgoing(plugin, channel); + } + } + } + } + + private void addToIncoming(PluginMessageListenerRegistration registration) { + synchronized (incomingLock) { + Set registrations = incomingByChannel.get(registration.getChannel()); + + if (registrations == null) { + registrations = new HashSet(); + incomingByChannel.put(registration.getChannel(), registrations); + } else { + if (registrations.contains(registration)) { + throw new IllegalArgumentException("This registration already exists"); + } + } + + registrations.add(registration); + + registrations = incomingByPlugin.get(registration.getPlugin()); + + if (registrations == null) { + registrations = new HashSet(); + incomingByPlugin.put(registration.getPlugin(), registrations); + } else { + if (registrations.contains(registration)) { + throw new IllegalArgumentException("This registration already exists"); + } + } + + registrations.add(registration); + } + } + + private void removeFromIncoming(PluginMessageListenerRegistration registration) { + synchronized (incomingLock) { + Set registrations = incomingByChannel.get(registration.getChannel()); + + if (registrations != null) { + registrations.remove(registration); + + if (registrations.isEmpty()) { + incomingByChannel.remove(registration.getChannel()); + } + } + + registrations = incomingByPlugin.get(registration.getPlugin()); + + if (registrations != null) { + registrations.remove(registration); + + if (registrations.isEmpty()) { + incomingByPlugin.remove(registration.getPlugin()); + } + } + } + } + + private void removeFromIncoming(Plugin plugin, String channel) { + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); + + for (PluginMessageListenerRegistration registration : toRemove) { + if (registration.getChannel().equals(channel)) { + removeFromIncoming(registration); + } + } + } + } + } + + private void removeFromIncoming(Plugin plugin) { + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); + + incomingByPlugin.remove(plugin); + + for (PluginMessageListenerRegistration registration : toRemove) { + removeFromIncoming(registration); + } + } + } + } + + public boolean isReservedChannel(String channel) { + validateChannel(channel); + + return channel.equals("REGISTER") || channel.equals("UNREGISTER"); + } + + public void registerOutgoingPluginChannel(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + if (isReservedChannel(channel)) { + throw new ReservedChannelException(channel); + } + + addToOutgoing(plugin, channel); + } + + public void unregisterOutgoingPluginChannel(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + removeFromOutgoing(plugin, channel); + } + + public void unregisterOutgoingPluginChannel(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + removeFromOutgoing(plugin); + } + + public PluginMessageListenerRegistration registerIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + if (isReservedChannel(channel)) { + throw new ReservedChannelException(channel); + } + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + + PluginMessageListenerRegistration result = new PluginMessageListenerRegistration(this, plugin, channel, listener); + + addToIncoming(result); + + return result; + } + + public void unregisterIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + validateChannel(channel); + + removeFromIncoming(new PluginMessageListenerRegistration(this, plugin, channel, listener)); + } + + public void unregisterIncomingPluginChannel(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + removeFromIncoming(plugin, channel); + } + + public void unregisterIncomingPluginChannel(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + removeFromIncoming(plugin); + } + + public Set getOutgoingChannels() { + synchronized (outgoingLock) { + Set keys = outgoingByChannel.keySet(); + return ImmutableSet.copyOf(keys); + } + } + + public Set getOutgoingChannels(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + synchronized (outgoingLock) { + Set channels = outgoingByPlugin.get(plugin); + + if (channels != null) { + return ImmutableSet.copyOf(channels); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannels() { + synchronized (incomingLock) { + Set keys = incomingByChannel.keySet(); + return ImmutableSet.copyOf(keys); + } + } + + public Set getIncomingChannels(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + Builder builder = ImmutableSet.builder(); + + for (PluginMessageListenerRegistration registration : registrations) { + builder.add(registration.getChannel()); + } + + return builder.build(); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannelRegistrations(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + return ImmutableSet.copyOf(registrations); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannelRegistrations(String channel) { + validateChannel(channel); + + synchronized (incomingLock) { + Set registrations = incomingByChannel.get(channel); + + if (registrations != null) { + return ImmutableSet.copyOf(registrations); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannelRegistrations(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + Builder builder = ImmutableSet.builder(); + + for (PluginMessageListenerRegistration registration : registrations) { + if (registration.getChannel().equals(channel)) { + builder.add(registration); + } + } + + return builder.build(); + } else { + return ImmutableSet.of(); + } + } + } + + public boolean isRegistrationValid(PluginMessageListenerRegistration registration) { + if (registration == null) { + throw new IllegalArgumentException("Registration cannot be null"); + } + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(registration.getPlugin()); + + if (registrations != null) { + return registrations.contains(registration); + } + + return false; + } + } + + public boolean isIncomingChannelRegistered(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + for (PluginMessageListenerRegistration registration : registrations) { + if (registration.getChannel().equals(channel)) { + return true; + } + } + } + + return false; + } + } + + public boolean isOutgoingChannelRegistered(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + synchronized (outgoingLock) { + Set channels = outgoingByPlugin.get(plugin); + + if (channels != null) { + return channels.contains(channel); + } + + return false; + } + } + + public void dispatchIncomingMessage(Player source, String channel, byte[] message) { + if (source == null) { + throw new IllegalArgumentException("Player source cannot be null"); + } + if (message == null) { + throw new IllegalArgumentException("Message cannot be null"); + } + validateChannel(channel); + + Set registrations = getIncomingChannelRegistrations(channel); + + for (PluginMessageListenerRegistration registration : registrations) { + // Spigot Start + try + { + registration.getListener().onPluginMessageReceived( channel, source, message ); + } catch ( Throwable t ) + { + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.WARNING, "Could not pass incoming plugin message to " + registration.getPlugin(), t ); + } + // Spigot End + } + } + + /** + * Validates a Plugin Channel name. + * + * @param channel Channel name to validate. + */ + public static void validateChannel(String channel) { + if (channel == null) { + throw new IllegalArgumentException("Channel cannot be null"); + } + if (channel.length() > Messenger.MAX_CHANNEL_SIZE) { + throw new ChannelNameTooLongException(channel); + } + } + + /** + * Validates the input of a Plugin Message, ensuring the arguments are all + * valid. + * + * @param messenger Messenger to use for validation. + * @param source Source plugin of the Message. + * @param channel Plugin Channel to send the message by. + * @param message Raw message payload to send. + * @throws IllegalArgumentException Thrown if the source plugin is + * disabled. + * @throws IllegalArgumentException Thrown if source, channel or message + * is null. + * @throws MessageTooLargeException Thrown if the message is too big. + * @throws ChannelNameTooLongException Thrown if the channel name is too + * long. + * @throws ChannelNotRegisteredException Thrown if the channel is not + * registered for this plugin. + */ + public static void validatePluginMessage(Messenger messenger, Plugin source, String channel, byte[] message) { + if (messenger == null) { + throw new IllegalArgumentException("Messenger cannot be null"); + } + if (source == null) { + throw new IllegalArgumentException("Plugin source cannot be null"); + } + if (!source.isEnabled()) { + throw new IllegalArgumentException("Plugin must be enabled to send messages"); + } + if (message == null) { + throw new IllegalArgumentException("Message cannot be null"); + } + if (!messenger.isOutgoingChannelRegistered(source, channel)) { + throw new ChannelNotRegisteredException(channel); + } + if (message.length > Messenger.MAX_MESSAGE_SIZE) { + throw new MessageTooLargeException(message); + } + validateChannel(channel); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/potion/Potion.java b/vspigot-api/src/main/java/org/bukkit/potion/Potion.java new file mode 100644 index 0000000..a358c29 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/potion/Potion.java @@ -0,0 +1,442 @@ +package org.bukkit.potion; + +import java.util.Collection; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; + +import com.google.common.collect.ImmutableList; + +/** + * Represents a minecraft potion + */ +public class Potion { + private boolean extended = false; + private boolean splash = false; + private int level = 1; + private int name = -1; + private PotionType type; + + /** + * Construct a new potion of the given type. Unless the type is {@link + * PotionType#WATER}, it will be level one, without extended duration. + * Don't use this constructor to create a no-effect potion other than + * water bottle. + * + * @param type The potion type + * @see #Potion(int) + */ + public Potion(PotionType type) { + this.type = type; + if (type != null) { + this.name = type.getDamageValue(); + } + if (type == null || type == PotionType.WATER) { + this.level = 0; + } + } + + /** + * @deprecated In favour of {@link #Potion(PotionType, int)} + */ + @SuppressWarnings("javadoc") + @Deprecated + public Potion(PotionType type, Tier tier) { + this(type, tier == Tier.TWO ? 2 : 1); + Validate.notNull(type, "Type cannot be null"); + } + + /** + * @deprecated In favour of {@link #Potion(PotionType, int, boolean)} + */ + @SuppressWarnings("javadoc") + @Deprecated + public Potion(PotionType type, Tier tier, boolean splash) { + this(type, tier == Tier.TWO ? 2 : 1, splash); + } + + /** + * @deprecated In favour of {@link #Potion(PotionType, int, boolean, + * boolean)} + */ + @SuppressWarnings("javadoc") + @Deprecated + public Potion(PotionType type, Tier tier, boolean splash, boolean extended) { + this(type, tier, splash); + this.extended = extended; + } + + /** + * Create a new potion of the given type and level. + * + * @param type The type of potion. + * @param level The potion's level. + */ + public Potion(PotionType type, int level) { + this(type); + Validate.notNull(type, "Type cannot be null"); + Validate.isTrue(type != PotionType.WATER, "Water bottles don't have a level!"); + Validate.isTrue(level > 0 && level < 3, "Level must be 1 or 2"); + this.level = level; + } + + /** + * Create a new potion of the given type and level. + * + * @param type The type of potion. + * @param level The potion's level. + * @param splash Whether it is a splash potion. + * @deprecated In favour of using {@link #Potion(PotionType)} with {@link + * #splash()}. + */ + @Deprecated + public Potion(PotionType type, int level, boolean splash) { + this(type, level); + this.splash = splash; + } + + /** + * Create a new potion of the given type and level. + * + * @param type The type of potion. + * @param level The potion's level. + * @param splash Whether it is a splash potion. + * @param extended Whether it has an extended duration. + * @deprecated In favour of using {@link #Potion(PotionType)} with {@link + * #extend()} and possibly {@link #splash()}. + */ + @Deprecated + public Potion(PotionType type, int level, boolean splash, boolean extended) { + this(type, level, splash); + this.extended = extended; + } + + /** + * Create a potion with a specific name. + * + * @param name The name index (0-63) + */ + public Potion(int name) { + this(PotionType.getByDamageValue(name & POTION_BIT)); + this.name = name & NAME_BIT; + if ((name & POTION_BIT) == 0) { + // If it's 0 it would've become PotionType.WATER, but it should actually be mundane potion + this.type = null; + } + } + + /** + * Chain this to the constructor to make the potion a splash potion. + * + * @return The potion. + */ + public Potion splash() { + setSplash(true); + return this; + } + + /** + * Chain this to the constructor to extend the potion's duration. + * + * @return The potion. + */ + public Potion extend() { + setHasExtendedDuration(true); + return this; + } + + /** + * Applies the effects of this potion to the given {@link ItemStack}. The + * ItemStack must be a potion. + * + * @param to The itemstack to apply to + */ + public void apply(ItemStack to) { + Validate.notNull(to, "itemstack cannot be null"); + Validate.isTrue(to.getType() == Material.POTION, "given itemstack is not a potion"); + to.setDurability(toDamageValue()); + } + + /** + * Applies the effects that would be applied by this potion to the given + * {@link LivingEntity}. + * + * @see LivingEntity#addPotionEffects(Collection) + * @param to The entity to apply the effects to + */ + public void apply(LivingEntity to) { + Validate.notNull(to, "entity cannot be null"); + to.addPotionEffects(getEffects()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Potion other = (Potion) obj; + return extended == other.extended && splash == other.splash && level == other.level && type == other.type; + } + + /** + * Returns a collection of {@link PotionEffect}s that this {@link Potion} + * would confer upon a {@link LivingEntity}. + * + * @see PotionBrewer#getEffectsFromDamage(int) + * @see Potion#toDamageValue() + * @return The effects that this potion applies + */ + public Collection getEffects() { + if (type == null) return ImmutableList.of(); + return getBrewer().getEffectsFromDamage(toDamageValue()); + } + + /** + * Returns the level of this potion. + * + * @return The level of this potion + */ + public int getLevel() { + return level; + } + + /** + * Returns the {@link Tier} of this potion. + * + * @return The tier of this potion + */ + @Deprecated + public Tier getTier() { + return level == 2 ? Tier.TWO : Tier.ONE; + } + + /** + * Returns the {@link PotionType} of this potion. + * + * @return The type of this potion + */ + public PotionType getType() { + return type; + } + + /** + * Returns whether this potion has an extended duration. + * + * @return Whether this potion has extended duration + */ + public boolean hasExtendedDuration() { + return extended; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = prime + level; + result = prime * result + (extended ? 1231 : 1237); + result = prime * result + (splash ? 1231 : 1237); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + /** + * Returns whether this potion is a splash potion. + * + * @return Whether this is a splash potion + */ + public boolean isSplash() { + return splash; + } + + /** + * Set whether this potion has extended duration. This will cause the + * potion to have roughly 8/3 more duration than a regular potion. + * + * @param isExtended Whether the potion should have extended duration + */ + public void setHasExtendedDuration(boolean isExtended) { + Validate.isTrue(type == null || !type.isInstant(), "Instant potions cannot be extended"); + extended = isExtended; + } + + /** + * Sets whether this potion is a splash potion. Splash potions can be + * thrown for a radius effect. + * + * @param isSplash Whether this is a splash potion + */ + public void setSplash(boolean isSplash) { + splash = isSplash; + } + + /** + * Sets the {@link Tier} of this potion. + * + * @param tier The new tier of this potion + * @deprecated In favour of {@link #setLevel(int)} + */ + @Deprecated + public void setTier(Tier tier) { + Validate.notNull(tier, "tier cannot be null"); + this.level = (tier == Tier.TWO ? 2 : 1); + } + + /** + * Sets the {@link PotionType} of this potion. + * + * @param type The new type of this potion + */ + public void setType(PotionType type) { + this.type = type; + } + + /** + * Sets the level of this potion. + * + * @param level The new level of this potion + */ + public void setLevel(int level) { + Validate.notNull(this.type, "No-effect potions don't have a level."); + int max = type.getMaxLevel(); + Validate.isTrue(level > 0 && level <= max, "Level must be " + (max == 1 ? "" : "between 1 and ") + max + " for this potion"); + this.level = level; + } + + /** + * Converts this potion to a valid potion damage short, usable for potion + * item stacks. + * + * @return The damage value of this potion + * @deprecated Magic value + */ + @Deprecated + public short toDamageValue() { + short damage; + if (type == PotionType.WATER) { + return 0; + } else if (type == null) { + // Without this, mundanePotion.toDamageValue() would return 0 + damage = (short) (name == 0 ? 8192 : name); + } else { + damage = (short) (level - 1); + damage <<= TIER_SHIFT; + damage |= (short) type.getDamageValue(); + } + if (splash) { + damage |= SPLASH_BIT; + } + if (extended) { + damage |= EXTENDED_BIT; + } + return damage; + } + + /** + * Converts this potion to an {@link ItemStack} with the specified amount + * and a correct damage value. + * + * @param amount The amount of the ItemStack + * @return The created ItemStack + */ + public ItemStack toItemStack(int amount) { + return new ItemStack(Material.POTION, amount, toDamageValue()); + } + + @Deprecated + public enum Tier { + ONE(0), + TWO(0x20); + + private int damageBit; + + Tier(int bit) { + damageBit = bit; + } + + public int getDamageBit() { + return damageBit; + } + + public static Tier getByDamageBit(int damageBit) { + for (Tier tier : Tier.values()) { + if (tier.damageBit == damageBit) + return tier; + } + return null; + } + } + + private static PotionBrewer brewer; + + private static final int EXTENDED_BIT = 0x40; + private static final int POTION_BIT = 0xF; + private static final int SPLASH_BIT = 0x4000; + private static final int TIER_BIT = 0x20; + private static final int TIER_SHIFT = 5; + private static final int NAME_BIT = 0x3F; + + /** + * + * @deprecated Magic value + */ + @Deprecated + public static Potion fromDamage(int damage) { + PotionType type = PotionType.getByDamageValue(damage & POTION_BIT); + Potion potion; + if (type == null || (type == PotionType.WATER && damage != 0)) { + potion = new Potion(damage & NAME_BIT); + } else { + int level = (damage & TIER_BIT) >> TIER_SHIFT; + level++; + potion = new Potion(type, level); + } + if ((damage & SPLASH_BIT) > 0) { + potion = potion.splash(); + } + if ((damage & EXTENDED_BIT) > 0) { + potion = potion.extend(); + } + return potion; + } + + public static Potion fromItemStack(ItemStack item) { + Validate.notNull(item, "item cannot be null"); + if (item.getType() != Material.POTION) + throw new IllegalArgumentException("item is not a potion"); + return fromDamage(item.getDurability()); + } + + /** + * Returns an instance of {@link PotionBrewer}. + * + * @return An instance of PotionBrewer + */ + public static PotionBrewer getBrewer() { + return brewer; + } + + /** + * Sets the current instance of {@link PotionBrewer}. Generally not to be + * used from within a plugin. + * + * @param other The new PotionBrewer + */ + public static void setPotionBrewer(PotionBrewer other) { + if (brewer != null) + throw new IllegalArgumentException("brewer can only be set internally"); + brewer = other; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public int getNameId() { + return name; + } +} \ No newline at end of file diff --git a/vspigot-api/src/main/java/org/bukkit/potion/PotionBrewer.java b/vspigot-api/src/main/java/org/bukkit/potion/PotionBrewer.java new file mode 100644 index 0000000..5275517 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/potion/PotionBrewer.java @@ -0,0 +1,31 @@ +package org.bukkit.potion; + +import java.util.Collection; + +/** + * Represents a brewer that can create {@link PotionEffect}s. + */ +public interface PotionBrewer { + + /** + * Creates a {@link PotionEffect} from the given {@link PotionEffectType}, + * applying duration modifiers and checks. + * + * @param potion The type of potion + * @param duration The duration in ticks + * @param amplifier The amplifier of the effect + * @return The resulting potion effect + */ + public PotionEffect createEffect(PotionEffectType potion, int duration, int amplifier); + + /** + * Returns a collection of {@link PotionEffect} that would be applied from + * a potion with the given data value. + * + * @param damage The data value of the potion + * @return The list of effects + * @deprecated Magic value + */ + @Deprecated + public Collection getEffectsFromDamage(int damage); +} diff --git a/vspigot-api/src/main/java/org/bukkit/potion/PotionEffect.java b/vspigot-api/src/main/java/org/bukkit/potion/PotionEffect.java new file mode 100644 index 0000000..24ee19d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/potion/PotionEffect.java @@ -0,0 +1,179 @@ +package org.bukkit.potion; + +import java.util.Map; +import java.util.NoSuchElementException; + +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.entity.LivingEntity; + +import com.google.common.collect.ImmutableMap; + +/** + * Represents a potion effect, that can be added to a {@link LivingEntity}. A + * potion effect has a duration that it will last for, an amplifier that will + * enhance its effects, and a {@link PotionEffectType}, that represents its + * effect on an entity. + */ +@SerializableAs("PotionEffect") +public class PotionEffect implements ConfigurationSerializable { + private static final String AMPLIFIER = "amplifier"; + private static final String DURATION = "duration"; + private static final String TYPE = "effect"; + private static final String AMBIENT = "ambient"; + private final int amplifier; + private final int duration; + private final PotionEffectType type; + private final boolean ambient; + + /** + * Creates a potion effect. + * + * @param type effect type + * @param duration measured in ticks, see {@link + * PotionEffect#getDuration()} + * @param amplifier the amplifier, see {@link PotionEffect#getAmplifier()} + * @param ambient the ambient status, see {@link PotionEffect#isAmbient()} + */ + public PotionEffect(PotionEffectType type, int duration, int amplifier, boolean ambient) { + Validate.notNull(type, "effect type cannot be null"); + this.type = type; + this.duration = duration; + this.amplifier = amplifier; + this.ambient = ambient; + } + + /** + * Creates a potion effect. Assumes ambient is true. + * + * @param type Effect type + * @param duration measured in ticks + * @param amplifier the amplifier for the effect + * @see PotionEffect#PotionEffect(PotionEffectType, int, int, boolean) + */ + public PotionEffect(PotionEffectType type, int duration, int amplifier) { + this(type, duration, amplifier, true); + } + + /** + * Constructor for deserialization. + * + * @param map the map to deserialize from + */ + public PotionEffect(Map map) { + this(getEffectType(map), getInt(map, DURATION), getInt(map, AMPLIFIER), getBool(map, AMBIENT)); + } + + private static PotionEffectType getEffectType(Map map) { + int type = getInt(map, TYPE); + PotionEffectType effect = PotionEffectType.getById(type); + if (effect != null) { + return effect; + } + throw new NoSuchElementException(map + " does not contain " + TYPE); + } + + private static int getInt(Map map, Object key) { + Object num = map.get(key); + if (num instanceof Integer) { + return (Integer) num; + } + throw new NoSuchElementException(map + " does not contain " + key); + } + + private static boolean getBool(Map map, Object key) { + Object bool = map.get(key); + if (bool instanceof Boolean) { + return (Boolean) bool; + } + throw new NoSuchElementException(map + " does not contain " + key); + } + + public Map serialize() { + return ImmutableMap.of( + TYPE, type.getId(), + DURATION, duration, + AMPLIFIER, amplifier, + AMBIENT, ambient + ); + } + + /** + * Attempts to add the effect represented by this object to the given + * {@link LivingEntity}. + * + * @see LivingEntity#addPotionEffect(PotionEffect) + * @param entity The entity to add this effect to + * @return Whether the effect could be added + */ + public boolean apply(LivingEntity entity) { + return entity.addPotionEffect(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PotionEffect)) { + return false; + } + PotionEffect that = (PotionEffect) obj; + return this.type.equals(that.type) && this.ambient == that.ambient && this.amplifier == that.amplifier && this.duration == that.duration; + } + + /** + * Returns the amplifier of this effect. A higher amplifier means the + * potion effect happens more often over its duration and in some cases + * has more effect on its target. + * + * @return The effect amplifier + */ + public int getAmplifier() { + return amplifier; + } + + /** + * Returns the duration (in ticks) that this effect will run for when + * applied to a {@link LivingEntity}. + * + * @return The duration of the effect + */ + public int getDuration() { + return duration; + } + + /** + * Returns the {@link PotionEffectType} of this effect. + * + * @return The potion type of this effect + */ + public PotionEffectType getType() { + return type; + } + + /** + * Makes potion effect produce more, translucent, particles. + * + * @return if this effect is ambient + */ + public boolean isAmbient() { + return ambient; + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 31 + type.hashCode(); + hash = hash * 31 + amplifier; + hash = hash * 31 + duration; + hash ^= 0x22222222 >> (ambient ? 1 : -1); + return hash; + } + + @Override + public String toString() { + return type.getName() + (ambient ? ":(" : ":") + duration + "t-x" + amplifier + (ambient ? ")" : ""); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/potion/PotionEffectType.java b/vspigot-api/src/main/java/org/bukkit/potion/PotionEffectType.java new file mode 100644 index 0000000..4919d59 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/potion/PotionEffectType.java @@ -0,0 +1,269 @@ +package org.bukkit.potion; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang.Validate; + +/** + * Represents a type of potion and its effect on an entity. + */ +public abstract class PotionEffectType { + /** + * Increases movement speed. + */ + public static final PotionEffectType SPEED = new PotionEffectTypeWrapper(1); + + /** + * Decreases movement speed. + */ + public static final PotionEffectType SLOW = new PotionEffectTypeWrapper(2); + + /** + * Increases dig speed. + */ + public static final PotionEffectType FAST_DIGGING = new PotionEffectTypeWrapper(3); + + /** + * Decreases dig speed. + */ + public static final PotionEffectType SLOW_DIGGING = new PotionEffectTypeWrapper(4); + + /** + * Increases damage dealt. + */ + public static final PotionEffectType INCREASE_DAMAGE = new PotionEffectTypeWrapper(5); + + /** + * Heals an entity. + */ + public static final PotionEffectType HEAL = new PotionEffectTypeWrapper(6); + + /** + * Hurts an entity. + */ + public static final PotionEffectType HARM = new PotionEffectTypeWrapper(7); + + /** + * Increases jump height. + */ + public static final PotionEffectType JUMP = new PotionEffectTypeWrapper(8); + + /** + * Warps vision on the client. + */ + public static final PotionEffectType CONFUSION = new PotionEffectTypeWrapper(9); + + /** + * Regenerates health. + */ + public static final PotionEffectType REGENERATION = new PotionEffectTypeWrapper(10); + + /** + * Decreases damage dealt to an entity. + */ + public static final PotionEffectType DAMAGE_RESISTANCE = new PotionEffectTypeWrapper(11); + + /** + * Stops fire damage. + */ + public static final PotionEffectType FIRE_RESISTANCE = new PotionEffectTypeWrapper(12); + + /** + * Allows breathing underwater. + */ + public static final PotionEffectType WATER_BREATHING = new PotionEffectTypeWrapper(13); + + /** + * Grants invisibility. + */ + public static final PotionEffectType INVISIBILITY = new PotionEffectTypeWrapper(14); + + /** + * Blinds an entity. + */ + public static final PotionEffectType BLINDNESS = new PotionEffectTypeWrapper(15); + + /** + * Allows an entity to see in the dark. + */ + public static final PotionEffectType NIGHT_VISION = new PotionEffectTypeWrapper(16); + + /** + * Increases hunger. + */ + public static final PotionEffectType HUNGER = new PotionEffectTypeWrapper(17); + + /** + * Decreases damage dealt by an entity. + */ + public static final PotionEffectType WEAKNESS = new PotionEffectTypeWrapper(18); + + /** + * Deals damage to an entity over time. + */ + public static final PotionEffectType POISON = new PotionEffectTypeWrapper(19); + + /** + * Deals damage to an entity over time and gives the health to the + * shooter. + */ + public static final PotionEffectType WITHER = new PotionEffectTypeWrapper(20); + + /** + * Increases the maximum health of an entity. + */ + public static final PotionEffectType HEALTH_BOOST = new PotionEffectTypeWrapper(21); + + /** + * Increases the maximum health of an entity with health that cannot be + * regenerated, but is refilled every 30 seconds. + */ + public static final PotionEffectType ABSORPTION = new PotionEffectTypeWrapper(22); + + /** + * Increases the food level of an entity each tick. + */ + public static final PotionEffectType SATURATION = new PotionEffectTypeWrapper(23); + + private final int id; + + protected PotionEffectType(int id) { + this.id = id; + } + + /** + * Creates a PotionEffect from this PotionEffectType, applying duration + * modifiers and checks. + * + * @see PotionBrewer#createEffect(PotionEffectType, int, int) + * @param duration time in ticks + * @param amplifier the effect's amplifier + * @return a resulting potion effect + */ + public PotionEffect createEffect(int duration, int amplifier) { + return Potion.getBrewer().createEffect(this, duration, amplifier); + } + + /** + * Returns the duration modifier applied to effects of this type. + * + * @return duration modifier + */ + public abstract double getDurationModifier(); + + /** + * Returns the unique ID of this type. + * + * @return Unique ID + * @deprecated Magic value + */ + @Deprecated + public int getId() { + return id; + } + + /** + * Returns the name of this effect type. + * + * @return The name of this effect type + */ + public abstract String getName(); + + /** + * Returns whether the effect of this type happens once, immediately. + * + * @return whether this type is normally instant + */ + public abstract boolean isInstant(); + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof PotionEffectType)) { + return false; + } + final PotionEffectType other = (PotionEffectType) obj; + if (this.id != other.id) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return id; + } + + @Override + public String toString() { + return "PotionEffectType[" + id + ", " + getName() + "]"; + } + + private static final PotionEffectType[] byId = new PotionEffectType[24]; + private static final Map byName = new HashMap(); + // will break on updates. + private static boolean acceptingNew = true; + + /** + * Gets the effect type specified by the unique id. + * + * @param id Unique ID to fetch + * @return Resulting type, or null if not found. + * @deprecated Magic value + */ + @Deprecated + public static PotionEffectType getById(int id) { + if (id >= byId.length || id < 0) + return null; + return byId[id]; + } + + /** + * Gets the effect type specified by the given name. + * + * @param name Name of PotionEffectType to fetch + * @return Resulting PotionEffectType, or null if not found. + */ + public static PotionEffectType getByName(String name) { + Validate.notNull(name, "name cannot be null"); + return byName.get(name.toLowerCase()); + } + + /** + * Registers an effect type with the given object. + *

            + * Generally not to be used from within a plugin. + * + * @param type PotionType to register + */ + public static void registerPotionEffectType(PotionEffectType type) { + if (byId[type.id] != null || byName.containsKey(type.getName().toLowerCase())) { + throw new IllegalArgumentException("Cannot set already-set type"); + } else if (!acceptingNew) { + throw new IllegalStateException( + "No longer accepting new potion effect types (can only be done by the server implementation)"); + } + + byId[type.id] = type; + byName.put(type.getName().toLowerCase(), type); + } + + /** + * Stops accepting any effect type registrations. + */ + public static void stopAcceptingRegistrations() { + acceptingNew = false; + } + + /** + * Returns an array of all the registered {@link PotionEffectType}s. + * + * @return Array of types. + */ + public static PotionEffectType[] values() { + return byId.clone(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/potion/PotionEffectTypeWrapper.java b/vspigot-api/src/main/java/org/bukkit/potion/PotionEffectTypeWrapper.java new file mode 100644 index 0000000..5db1ce8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/potion/PotionEffectTypeWrapper.java @@ -0,0 +1,31 @@ +package org.bukkit.potion; + +public class PotionEffectTypeWrapper extends PotionEffectType { + protected PotionEffectTypeWrapper(int id) { + super(id); + } + + @Override + public double getDurationModifier() { + return getType().getDurationModifier(); + } + + @Override + public String getName() { + return getType().getName(); + } + + /** + * Get the potion type bound to this wrapper. + * + * @return The potion effect type + */ + public PotionEffectType getType() { + return PotionEffectType.getById(getId()); + } + + @Override + public boolean isInstant() { + return getType().isInstant(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/potion/PotionType.java b/vspigot-api/src/main/java/org/bukkit/potion/PotionType.java new file mode 100644 index 0000000..a02b6a8 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/potion/PotionType.java @@ -0,0 +1,71 @@ +package org.bukkit.potion; + +public enum PotionType { + WATER(0, null, 0), + REGEN(1, PotionEffectType.REGENERATION, 2), + SPEED(2, PotionEffectType.SPEED, 2), + FIRE_RESISTANCE(3, PotionEffectType.FIRE_RESISTANCE, 1), + POISON(4, PotionEffectType.POISON, 2), + INSTANT_HEAL(5, PotionEffectType.HEAL, 2), + NIGHT_VISION(6, PotionEffectType.NIGHT_VISION, 1), + WEAKNESS(8, PotionEffectType.WEAKNESS, 1), + STRENGTH(9, PotionEffectType.INCREASE_DAMAGE, 2), + SLOWNESS(10, PotionEffectType.SLOW, 1), + INSTANT_DAMAGE(12, PotionEffectType.HARM, 2), + WATER_BREATHING(13, PotionEffectType.WATER_BREATHING, 1), + INVISIBILITY(14, PotionEffectType.INVISIBILITY, 1), + ; + + private final int damageValue, maxLevel; + private final PotionEffectType effect; + + PotionType(int damageValue, PotionEffectType effect, int maxLevel) { + this.damageValue = damageValue; + this.effect = effect; + this.maxLevel = maxLevel; + } + + public PotionEffectType getEffectType() { + return effect; + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public int getDamageValue() { + return damageValue; + } + + public int getMaxLevel() { + return maxLevel; + } + + public boolean isInstant() { + return effect == null ? true : effect.isInstant(); + } + + /** + * + * @deprecated Magic value + */ + @Deprecated + public static PotionType getByDamageValue(int damage) { + for (PotionType type : PotionType.values()) { + if (type.damageValue == damage) + return type; + } + return null; + } + + public static PotionType getByEffect(PotionEffectType effectType) { + if (effectType == null) + return WATER; + for (PotionType type : PotionType.values()) { + if (effectType.equals(type.effect)) + return type; + } + return null; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/projectiles/BlockProjectileSource.java b/vspigot-api/src/main/java/org/bukkit/projectiles/BlockProjectileSource.java new file mode 100644 index 0000000..e713c0d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/projectiles/BlockProjectileSource.java @@ -0,0 +1,13 @@ +package org.bukkit.projectiles; + +import org.bukkit.block.Block; + +public interface BlockProjectileSource extends ProjectileSource { + + /** + * Gets the block this projectile source belongs to. + * + * @return Block for the projectile source + */ + public Block getBlock(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/projectiles/ProjectileSource.java b/vspigot-api/src/main/java/org/bukkit/projectiles/ProjectileSource.java new file mode 100644 index 0000000..afad8d7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/projectiles/ProjectileSource.java @@ -0,0 +1,28 @@ +package org.bukkit.projectiles; + +import org.bukkit.entity.Projectile; +import org.bukkit.util.Vector; + +/** + * Represents a valid source of a projectile. + */ +public interface ProjectileSource { + + /** + * Launches a {@link Projectile} from the ProjectileSource. + * + * @param projectile class of the projectile to launch + * @return the launched projectile + */ + public T launchProjectile(Class projectile); + + /** + * Launches a {@link Projectile} from the ProjectileSource with an + * initial velocity. + * + * @param projectile class of the projectile to launch + * @param velocity the velocity with which to launch + * @return the launched projectile + */ + public T launchProjectile(Class projectile, Vector velocity); +} diff --git a/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitRunnable.java b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitRunnable.java new file mode 100644 index 0000000..c146ec7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitRunnable.java @@ -0,0 +1,149 @@ +package org.bukkit.scheduler; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +/** + * This class is provided as an easy way to handle scheduling tasks. + */ +public abstract class BukkitRunnable implements Runnable { + private int taskId = -1; + + /** + * Attempts to cancel this task. + * + * @throws IllegalStateException if task was not scheduled yet + */ + public synchronized void cancel() throws IllegalStateException { + Bukkit.getScheduler().cancelTask(getTaskId()); + } + + /** + * Schedules this in the Bukkit scheduler to run on next tick. + * + * @param plugin the reference to the plugin scheduling task + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see BukkitScheduler#runTask(Plugin, Runnable) + */ + public synchronized BukkitTask runTask(Plugin plugin) throws IllegalArgumentException, IllegalStateException { + checkState(); + return setupId(Bukkit.getScheduler().runTask(plugin, (Runnable) this)); + } + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Schedules this in the Bukkit scheduler to run asynchronously. + * + * @param plugin the reference to the plugin scheduling task + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see BukkitScheduler#runTaskAsynchronously(Plugin, Runnable) + */ + public synchronized BukkitTask runTaskAsynchronously(Plugin plugin) throws IllegalArgumentException, IllegalStateException { + checkState(); + return setupId(Bukkit.getScheduler().runTaskAsynchronously(plugin, (Runnable) this)); + } + + /** + * Schedules this to run after the specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see BukkitScheduler#runTaskLater(Plugin, Runnable, long) + */ + public synchronized BukkitTask runTaskLater(Plugin plugin, long delay) throws IllegalArgumentException, IllegalStateException { + checkState(); + return setupId(Bukkit.getScheduler().runTaskLater(plugin, (Runnable) this, delay)); + } + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Schedules this to run asynchronously after the specified number of + * server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see BukkitScheduler#runTaskLaterAsynchronously(Plugin, Runnable, long) + */ + public synchronized BukkitTask runTaskLaterAsynchronously(Plugin plugin, long delay) throws IllegalArgumentException, IllegalStateException { + checkState(); + return setupId(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, (Runnable) this, delay)); + } + + /** + * Schedules this to repeatedly run until cancelled, starting after the + * specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task + * @param period the ticks to wait between runs + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see BukkitScheduler#runTaskTimer(Plugin, Runnable, long, long) + */ + public synchronized BukkitTask runTaskTimer(Plugin plugin, long delay, long period) throws IllegalArgumentException, IllegalStateException { + checkState(); + return setupId(Bukkit.getScheduler().runTaskTimer(plugin, (Runnable) this, delay, period)); + } + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Schedules this to repeatedly run asynchronously until cancelled, + * starting after the specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task for the first + * time + * @param period the ticks to wait between runs + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see BukkitScheduler#runTaskTimerAsynchronously(Plugin, Runnable, long, + * long) + */ + public synchronized BukkitTask runTaskTimerAsynchronously(Plugin plugin, long delay, long period) throws IllegalArgumentException, IllegalStateException { + checkState(); + return setupId(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, (Runnable) this, delay, period)); + } + + /** + * Gets the task id for this runnable. + * + * @return the task id that this runnable was scheduled as + * @throws IllegalStateException if task was not scheduled yet + */ + public synchronized int getTaskId() throws IllegalStateException { + final int id = taskId; + if (id == -1) { + throw new IllegalStateException("Not scheduled yet"); + } + return id; + } + + private void checkState() { + if (taskId != -1) { + throw new IllegalStateException("Already scheduled as " + taskId); + } + } + + private BukkitTask setupId(final BukkitTask task) { + this.taskId = task.getTaskId(); + return task; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitScheduler.java b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitScheduler.java new file mode 100644 index 0000000..dfecafa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitScheduler.java @@ -0,0 +1,319 @@ +package org.bukkit.scheduler; + +import org.bukkit.plugin.Plugin; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.List; + +public interface BukkitScheduler { + + /** + * Schedules a once off task to occur after a delay. + *

            + * This task will be executed by the main server thread. + * + * @param plugin Plugin that owns the task + * @param task Task to be executed + * @param delay Delay in server ticks before executing task + * @return Task id number (-1 if scheduling failed) + */ + public int scheduleSyncDelayedTask(Plugin plugin, Runnable task, long delay); + + /** + * @deprecated Use {@link BukkitRunnable#runTaskLater(Plugin, long)} + */ + @Deprecated + public int scheduleSyncDelayedTask(Plugin plugin, BukkitRunnable task, long delay); + + /** + * Schedules a once off task to occur as soon as possible. + *

            + * This task will be executed by the main server thread. + * + * @param plugin Plugin that owns the task + * @param task Task to be executed + * @return Task id number (-1 if scheduling failed) + */ + public int scheduleSyncDelayedTask(Plugin plugin, Runnable task); + + /** + * @deprecated Use {@link BukkitRunnable#runTask(Plugin)} + */ + @Deprecated + public int scheduleSyncDelayedTask(Plugin plugin, BukkitRunnable task); + + /** + * Schedules a repeating task. + *

            + * This task will be executed by the main server thread. + * + * @param plugin Plugin that owns the task + * @param task Task to be executed + * @param delay Delay in server ticks before executing first repeat + * @param period Period in server ticks of the task + * @return Task id number (-1 if scheduling failed) + */ + public int scheduleSyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period); + + /** + * @deprecated Use {@link BukkitRunnable#runTaskTimer(Plugin, long, long)} + */ + @Deprecated + public int scheduleSyncRepeatingTask(Plugin plugin, BukkitRunnable task, long delay, long period); + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Schedules a once off task to occur after a delay. This task will be + * executed by a thread managed by the scheduler. + * + * @param plugin Plugin that owns the task + * @param task Task to be executed + * @param delay Delay in server ticks before executing task + * @return Task id number (-1 if scheduling failed) + * @deprecated This name is misleading, as it does not schedule "a sync" + * task, but rather, "an async" task + */ + @Deprecated + public int scheduleAsyncDelayedTask(Plugin plugin, Runnable task, long delay); + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Schedules a once off task to occur as soon as possible. This task will + * be executed by a thread managed by the scheduler. + * + * @param plugin Plugin that owns the task + * @param task Task to be executed + * @return Task id number (-1 if scheduling failed) + * @deprecated This name is misleading, as it does not schedule "a sync" + * task, but rather, "an async" task + */ + @Deprecated + public int scheduleAsyncDelayedTask(Plugin plugin, Runnable task); + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Schedules a repeating task. This task will be executed by a thread + * managed by the scheduler. + * + * @param plugin Plugin that owns the task + * @param task Task to be executed + * @param delay Delay in server ticks before executing first repeat + * @param period Period in server ticks of the task + * @return Task id number (-1 if scheduling failed) + * @deprecated This name is misleading, as it does not schedule "a sync" + * task, but rather, "an async" task + */ + @Deprecated + public int scheduleAsyncRepeatingTask(Plugin plugin, Runnable task, long delay, long period); + + /** + * Calls a method on the main thread and returns a Future object. This + * task will be executed by the main server thread. + *

              + *
            • Note: The Future.get() methods must NOT be called from the main + * thread. + *
            • Note2: There is at least an average of 10ms latency until the + * isDone() method returns true. + *
            + * @param The callable's return type + * @param plugin Plugin that owns the task + * @param task Task to be executed + * @return Future Future object related to the task + */ + public Future callSyncMethod(Plugin plugin, Callable task); + + /** + * Removes task from scheduler. + * + * @param taskId Id number of task to be removed + */ + public void cancelTask(int taskId); + + /** + * Removes all tasks associated with a particular plugin from the + * scheduler. + * + * @param plugin Owner of tasks to be removed + */ + public void cancelTasks(Plugin plugin); + + /** + * Removes all tasks from the scheduler. + */ + public void cancelAllTasks(); + + /** + * Check if the task currently running. + *

            + * A repeating task might not be running currently, but will be running in + * the future. A task that has finished, and does not repeat, will not be + * running ever again. + *

            + * Explicitly, a task is running if there exists a thread for it, and that + * thread is alive. + * + * @param taskId The task to check. + *

            + * @return If the task is currently running. + */ + public boolean isCurrentlyRunning(int taskId); + + /** + * Check if the task queued to be run later. + *

            + * If a repeating task is currently running, it might not be queued now + * but could be in the future. A task that is not queued, and not running, + * will not be queued again. + * + * @param taskId The task to check. + *

            + * @return If the task is queued to be run. + */ + public boolean isQueued(int taskId); + + /** + * Returns a list of all active workers. + *

            + * This list contains asynch tasks that are being executed by separate + * threads. + * + * @return Active workers + */ + public List getActiveWorkers(); + + /** + * Returns a list of all pending tasks. The ordering of the tasks is not + * related to their order of execution. + * + * @return Active workers + */ + public List getPendingTasks(); + + /** + * Returns a task that will run on the next server tick. + * + * @param plugin the reference to the plugin scheduling task + * @param task the task to be run + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalArgumentException if task is null + */ + public BukkitTask runTask(Plugin plugin, Runnable task) throws IllegalArgumentException; + + /** + * @deprecated Use {@link BukkitRunnable#runTask(Plugin)} + */ + @Deprecated + public BukkitTask runTask(Plugin plugin, BukkitRunnable task) throws IllegalArgumentException; + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Returns a task that will run asynchronously. + * + * @param plugin the reference to the plugin scheduling task + * @param task the task to be run + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalArgumentException if task is null + */ + public BukkitTask runTaskAsynchronously(Plugin plugin, Runnable task) throws IllegalArgumentException; + + /** + * @deprecated Use {@link BukkitRunnable#runTaskAsynchronously(Plugin)} + */ + @Deprecated + public BukkitTask runTaskAsynchronously(Plugin plugin, BukkitRunnable task) throws IllegalArgumentException; + + /** + * Returns a task that will run after the specified number of server + * ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param task the task to be run + * @param delay the ticks to wait before running the task + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalArgumentException if task is null + */ + public BukkitTask runTaskLater(Plugin plugin, Runnable task, long delay) throws IllegalArgumentException; + + /** + * @deprecated Use {@link BukkitRunnable#runTaskLater(Plugin, long)} + */ + @Deprecated + public BukkitTask runTaskLater(Plugin plugin, BukkitRunnable task, long delay) throws IllegalArgumentException; + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Returns a task that will run asynchronously after the specified number + * of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param task the task to be run + * @param delay the ticks to wait before running the task + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalArgumentException if task is null + */ + public BukkitTask runTaskLaterAsynchronously(Plugin plugin, Runnable task, long delay) throws IllegalArgumentException; + + /** + * @deprecated Use {@link BukkitRunnable#runTaskLaterAsynchronously(Plugin, long)} + */ + @Deprecated + public BukkitTask runTaskLaterAsynchronously(Plugin plugin, BukkitRunnable task, long delay) throws IllegalArgumentException; + + /** + * Returns a task that will repeatedly run until cancelled, starting after + * the specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param task the task to be run + * @param delay the ticks to wait before running the task + * @param period the ticks to wait between runs + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalArgumentException if task is null + */ + public BukkitTask runTaskTimer(Plugin plugin, Runnable task, long delay, long period) throws IllegalArgumentException; + + /** + * @deprecated Use {@link BukkitRunnable#runTaskTimer(Plugin, long, long)} + */ + @Deprecated + public BukkitTask runTaskTimer(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException; + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

            + * Returns a task that will repeatedly run asynchronously until cancelled, + * starting after the specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param task the task to be run + * @param delay the ticks to wait before running the task for the first + * time + * @param period the ticks to wait between runs + * @return a BukkitTask that contains the id number + * @throws IllegalArgumentException if plugin is null + * @throws IllegalArgumentException if task is null + */ + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, Runnable task, long delay, long period) throws IllegalArgumentException; + + /** + * @deprecated Use {@link BukkitRunnable#runTaskTimerAsynchronously(Plugin, long, long)} + */ + @Deprecated + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException; +} diff --git a/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitTask.java b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitTask.java new file mode 100644 index 0000000..e447e64 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitTask.java @@ -0,0 +1,35 @@ +package org.bukkit.scheduler; + +import org.bukkit.plugin.Plugin; + +/** + * Represents a task being executed by the scheduler + */ +public interface BukkitTask { + + /** + * Returns the taskId for the task. + * + * @return Task id number + */ + public int getTaskId(); + + /** + * Returns the Plugin that owns this task. + * + * @return The Plugin that owns the task + */ + public Plugin getOwner(); + + /** + * Returns true if the Task is a sync task. + * + * @return true if the task is run by main thread + */ + public boolean isSync(); + + /** + * Will attempt to cancel this task. + */ + public void cancel(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitWorker.java b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitWorker.java new file mode 100644 index 0000000..fe1afbd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scheduler/BukkitWorker.java @@ -0,0 +1,34 @@ +package org.bukkit.scheduler; + +import org.bukkit.plugin.Plugin; + +/** + * Represents a worker thread for the scheduler. This gives information about + * the Thread object for the task, owner of the task and the taskId. + *

            + * Workers are used to execute async tasks. + */ +public interface BukkitWorker { + + /** + * Returns the taskId for the task being executed by this worker. + * + * @return Task id number + */ + public int getTaskId(); + + /** + * Returns the Plugin that owns this task. + * + * @return The Plugin that owns the task + */ + public Plugin getOwner(); + + /** + * Returns the thread for the worker. + * + * @return The Thread object for the worker + */ + public Thread getThread(); + +} diff --git a/vspigot-api/src/main/java/org/bukkit/scoreboard/Criterias.java b/vspigot-api/src/main/java/org/bukkit/scoreboard/Criterias.java new file mode 100644 index 0000000..cd81c87 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scoreboard/Criterias.java @@ -0,0 +1,20 @@ +package org.bukkit.scoreboard; + +/** + * Criteria names which trigger an objective to be modified by actions in-game + */ +public class Criterias { + public static final String HEALTH; + public static final String PLAYER_KILLS; + public static final String TOTAL_KILLS; + public static final String DEATHS; + + static { + HEALTH="health"; + PLAYER_KILLS="playerKillCount"; + TOTAL_KILLS="totalKillCount"; + DEATHS="deathCount"; + } + + private Criterias() {} +} diff --git a/vspigot-api/src/main/java/org/bukkit/scoreboard/DisplaySlot.java b/vspigot-api/src/main/java/org/bukkit/scoreboard/DisplaySlot.java new file mode 100644 index 0000000..5d58a18 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scoreboard/DisplaySlot.java @@ -0,0 +1,10 @@ +package org.bukkit.scoreboard; + +/** + * Locations for displaying objectives to the player + */ +public enum DisplaySlot { + BELOW_NAME, + PLAYER_LIST, + SIDEBAR; +} diff --git a/vspigot-api/src/main/java/org/bukkit/scoreboard/Objective.java b/vspigot-api/src/main/java/org/bukkit/scoreboard/Objective.java new file mode 100644 index 0000000..321aac7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scoreboard/Objective.java @@ -0,0 +1,110 @@ +package org.bukkit.scoreboard; + +import org.bukkit.OfflinePlayer; + +/** + * An objective on a scoreboard that can show scores specific to entries. This + * objective is only relevant to the display of the associated {@link + * #getScoreboard() scoreboard}. + */ +public interface Objective { + + /** + * Gets the name of this Objective + * + * @return this objective'ss name + * @throws IllegalStateException if this objective has been unregistered + */ + String getName() throws IllegalStateException; + + /** + * Gets the name displayed to players for this objective + * + * @return this objective's display name + * @throws IllegalStateException if this objective has been unregistered + */ + String getDisplayName() throws IllegalStateException; + + /** + * Sets the name displayed to players for this objective. + * + * @param displayName Display name to set + * @throws IllegalStateException if this objective has been unregistered + * @throws IllegalArgumentException if displayName is null + * @throws IllegalArgumentException if displayName is longer than 32 + * characters. + */ + void setDisplayName(String displayName) throws IllegalStateException, IllegalArgumentException; + + /** + * Gets the criteria this objective tracks. + * + * @return this objective's criteria + * @throws IllegalStateException if this objective has been unregistered + */ + String getCriteria() throws IllegalStateException; + + /** + * Gets if the objective's scores can be modified directly by a plugin. + * + * @return true if scores are modifiable + * @throws IllegalStateException if this objective has been unregistered + * @see Criterias#HEALTH + */ + boolean isModifiable() throws IllegalStateException; + + /** + * Gets the scoreboard to which this objective is attached. + * + * @return Owning scoreboard, or null if it has been {@link #unregister() + * unregistered} + */ + Scoreboard getScoreboard(); + + /** + * Unregisters this objective from the {@link Scoreboard scoreboard.} + * + * @throws IllegalStateException if this objective has been unregistered + */ + void unregister() throws IllegalStateException; + + /** + * Sets this objective to display on the specified slot for the + * scoreboard, removing it from any other display slot. + * + * @param slot display slot to change, or null to not display + * @throws IllegalStateException if this objective has been unregistered + */ + void setDisplaySlot(DisplaySlot slot) throws IllegalStateException; + + /** + * Gets the display slot this objective is displayed at. + * + * @return the display slot for this objective, or null if not displayed + * @throws IllegalStateException if this objective has been unregistered + */ + DisplaySlot getDisplaySlot() throws IllegalStateException; + + /** + * Gets a player's Score for an Objective on this Scoreboard + * + * @param player Player for the Score + * @return Score tracking the Objective and player specified + * @throws IllegalArgumentException if player is null + * @throws IllegalStateException if this objective has been unregistered + * @deprecated Scoreboards can contain entries that aren't players + * @see #getScore(String) + */ + @Deprecated + Score getScore(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException; + + /** + * Gets an entry's Score for an Objective on this Scoreboard. + * + * @param entry Entry for the Score + * @return Score tracking the Objective and entry specified + * @throws IllegalArgumentException if entry is null + * @throws IllegalStateException if this objective has been unregistered + */ + Score getScore(String entry) throws IllegalArgumentException, IllegalStateException; +} diff --git a/vspigot-api/src/main/java/org/bukkit/scoreboard/Score.java b/vspigot-api/src/main/java/org/bukkit/scoreboard/Score.java new file mode 100644 index 0000000..2410cbd --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scoreboard/Score.java @@ -0,0 +1,72 @@ +package org.bukkit.scoreboard; + +import org.bukkit.OfflinePlayer; + +/** + * A score entry for an {@link #getEntry() entry} on an {@link + * #getObjective() objective}. Changing this will not affect any other + * objective or scoreboard. + */ +public interface Score { + + /** + * Gets the OfflinePlayer being tracked by this Score + * + * @return this Score's tracked player + * @deprecated Scoreboards can contain entries that aren't players + * @see #getEntry() + */ + @Deprecated + OfflinePlayer getPlayer(); + + /** + * Gets the entry being tracked by this Score + * + * @return this Score's tracked entry + */ + String getEntry(); + + /** + * Gets the Objective being tracked by this Score + * + * @return this Score's tracked objective + */ + Objective getObjective(); + + /** + * Gets the current score + * + * @return the current score + * @throws IllegalStateException if the associated objective has been + * unregistered + */ + int getScore() throws IllegalStateException; + + /** + * Sets the current score. + * + * @param score New score + * @throws IllegalStateException if the associated objective has been + * unregistered + */ + void setScore(int score) throws IllegalStateException; + + // Spigot start + /** + * Shows if this score has been set at any point in time. + * + * @return if this score has been set before + * @throws IllegalStateException if the associated objective has been + * unregistered + */ + boolean isScoreSet() throws IllegalStateException; + // Spigot end + + /** + * Gets the scoreboard for the associated objective. + * + * @return the owning objective's scoreboard, or null if it has been + * {@link Objective#unregister() unregistered} + */ + Scoreboard getScoreboard(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/scoreboard/Scoreboard.java b/vspigot-api/src/main/java/org/bukkit/scoreboard/Scoreboard.java new file mode 100644 index 0000000..d244a7f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scoreboard/Scoreboard.java @@ -0,0 +1,159 @@ +package org.bukkit.scoreboard; + +import java.util.Set; + +import org.bukkit.OfflinePlayer; + +/** + * A scoreboard + */ +public interface Scoreboard { + + /** + * Registers an Objective on this Scoreboard + * + * @param name Name of the Objective + * @param criteria Criteria for the Objective + * @return The registered Objective + * @throws IllegalArgumentException if name is null + * @throws IllegalArgumentException if criteria is null + * @throws IllegalArgumentException if an objective by that name already + * exists + */ + Objective registerNewObjective(String name, String criteria) throws IllegalArgumentException; + + /** + * Gets an Objective on this Scoreboard by name + * + * @param name Name of the Objective + * @return the Objective or null if it does not exist + * @throws IllegalArgumentException if name is null + */ + Objective getObjective(String name) throws IllegalArgumentException; + + /** + * Gets all Objectives of a Criteria on the Scoreboard + * + * @param criteria Criteria to search by + * @return an immutable set of Objectives using the specified Criteria + */ + Set getObjectivesByCriteria(String criteria) throws IllegalArgumentException; + + /** + * Gets all Objectives on this Scoreboard + * + * @return An immutable set of all Objectives on this Scoreboard + */ + Set getObjectives(); + + /** + * Gets the Objective currently displayed in a DisplaySlot on this + * Scoreboard + * + * @param slot The DisplaySlot + * @return the Objective currently displayed or null if nothing is + * displayed in that DisplaySlot + * @throws IllegalArgumentException if slot is null + */ + Objective getObjective(DisplaySlot slot) throws IllegalArgumentException; + + /** + * Gets all scores for a player on this Scoreboard + * + * @param player the player whose scores are being retrieved + * @return immutable set of all scores tracked for the player + * @throws IllegalArgumentException if player is null + * @deprecated Scoreboards can contain entries that aren't players + * @see #getScores(String) + */ + @Deprecated + Set getScores(OfflinePlayer player) throws IllegalArgumentException; + + /** + * Gets all scores for an entry on this Scoreboard + * + * @param entry the entry whose scores are being retrieved + * @return immutable set of all scores tracked for the entry + * @throws IllegalArgumentException if entry is null + */ + Set getScores(String entry) throws IllegalArgumentException; + + /** + * Removes all scores for a player on this Scoreboard + * + * @param player the player to drop all current scores for + * @throws IllegalArgumentException if player is null + * @deprecated Scoreboards can contain entries that aren't players + * @see #resetScores(String) + */ + @Deprecated + void resetScores(OfflinePlayer player) throws IllegalArgumentException; + + /** + * Removes all scores for an entry on this Scoreboard + * + * @param entry the entry to drop all current scores for + * @throws IllegalArgumentException if entry is null + */ + void resetScores(String entry) throws IllegalArgumentException; + + /** + * Gets a player's Team on this Scoreboard + * + * @param player the player to search for + * @return the player's Team or null if the player is not on a team + * @throws IllegalArgumentException if player is null + */ + Team getPlayerTeam(OfflinePlayer player) throws IllegalArgumentException; + + /** + * Gets a Team by name on this Scoreboard + * + * @param teamName Team name + * @return the matching Team or null if no matches + * @throws IllegalArgumentException if teamName is null + */ + Team getTeam(String teamName) throws IllegalArgumentException; + + /** + * Gets all teams on this Scoreboard + * + * @return an immutable set of Teams + */ + Set getTeams(); + + /** + * Registers a Team on this Scoreboard + * + * @param name Team name + * @return registered Team + * @throws IllegalArgumentException if name is null + * @throws IllegalArgumentException if team by that name already exists + */ + Team registerNewTeam(String name) throws IllegalArgumentException; + + /** + * Gets all players tracked by this Scoreboard + * + * @return immutable set of all tracked players + * @deprecated Scoreboards can contain entries that aren't players + * @see #getEntries() + */ + @Deprecated + Set getPlayers(); + + /** + * Gets all entries tracked by this Scoreboard + * + * @return immutable set of all tracked entries + */ + Set getEntries(); + + /** + * Clears any objective in the specified slot. + * + * @param slot the slot to remove objectives + * @throws IllegalArgumentException if slot is null + */ + void clearSlot(DisplaySlot slot) throws IllegalArgumentException; +} diff --git a/vspigot-api/src/main/java/org/bukkit/scoreboard/ScoreboardManager.java b/vspigot-api/src/main/java/org/bukkit/scoreboard/ScoreboardManager.java new file mode 100644 index 0000000..00b67a1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scoreboard/ScoreboardManager.java @@ -0,0 +1,29 @@ +package org.bukkit.scoreboard; + +import java.lang.ref.WeakReference; + +/** + * Manager of Scoreboards + */ +public interface ScoreboardManager { + + /** + * Gets the primary Scoreboard controlled by the server. + *

            + * This Scoreboard is saved by the server, is affected by the /scoreboard + * command, and is the scoreboard shown by default to players. + * + * @return the default sever scoreboard + */ + Scoreboard getMainScoreboard(); + + /** + * Gets a new Scoreboard to be tracked by the server. This scoreboard will + * be tracked as long as a reference is kept, either by a player or by a + * plugin. + * + * @return the registered Scoreboard + * @see WeakReference + */ + Scoreboard getNewScoreboard(); +} diff --git a/vspigot-api/src/main/java/org/bukkit/scoreboard/Team.java b/vspigot-api/src/main/java/org/bukkit/scoreboard/Team.java new file mode 100644 index 0000000..b90b9c3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/scoreboard/Team.java @@ -0,0 +1,210 @@ +package org.bukkit.scoreboard; + +import java.util.Set; + +import org.bukkit.OfflinePlayer; +import org.bukkit.potion.PotionEffectType; + +/** + * A team on a scoreboard that has a common display theme and other + * properties. This team is only relevant to the display of the associated + * {@link #getScoreboard() scoreboard}. + */ +public interface Team { + + /** + * Gets the name of this Team + * + * @return Objective name + * @throws IllegalStateException if this team has been unregistered + */ + String getName() throws IllegalStateException; + + /** + * Gets the name displayed to players for this team + * + * @return Team display name + * @throws IllegalStateException if this team has been unregistered + */ + String getDisplayName() throws IllegalStateException; + + /** + * Sets the name displayed to players for this team + * + * @param displayName New display name + * @throws IllegalArgumentException if displayName is longer than 32 + * characters. + * @throws IllegalStateException if this team has been unregistered + */ + void setDisplayName(String displayName) throws IllegalStateException, IllegalArgumentException; + + /** + * Gets the prefix prepended to the display of players on this team. + * + * @return Team prefix + * @throws IllegalStateException if this team has been unregistered + */ + String getPrefix() throws IllegalStateException; + + /** + * Sets the prefix prepended to the display of players on this team. + * + * @param prefix New prefix + * @throws IllegalArgumentException if prefix is null + * @throws IllegalArgumentException if prefix is longer than 16 + * characters + * @throws IllegalStateException if this team has been unregistered + */ + void setPrefix(String prefix) throws IllegalStateException, IllegalArgumentException; + + /** + * Gets the suffix appended to the display of players on this team. + * + * @return the team's current suffix + * @throws IllegalStateException if this team has been unregistered + */ + String getSuffix() throws IllegalStateException; + + /** + * Sets the suffix appended to the display of players on this team. + * + * @param suffix the new suffix for this team. + * @throws IllegalArgumentException if suffix is null + * @throws IllegalArgumentException if suffix is longer than 16 + * characters + * @throws IllegalStateException if this team has been unregistered + */ + void setSuffix(String suffix) throws IllegalStateException, IllegalArgumentException; + + /** + * Gets the team friendly fire state + * + * @return true if friendly fire is enabled + * @throws IllegalStateException if this team has been unregistered + */ + boolean allowFriendlyFire() throws IllegalStateException; + + /** + * Sets the team friendly fire state + * + * @param enabled true if friendly fire is to be allowed + * @throws IllegalStateException if this team has been unregistered + */ + void setAllowFriendlyFire(boolean enabled) throws IllegalStateException; + + /** + * Gets the team's ability to see {@link PotionEffectType#INVISIBILITY + * invisible} teammates. + * + * @return true if team members can see invisible members + * @throws IllegalStateException if this team has been unregistered + */ + boolean canSeeFriendlyInvisibles() throws IllegalStateException; + + /** + * Sets the team's ability to see {@link PotionEffectType#INVISIBILITY + * invisible} teammates. + * + * @param enabled true if invisible teammates are to be visible + * @throws IllegalStateException if this team has been unregistered + */ + void setCanSeeFriendlyInvisibles(boolean enabled) throws IllegalStateException; + + /** + * Gets the Set of players on the team + * + * @return players on the team + * @throws IllegalStateException if this team has been unregistered + */ + Set getPlayers() throws IllegalStateException; + + // Spigot start + /** + * Same as the player method, but with an arbitrary string. + * + * @see #getPlayers() + */ + Set getEntries() throws IllegalStateException; + // Spigot End + + /** + * Gets the size of the team + * + * @return number of players on the team + * @throws IllegalStateException if this team has been unregistered + */ + int getSize() throws IllegalStateException; + + /** + * Gets the Scoreboard to which this team is attached + * + * @return Owning scoreboard, or null if this team has been {@link + * #unregister() unregistered} + */ + Scoreboard getScoreboard(); + + /** + * This puts the specified player onto this team for the scoreboard. + *

            + * This will remove the player from any other team on the scoreboard. + * + * @param player the player to add + * @throws IllegalArgumentException if player is null + * @throws IllegalStateException if this team has been unregistered + */ + void addPlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException; + + // Spigot start + /** + * Same as the player method, but with an arbitrary string. + * + * @see #addPlayer(org.bukkit.OfflinePlayer) + */ + void addEntry(String entry) throws IllegalStateException, IllegalArgumentException; + // Spigot end + + /** + * Removes the player from this team. + * + * @param player the player to remove + * @return if the player was on this team + * @throws IllegalArgumentException if player is null + * @throws IllegalStateException if this team has been unregistered + */ + boolean removePlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException; + + // Spigot start + /** + * Same as the player method, but with an arbitrary string. + * + * @see #removePlayer(org.bukkit.OfflinePlayer) + */ + boolean removeEntry(String entry) throws IllegalStateException, IllegalArgumentException; + // Spigot end + + /** + * Unregisters this team from the Scoreboard + * + * @throws IllegalStateException if this team has been unregistered + */ + void unregister() throws IllegalStateException; + + /** + * Checks to see if the specified player is a member of this team. + * + * @param player the player to search for + * @return true if the player is a member of this team + * @throws IllegalArgumentException if player is null + * @throws IllegalStateException if this team has been unregistered + */ + boolean hasPlayer(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException; + + // Spigot start + /** + * Same as the player method, but with an arbitrary string. + * + * @see #hasPlayer(org.bukkit.OfflinePlayer) + */ + boolean hasEntry(String entry) throws IllegalArgumentException,IllegalStateException; + // Spigot end +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/BlockIterator.java b/vspigot-api/src/main/java/org/bukkit/util/BlockIterator.java new file mode 100644 index 0000000..5c85778 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/BlockIterator.java @@ -0,0 +1,357 @@ +package org.bukkit.util; + +import static org.bukkit.util.NumberConversions.*; + +import org.bukkit.World; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.LivingEntity; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * This class performs ray tracing and iterates along blocks on a line + */ +public class BlockIterator implements Iterator { + + private final World world; + private final int maxDistance; + + private static final int gridSize = 1 << 24; + + private boolean end = false; + + private Block[] blockQueue = new Block[3]; + private int currentBlock = 0; + private int currentDistance = 0; + private int maxDistanceInt; + + private int secondError; + private int thirdError; + + private int secondStep; + private int thirdStep; + + private BlockFace mainFace; + private BlockFace secondFace; + private BlockFace thirdFace; + + /** + * Constructs the BlockIterator + * + * @param world The world to use for tracing + * @param start A Vector giving the initial location for the trace + * @param direction A Vector pointing in the direction for the trace + * @param yOffset The trace begins vertically offset from the start vector + * by this value + * @param maxDistance This is the maximum distance in blocks for the + * trace. Setting this value above 140 may lead to problems with + * unloaded chunks. A value of 0 indicates no limit + * + */ + public BlockIterator(World world, Vector start, Vector direction, double yOffset, int maxDistance) { + this.world = world; + this.maxDistance = maxDistance; + + Vector startClone = start.clone(); + + startClone.setY(startClone.getY() + yOffset); + + currentDistance = 0; + + double mainDirection = 0; + double secondDirection = 0; + double thirdDirection = 0; + + double mainPosition = 0; + double secondPosition = 0; + double thirdPosition = 0; + + Block startBlock = this.world.getBlockAt(floor(startClone.getX()), floor(startClone.getY()), floor(startClone.getZ())); + + if (getXLength(direction) > mainDirection) { + mainFace = getXFace(direction); + mainDirection = getXLength(direction); + mainPosition = getXPosition(direction, startClone, startBlock); + + secondFace = getYFace(direction); + secondDirection = getYLength(direction); + secondPosition = getYPosition(direction, startClone, startBlock); + + thirdFace = getZFace(direction); + thirdDirection = getZLength(direction); + thirdPosition = getZPosition(direction, startClone, startBlock); + } + if (getYLength(direction) > mainDirection) { + mainFace = getYFace(direction); + mainDirection = getYLength(direction); + mainPosition = getYPosition(direction, startClone, startBlock); + + secondFace = getZFace(direction); + secondDirection = getZLength(direction); + secondPosition = getZPosition(direction, startClone, startBlock); + + thirdFace = getXFace(direction); + thirdDirection = getXLength(direction); + thirdPosition = getXPosition(direction, startClone, startBlock); + } + if (getZLength(direction) > mainDirection) { + mainFace = getZFace(direction); + mainDirection = getZLength(direction); + mainPosition = getZPosition(direction, startClone, startBlock); + + secondFace = getXFace(direction); + secondDirection = getXLength(direction); + secondPosition = getXPosition(direction, startClone, startBlock); + + thirdFace = getYFace(direction); + thirdDirection = getYLength(direction); + thirdPosition = getYPosition(direction, startClone, startBlock); + } + + // trace line backwards to find intercept with plane perpendicular to the main axis + + double d = mainPosition / mainDirection; // how far to hit face behind + double secondd = secondPosition - secondDirection * d; + double thirdd = thirdPosition - thirdDirection * d; + + // Guarantee that the ray will pass though the start block. + // It is possible that it would miss due to rounding + // This should only move the ray by 1 grid position + secondError = floor(secondd * gridSize); + secondStep = round(secondDirection / mainDirection * gridSize); + thirdError = floor(thirdd * gridSize); + thirdStep = round(thirdDirection / mainDirection * gridSize); + + if (secondError + secondStep <= 0) { + secondError = -secondStep + 1; + } + + if (thirdError + thirdStep <= 0) { + thirdError = -thirdStep + 1; + } + + Block lastBlock; + + lastBlock = startBlock.getRelative(mainFace.getOppositeFace()); + + if (secondError < 0) { + secondError += gridSize; + lastBlock = lastBlock.getRelative(secondFace.getOppositeFace()); + } + + if (thirdError < 0) { + thirdError += gridSize; + lastBlock = lastBlock.getRelative(thirdFace.getOppositeFace()); + } + + // This means that when the variables are positive, it means that the coord=1 boundary has been crossed + secondError -= gridSize; + thirdError -= gridSize; + + blockQueue[0] = lastBlock; + currentBlock = -1; + + scan(); + + boolean startBlockFound = false; + + for (int cnt = currentBlock; cnt >= 0; cnt--) { + if (blockEquals(blockQueue[cnt], startBlock)) { + currentBlock = cnt; + startBlockFound = true; + break; + } + } + + if (!startBlockFound) { + throw new IllegalStateException("Start block missed in BlockIterator"); + } + + // Calculate the number of planes passed to give max distance + maxDistanceInt = round(maxDistance / (Math.sqrt(mainDirection * mainDirection + secondDirection * secondDirection + thirdDirection * thirdDirection) / mainDirection)); + + } + + private boolean blockEquals(Block a, Block b) { + return a.getX() == b.getX() && a.getY() == b.getY() && a.getZ() == b.getZ(); + } + + private BlockFace getXFace(Vector direction) { + return ((direction.getX() > 0) ? BlockFace.EAST : BlockFace.WEST); + } + + private BlockFace getYFace(Vector direction) { + return ((direction.getY() > 0) ? BlockFace.UP : BlockFace.DOWN); + } + + private BlockFace getZFace(Vector direction) { + return ((direction.getZ() > 0) ? BlockFace.SOUTH : BlockFace.NORTH); + } + + private double getXLength(Vector direction) { + return Math.abs(direction.getX()); + } + + private double getYLength(Vector direction) { + return Math.abs(direction.getY()); + } + + private double getZLength(Vector direction) { + return Math.abs(direction.getZ()); + } + + private double getPosition(double direction, double position, int blockPosition) { + return direction > 0 ? (position - blockPosition) : (blockPosition + 1 - position); + } + + private double getXPosition(Vector direction, Vector position, Block block) { + return getPosition(direction.getX(), position.getX(), block.getX()); + } + + private double getYPosition(Vector direction, Vector position, Block block) { + return getPosition(direction.getY(), position.getY(), block.getY()); + } + + private double getZPosition(Vector direction, Vector position, Block block) { + return getPosition(direction.getZ(), position.getZ(), block.getZ()); + } + + /** + * Constructs the BlockIterator + * + * @param loc The location for the start of the ray trace + * @param yOffset The trace begins vertically offset from the start vector + * by this value + * @param maxDistance This is the maximum distance in blocks for the + * trace. Setting this value above 140 may lead to problems with + * unloaded chunks. A value of 0 indicates no limit + */ + public BlockIterator(Location loc, double yOffset, int maxDistance) { + this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, maxDistance); + } + + /** + * Constructs the BlockIterator. + * + * @param loc The location for the start of the ray trace + * @param yOffset The trace begins vertically offset from the start vector + * by this value + */ + + public BlockIterator(Location loc, double yOffset) { + this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, 0); + } + + /** + * Constructs the BlockIterator. + * + * @param loc The location for the start of the ray trace + */ + + public BlockIterator(Location loc) { + this(loc, 0D); + } + + /** + * Constructs the BlockIterator. + * + * @param entity Information from the entity is used to set up the trace + * @param maxDistance This is the maximum distance in blocks for the + * trace. Setting this value above 140 may lead to problems with + * unloaded chunks. A value of 0 indicates no limit + */ + + public BlockIterator(LivingEntity entity, int maxDistance) { + this(entity.getLocation(), entity.getEyeHeight(), maxDistance); + } + + /** + * Constructs the BlockIterator. + * + * @param entity Information from the entity is used to set up the trace + */ + + public BlockIterator(LivingEntity entity) { + this(entity, 0); + } + + /** + * Returns true if the iteration has more elements + */ + + public boolean hasNext() { + scan(); + return currentBlock != -1; + } + + /** + * Returns the next Block in the trace + * + * @return the next Block in the trace + */ + + public Block next() { + scan(); + if (currentBlock <= -1) { + throw new NoSuchElementException(); + } else { + return blockQueue[currentBlock--]; + } + } + + public void remove() { + throw new UnsupportedOperationException("[BlockIterator] doesn't support block removal"); + } + + private void scan() { + if (currentBlock >= 0) { + return; + } + if (maxDistance != 0 && currentDistance > maxDistanceInt) { + end = true; + return; + } + if (end) { + return; + } + + currentDistance++; + + secondError += secondStep; + thirdError += thirdStep; + + if (secondError > 0 && thirdError > 0) { + blockQueue[2] = blockQueue[0].getRelative(mainFace); + if (((long) secondStep) * ((long) thirdError) < ((long) thirdStep) * ((long) secondError)) { + blockQueue[1] = blockQueue[2].getRelative(secondFace); + blockQueue[0] = blockQueue[1].getRelative(thirdFace); + } else { + blockQueue[1] = blockQueue[2].getRelative(thirdFace); + blockQueue[0] = blockQueue[1].getRelative(secondFace); + } + thirdError -= gridSize; + secondError -= gridSize; + currentBlock = 2; + return; + } else if (secondError > 0) { + blockQueue[1] = blockQueue[0].getRelative(mainFace); + blockQueue[0] = blockQueue[1].getRelative(secondFace); + secondError -= gridSize; + currentBlock = 1; + return; + } else if (thirdError > 0) { + blockQueue[1] = blockQueue[0].getRelative(mainFace); + blockQueue[0] = blockQueue[1].getRelative(thirdFace); + thirdError -= gridSize; + currentBlock = 1; + return; + } else { + blockQueue[0] = blockQueue[0].getRelative(mainFace); + currentBlock = 0; + return; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/BlockVector.java b/vspigot-api/src/main/java/org/bukkit/util/BlockVector.java new file mode 100644 index 0000000..bdf8f6d --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/BlockVector.java @@ -0,0 +1,128 @@ +package org.bukkit.util; + +import java.util.Map; +import org.bukkit.configuration.serialization.SerializableAs; + +/** + * A vector with a hash function that floors the X, Y, Z components, a la + * BlockVector in WorldEdit. BlockVectors can be used in hash sets and + * hash maps. Be aware that BlockVectors are mutable, but it is important + * that BlockVectors are never changed once put into a hash set or hash map. + */ +@SerializableAs("BlockVector") +public class BlockVector extends Vector { + + /** + * Construct the vector with all components as 0. + */ + public BlockVector() { + this.x = 0; + this.y = 0; + this.z = 0; + } + + /** + * Construct the vector with another vector. + * + * @param vec The other vector. + */ + public BlockVector(Vector vec) { + this.x = vec.getX(); + this.y = vec.getY(); + this.z = vec.getZ(); + } + + /** + * Construct the vector with provided integer components. + * + * @param x X component + * @param y Y component + * @param z Z component + */ + public BlockVector(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Construct the vector with provided double components. + * + * @param x X component + * @param y Y component + * @param z Z component + */ + public BlockVector(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Construct the vector with provided float components. + * + * @param x X component + * @param y Y component + * @param z Z component + */ + public BlockVector(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Checks if another object is equivalent. + * + * @param obj The other object + * @return whether the other object is equivalent + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BlockVector)) { + return false; + } + BlockVector other = (BlockVector) obj; + + return (int) other.getX() == (int) this.x && (int) other.getY() == (int) this.y && (int) other.getZ() == (int) this.z; + + } + + /** + * Returns a hash code for this vector. + * + * @return hash code + */ + @Override + public int hashCode() { + return (Integer.valueOf((int) x).hashCode() >> 13) ^ (Integer.valueOf((int) y).hashCode() >> 7) ^ Integer.valueOf((int) z).hashCode(); + } + + /** + * Get a new block vector. + * + * @return vector + */ + @Override + public BlockVector clone() { + return (BlockVector) super.clone(); + } + + public static BlockVector deserialize(Map args) { + double x = 0; + double y = 0; + double z = 0; + + if (args.containsKey("x")) { + x = (Double) args.get("x"); + } + if (args.containsKey("y")) { + y = (Double) args.get("y"); + } + if (args.containsKey("z")) { + z = (Double) args.get("z"); + } + + return new BlockVector(x, y, z); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/CachedServerIcon.java b/vspigot-api/src/main/java/org/bukkit/util/CachedServerIcon.java new file mode 100644 index 0000000..5ca863b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/CachedServerIcon.java @@ -0,0 +1,15 @@ +package org.bukkit.util; + +import org.bukkit.Server; +import org.bukkit.event.server.ServerListPingEvent; + +/** + * This is a cached version of a server-icon. It's internal representation + * and implementation is undefined. + * + * @see Server#getServerIcon() + * @see Server#loadServerIcon(java.awt.image.BufferedImage) + * @see Server#loadServerIcon(java.io.File) + * @see ServerListPingEvent#setServerIcon(CachedServerIcon) + */ +public interface CachedServerIcon {} diff --git a/vspigot-api/src/main/java/org/bukkit/util/ChatPaginator.java b/vspigot-api/src/main/java/org/bukkit/util/ChatPaginator.java new file mode 100644 index 0000000..24802d1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/ChatPaginator.java @@ -0,0 +1,169 @@ +package org.bukkit.util; + +import org.bukkit.ChatColor; + +import java.util.LinkedList; +import java.util.List; + +/** + * The ChatPaginator takes a raw string of arbitrary length and breaks it down + * into an array of strings appropriate for displaying on the Minecraft player + * console. + */ +public class ChatPaginator { + public static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 55; // Will never wrap, even with the largest characters + public static final int AVERAGE_CHAT_PAGE_WIDTH = 65; // Will typically not wrap using an average character distribution + public static final int UNBOUNDED_PAGE_WIDTH = Integer.MAX_VALUE; + public static final int OPEN_CHAT_PAGE_HEIGHT = 20; // The height of an expanded chat window + public static final int CLOSED_CHAT_PAGE_HEIGHT = 10; // The height of the default chat window + public static final int UNBOUNDED_PAGE_HEIGHT = Integer.MAX_VALUE; + + /** + * Breaks a raw string up into pages using the default width and height. + * + * @param unpaginatedString The raw string to break. + * @param pageNumber The page number to fetch. + * @return A single chat page. + */ + public static ChatPage paginate(String unpaginatedString, int pageNumber) { + return paginate(unpaginatedString, pageNumber, GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH, CLOSED_CHAT_PAGE_HEIGHT); + } + + /** + * Breaks a raw string up into pages using a provided width and height. + * + * @param unpaginatedString The raw string to break. + * @param pageNumber The page number to fetch. + * @param lineLength The desired width of a chat line. + * @param pageHeight The desired number of lines in a page. + * @return A single chat page. + */ + public static ChatPage paginate(String unpaginatedString, int pageNumber, int lineLength, int pageHeight) { + String[] lines = wordWrap(unpaginatedString, lineLength); + + int totalPages = lines.length / pageHeight + (lines.length % pageHeight == 0 ? 0 : 1); + int actualPageNumber = pageNumber <= totalPages ? pageNumber : totalPages; + + int from = (actualPageNumber - 1) * pageHeight; + int to = from + pageHeight <= lines.length ? from + pageHeight : lines.length; + String[] selectedLines = Java15Compat.Arrays_copyOfRange(lines, from, to); + + return new ChatPage(selectedLines, actualPageNumber, totalPages); + } + + /** + * Breaks a raw string up into a series of lines. Words are wrapped using + * spaces as decimeters and the newline character is respected. + * + * @param rawString The raw string to break. + * @param lineLength The length of a line of text. + * @return An array of word-wrapped lines. + */ + public static String[] wordWrap(String rawString, int lineLength) { + // A null string is a single line + if (rawString == null) { + return new String[] {""}; + } + + // A string shorter than the lineWidth is a single line + if (rawString.length() <= lineLength && !rawString.contains("\n")) { + return new String[] {rawString}; + } + + char[] rawChars = (rawString + ' ').toCharArray(); // add a trailing space to trigger pagination + StringBuilder word = new StringBuilder(); + StringBuilder line = new StringBuilder(); + List lines = new LinkedList(); + int lineColorChars = 0; + + for (int i = 0; i < rawChars.length; i++) { + char c = rawChars[i]; + + // skip chat color modifiers + if (c == ChatColor.COLOR_CHAR) { + word.append(ChatColor.getByChar(rawChars[i + 1])); + lineColorChars += 2; + i++; // Eat the next character as we have already processed it + continue; + } + + if (c == ' ' || c == '\n') { + if (line.length() == 0 && word.length() > lineLength) { // special case: extremely long word begins a line + for (String partialWord : word.toString().split("(?<=\\G.{" + lineLength + "})")) { + lines.add(partialWord); + } + } else if (line.length() + word.length() - lineColorChars == lineLength) { // Line exactly the correct length...newline + line.append(word); + lines.add(line.toString()); + line = new StringBuilder(); + lineColorChars = 0; + } else if (line.length() + 1 + word.length() - lineColorChars > lineLength) { // Line too long...break the line + for (String partialWord : word.toString().split("(?<=\\G.{" + lineLength + "})")) { + lines.add(line.toString()); + line = new StringBuilder(partialWord); + } + lineColorChars = 0; + } else { + if (line.length() > 0) { + line.append(' '); + } + line.append(word); + } + word = new StringBuilder(); + + if (c == '\n') { // Newline forces the line to flush + lines.add(line.toString()); + line = new StringBuilder(); + } + } else { + word.append(c); + } + } + + if(line.length() > 0) { // Only add the last line if there is anything to add + lines.add(line.toString()); + } + + // Iterate over the wrapped lines, applying the last color from one line to the beginning of the next + if (lines.get(0).length() == 0 || lines.get(0).charAt(0) != ChatColor.COLOR_CHAR) { + lines.set(0, ChatColor.WHITE + lines.get(0)); + } + for (int i = 1; i < lines.size(); i++) { + final String pLine = lines.get(i-1); + final String subLine = lines.get(i); + + char color = pLine.charAt(pLine.lastIndexOf(ChatColor.COLOR_CHAR) + 1); + if (subLine.length() == 0 || subLine.charAt(0) != ChatColor.COLOR_CHAR) { + lines.set(i, ChatColor.getByChar(color) + subLine); + } + } + + return lines.toArray(new String[lines.size()]); + } + + public static class ChatPage { + + private String[] lines; + private int pageNumber; + private int totalPages; + + public ChatPage(String[] lines, int pageNumber, int totalPages) { + this.lines = lines; + this.pageNumber = pageNumber; + this.totalPages = totalPages; + } + + public int getPageNumber() { + return pageNumber; + } + + public int getTotalPages() { + return totalPages; + } + + public String[] getLines() { + + return lines; + } + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/FileUtil.java b/vspigot-api/src/main/java/org/bukkit/util/FileUtil.java new file mode 100644 index 0000000..7cabf4c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/FileUtil.java @@ -0,0 +1,57 @@ +package org.bukkit.util; + +import java.nio.channels.FileChannel; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Class containing file utilities + */ +public class FileUtil { + + /** + * This method copies one file to another location + * + * @param inFile the source filename + * @param outFile the target filename + * @return true on success + */ + public static boolean copy(File inFile, File outFile) { + if (!inFile.exists()) { + return false; + } + + FileChannel in = null; + FileChannel out = null; + + try { + in = new FileInputStream(inFile).getChannel(); + out = new FileOutputStream(outFile).getChannel(); + + long pos = 0; + long size = in.size(); + + while (pos < size) { + pos += in.transferTo(pos, 10 * 1024 * 1024, out); + } + } catch (IOException ioe) { + return false; + } finally { + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException ioe) { + return false; + } + } + + return true; + + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/Java15Compat.java b/vspigot-api/src/main/java/org/bukkit/util/Java15Compat.java new file mode 100644 index 0000000..c119742 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/Java15Compat.java @@ -0,0 +1,21 @@ +package org.bukkit.util; + +import java.lang.reflect.Array; + +public class Java15Compat { + @SuppressWarnings("unchecked") + public static T[] Arrays_copyOfRange(T[] original, int start, int end) { + if (original.length >= start && 0 <= start) { + if (start <= end) { + int length = end - start; + int copyLength = Math.min(length, original.length - start); + T[] copy = (T[]) Array.newInstance(original.getClass().getComponentType(), length); + + System.arraycopy(original, start, copy, 0, copyLength); + return copy; + } + throw new IllegalArgumentException(); + } + throw new ArrayIndexOutOfBoundsException(); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/NumberConversions.java b/vspigot-api/src/main/java/org/bukkit/util/NumberConversions.java new file mode 100644 index 0000000..29f81d0 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/NumberConversions.java @@ -0,0 +1,104 @@ +package org.bukkit.util; + +/** + * Utils for casting number types to other number types + */ +public final class NumberConversions { + private NumberConversions() {} + + public static int floor(double num) { + final int floor = (int) num; + return floor == num ? floor : floor - (int) (Double.doubleToRawLongBits(num) >>> 63); + } + + public static int ceil(final double num) { + final int floor = (int) num; + return floor == num ? floor : floor + (int) (~Double.doubleToRawLongBits(num) >>> 63); + } + + public static int round(double num) { + return floor(num + 0.5d); + } + + public static double square(double num) { + return num * num; + } + + public static int toInt(Object object) { + if (object instanceof Number) { + return ((Number) object).intValue(); + } + + try { + return Integer.valueOf(object.toString()); + } catch (NumberFormatException e) { + } catch (NullPointerException e) { + } + return 0; + } + + public static float toFloat(Object object) { + if (object instanceof Number) { + return ((Number) object).floatValue(); + } + + try { + return Float.valueOf(object.toString()); + } catch (NumberFormatException e) { + } catch (NullPointerException e) { + } + return 0; + } + + public static double toDouble(Object object) { + if (object instanceof Number) { + return ((Number) object).doubleValue(); + } + + try { + return Double.valueOf(object.toString()); + } catch (NumberFormatException e) { + } catch (NullPointerException e) { + } + return 0; + } + + public static long toLong(Object object) { + if (object instanceof Number) { + return ((Number) object).longValue(); + } + + try { + return Long.valueOf(object.toString()); + } catch (NumberFormatException e) { + } catch (NullPointerException e) { + } + return 0; + } + + public static short toShort(Object object) { + if (object instanceof Number) { + return ((Number) object).shortValue(); + } + + try { + return Short.valueOf(object.toString()); + } catch (NumberFormatException e) { + } catch (NullPointerException e) { + } + return 0; + } + + public static byte toByte(Object object) { + if (object instanceof Number) { + return ((Number) object).byteValue(); + } + + try { + return Byte.valueOf(object.toString()); + } catch (NumberFormatException e) { + } catch (NullPointerException e) { + } + return 0; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/StringUtil.java b/vspigot-api/src/main/java/org/bukkit/util/StringUtil.java new file mode 100644 index 0000000..4a8753f --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/StringUtil.java @@ -0,0 +1,57 @@ +package org.bukkit.util; + +import java.util.Collection; +import org.apache.commons.lang.Validate; + +public class StringUtil { + + /** + * Copies all elements from the iterable collection of originals to the + * collection provided. + * + * @param token String to search for + * @param originals An iterable collection of strings to filter. + * @param collection The collection to add matches to + * @return the collection provided that would have the elements copied + * into + * @throws UnsupportedOperationException if the collection is immutable + * and originals contains a string which starts with the specified + * search string. + * @throws IllegalArgumentException if any parameter is is null + * @throws IllegalArgumentException if originals contains a null element. + * Note: the collection may be modified before this is thrown + */ + public static > T copyPartialMatches(final String token, final Iterable originals, final T collection) throws UnsupportedOperationException, IllegalArgumentException { + Validate.notNull(token, "Search token cannot be null"); + Validate.notNull(collection, "Collection cannot be null"); + Validate.notNull(originals, "Originals cannot be null"); + + for (String string : originals) { + if (startsWithIgnoreCase(string, token)) { + collection.add(string); + } + } + + return collection; + } + + /** + * This method uses a region to check case-insensitive equality. This + * means the internal array does not need to be copied like a + * toLowerCase() call would. + * + * @param string String to check + * @param prefix Prefix of string to compare + * @return true if provided string starts with, ignoring case, the prefix + * provided + * @throws NullPointerException if prefix is null + * @throws IllegalArgumentException if string is null + */ + public static boolean startsWithIgnoreCase(final String string, final String prefix) throws IllegalArgumentException, NullPointerException { + Validate.notNull(string, "Cannot check a null string for a match"); + if (string.length() < prefix.length()) { + return false; + } + return string.regionMatches(true, 0, prefix, 0, prefix.length()); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/Vector.java b/vspigot-api/src/main/java/org/bukkit/util/Vector.java new file mode 100644 index 0000000..61116ea --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/Vector.java @@ -0,0 +1,667 @@ +package org.bukkit.util; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; + +/** + * Represents a mutable vector. Because the components of Vectors are mutable, + * storing Vectors long term may be dangerous if passing code modifies the + * Vector later. If you want to keep around a Vector, it may be wise to call + * clone() in order to get a copy. + */ +@SerializableAs("Vector") +public class Vector implements Cloneable, ConfigurationSerializable { + private static final long serialVersionUID = -2657651106777219169L; + + private static Random random = new Random(); + + /** + * Threshold for fuzzy equals(). + */ + private static final double epsilon = 0.000001; + + protected double x; + protected double y; + protected double z; + + /** + * Construct the vector with all components as 0. + */ + public Vector() { + this.x = 0; + this.y = 0; + this.z = 0; + } + + /** + * Construct the vector with provided integer components. + * + * @param x X component + * @param y Y component + * @param z Z component + */ + public Vector(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Construct the vector with provided double components. + * + * @param x X component + * @param y Y component + * @param z Z component + */ + public Vector(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Construct the vector with provided float components. + * + * @param x X component + * @param y Y component + * @param z Z component + */ + public Vector(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Adds a vector to this one + * + * @param vec The other vector + * @return the same vector + */ + public Vector add(Vector vec) { + x += vec.x; + y += vec.y; + z += vec.z; + return this; + } + + /** + * Subtracts a vector from this one. + * + * @param vec The other vector + * @return the same vector + */ + public Vector subtract(Vector vec) { + x -= vec.x; + y -= vec.y; + z -= vec.z; + return this; + } + + /** + * Multiplies the vector by another. + * + * @param vec The other vector + * @return the same vector + */ + public Vector multiply(Vector vec) { + x *= vec.x; + y *= vec.y; + z *= vec.z; + return this; + } + + /** + * Divides the vector by another. + * + * @param vec The other vector + * @return the same vector + */ + public Vector divide(Vector vec) { + x /= vec.x; + y /= vec.y; + z /= vec.z; + return this; + } + + /** + * Copies another vector + * + * @param vec The other vector + * @return the same vector + */ + public Vector copy(Vector vec) { + x = vec.x; + y = vec.y; + z = vec.z; + return this; + } + + /** + * Gets the magnitude of the vector, 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 vector'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. + * + * @return the magnitude + */ + public double length() { + return Math.sqrt(NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z)); + } + + /** + * Gets the magnitude of the vector squared. + * + * @return the magnitude + */ + public double lengthSquared() { + return NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z); + } + + /** + * Get the distance between this vector 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 vector'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. + * + * @param o The other vector + * @return the distance + */ + public double distance(Vector o) { + return Math.sqrt(NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z)); + } + + /** + * Get the squared distance between this vector and another. + * + * @param o The other vector + * @return the distance + */ + public double distanceSquared(Vector o) { + return NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z); + } + + /** + * Gets the angle between this vector and another in radians. + * + * @param other The other vector + * @return angle in radians + */ + public float angle(Vector other) { + double dot = dot(other) / (length() * other.length()); + + return (float) Math.acos(dot); + } + + /** + * Sets this vector to the midpoint between this vector and another. + * + * @param other The other vector + * @return this same vector (now a midpoint) + */ + public Vector midpoint(Vector other) { + x = (x + other.x) / 2; + y = (y + other.y) / 2; + z = (z + other.z) / 2; + return this; + } + + /** + * Gets a new midpoint vector between this vector and another. + * + * @param other The other vector + * @return a new midpoint vector + */ + public Vector getMidpoint(Vector other) { + double x = (this.x + other.x) / 2; + double y = (this.y + other.y) / 2; + double z = (this.z + other.z) / 2; + return new Vector(x, y, z); + } + + /** + * Performs scalar multiplication, multiplying all components with a + * scalar. + * + * @param m The factor + * @return the same vector + */ + public Vector multiply(int m) { + x *= m; + y *= m; + z *= m; + return this; + } + + /** + * Performs scalar multiplication, multiplying all components with a + * scalar. + * + * @param m The factor + * @return the same vector + */ + public Vector multiply(double m) { + x *= m; + y *= m; + z *= m; + return this; + } + + /** + * Performs scalar multiplication, multiplying all components with a + * scalar. + * + * @param m The factor + * @return the same vector + */ + public Vector multiply(float m) { + x *= m; + y *= m; + z *= m; + return this; + } + + /** + * Calculates the dot product of this vector with another. The dot product + * is defined as x1*x2+y1*y2+z1*z2. The returned value is a scalar. + * + * @param other The other vector + * @return dot product + */ + public double dot(Vector other) { + return x * other.x + y * other.y + z * other.z; + } + + /** + * Calculates the cross product of this vector with another. The cross + * product is defined as: + *

              + *
            • x = y1 * z2 - y2 * z1 + *
            • y = z1 * x2 - z2 * x1 + *
            • z = x1 * y2 - x2 * y1 + *
            + * + * @param o The other vector + * @return the same vector + */ + public Vector crossProduct(Vector o) { + double newX = y * o.z - o.y * z; + double newY = z * o.x - o.z * x; + double newZ = x * o.y - o.x * y; + + x = newX; + y = newY; + z = newZ; + return this; + } + + /** + * Converts this vector to a unit vector (a vector with length of 1). + * + * @return the same vector + */ + public Vector normalize() { + double length = length(); + + x /= length; + y /= length; + z /= length; + + return this; + } + + /** + * Zero this vector's components. + * + * @return the same vector + */ + public Vector zero() { + x = 0; + y = 0; + z = 0; + return this; + } + + /** + * Returns whether this vector is in an axis-aligned bounding box. + *

            + * The minimum and maximum vectors given must be truly the minimum and + * maximum X, Y and Z components. + * + * @param min Minimum vector + * @param max Maximum vector + * @return whether this vector is in the AABB + */ + public boolean isInAABB(Vector min, Vector max) { + return x >= min.x && x <= max.x && y >= min.y && y <= max.y && z >= min.z && z <= max.z; + } + + /** + * Returns whether this vector is within a sphere. + * + * @param origin Sphere origin. + * @param radius Sphere radius + * @return whether this vector is in the sphere + */ + public boolean isInSphere(Vector origin, double radius) { + return (NumberConversions.square(origin.x - x) + NumberConversions.square(origin.y - y) + NumberConversions.square(origin.z - z)) <= NumberConversions.square(radius); + } + + /** + * Gets the X component. + * + * @return The X component. + */ + public double getX() { + return x; + } + + /** + * Gets the floored value of the X component, indicating the block that + * this vector is contained with. + * + * @return block X + */ + public int getBlockX() { + return NumberConversions.floor(x); + } + + /** + * Gets the Y component. + * + * @return The Y component. + */ + public double getY() { + return y; + } + + /** + * Gets the floored value of the Y component, indicating the block that + * this vector is contained with. + * + * @return block y + */ + public int getBlockY() { + return NumberConversions.floor(y); + } + + /** + * Gets the Z component. + * + * @return The Z component. + */ + public double getZ() { + return z; + } + + /** + * Gets the floored value of the Z component, indicating the block that + * this vector is contained with. + * + * @return block z + */ + public int getBlockZ() { + return NumberConversions.floor(z); + } + + /** + * Set the X component. + * + * @param x The new X component. + * @return This vector. + */ + public Vector setX(int x) { + this.x = x; + return this; + } + + /** + * Set the X component. + * + * @param x The new X component. + * @return This vector. + */ + public Vector setX(double x) { + this.x = x; + return this; + } + + /** + * Set the X component. + * + * @param x The new X component. + * @return This vector. + */ + public Vector setX(float x) { + this.x = x; + return this; + } + + /** + * Set the Y component. + * + * @param y The new Y component. + * @return This vector. + */ + public Vector setY(int y) { + this.y = y; + return this; + } + + /** + * Set the Y component. + * + * @param y The new Y component. + * @return This vector. + */ + public Vector setY(double y) { + this.y = y; + return this; + } + + /** + * Set the Y component. + * + * @param y The new Y component. + * @return This vector. + */ + public Vector setY(float y) { + this.y = y; + return this; + } + + /** + * Set the Z component. + * + * @param z The new Z component. + * @return This vector. + */ + public Vector setZ(int z) { + this.z = z; + return this; + } + + /** + * Set the Z component. + * + * @param z The new Z component. + * @return This vector. + */ + public Vector setZ(double z) { + this.z = z; + return this; + } + + /** + * Set the Z component. + * + * @param z The new Z component. + * @return This vector. + */ + public Vector setZ(float z) { + this.z = z; + return this; + } + + /** + * Checks to see if two objects are equal. + *

            + * Only two Vectors can ever return true. This method uses a fuzzy match + * to account for floating point errors. The epsilon can be retrieved + * with epsilon. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Vector)) { + return false; + } + + Vector other = (Vector) obj; + + return Math.abs(x - other.x) < epsilon && Math.abs(y - other.y) < epsilon && Math.abs(z - other.z) < epsilon && (this.getClass().equals(obj.getClass())); + } + + /** + * Returns a hash code for this vector + * + * @return hash code + */ + @Override + public int hashCode() { + int hash = 7; + + hash = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); + hash = 79 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); + hash = 79 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); + return hash; + } + + /** + * Get a new vector. + * + * @return vector + */ + @Override + public Vector clone() { + try { + return (Vector) super.clone(); + } catch (CloneNotSupportedException e) { + throw new Error(e); + } + } + + /** + * Returns this vector's components as x,y,z. + */ + @Override + public String toString() { + return x + "," + y + "," + z; + } + + /** + * Gets a Location version of this vector with yaw and pitch being 0. + * + * @param world The world to link the location to. + * @return the location + */ + public Location toLocation(World world) { + return new Location(world, x, y, z); + } + + /** + * Gets a Location version of this vector. + * + * @param world The world to link the location to. + * @param yaw The desired yaw. + * @param pitch The desired pitch. + * @return the location + */ + public Location toLocation(World world, float yaw, float pitch) { + return new Location(world, x, y, z, yaw, pitch); + } + + /** + * Get the block vector of this vector. + * + * @return A block vector. + */ + public BlockVector toBlockVector() { + return new BlockVector(x, y, z); + } + + /** + * Get the threshold used for equals(). + * + * @return The epsilon. + */ + public static double getEpsilon() { + return epsilon; + } + + /** + * Gets the minimum components of two vectors. + * + * @param v1 The first vector. + * @param v2 The second vector. + * @return minimum + */ + public static Vector getMinimum(Vector v1, Vector v2) { + return new Vector(Math.min(v1.x, v2.x), Math.min(v1.y, v2.y), Math.min(v1.z, v2.z)); + } + + /** + * Gets the maximum components of two vectors. + * + * @param v1 The first vector. + * @param v2 The second vector. + * @return maximum + */ + public static Vector getMaximum(Vector v1, Vector v2) { + return new Vector(Math.max(v1.x, v2.x), Math.max(v1.y, v2.y), Math.max(v1.z, v2.z)); + } + + /** + * Gets a random vector with components having a random value between 0 + * and 1. + * + * @return A random vector. + */ + public static Vector getRandom() { + return new Vector(random.nextDouble(), random.nextDouble(), random.nextDouble()); + } + + public Map serialize() { + Map result = new LinkedHashMap(); + + result.put("x", getX()); + result.put("y", getY()); + result.put("z", getZ()); + + return result; + } + + public static Vector deserialize(Map args) { + double x = 0; + double y = 0; + double z = 0; + + if (args.containsKey("x")) { + x = (Double) args.get("x"); + } + if (args.containsKey("y")) { + y = (Double) args.get("y"); + } + if (args.containsKey("z")) { + z = (Double) args.get("z"); + } + + return new Vector(x, y, z); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/io/BukkitObjectInputStream.java b/vspigot-api/src/main/java/org/bukkit/util/io/BukkitObjectInputStream.java new file mode 100644 index 0000000..d4b2825 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/io/BukkitObjectInputStream.java @@ -0,0 +1,63 @@ +package org.bukkit.util.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; + +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; + +/** + * This class is designed to be used in conjunction with the {@link + * ConfigurationSerializable} API. It translates objects back to their + * original implementation after being serialized by {@link + * BukkitObjectInputStream}. + *

            + * Behavior of implementations extending this class is not guaranteed across + * future versions. + */ +public class BukkitObjectInputStream extends ObjectInputStream { + + /** + * Constructor provided to mirror super functionality. + * + * @throws IOException + * @throws SecurityException + * @see ObjectInputStream#ObjectInputStream() + */ + protected BukkitObjectInputStream() throws IOException, SecurityException { + super(); + super.enableResolveObject(true); + } + + /** + * Object input stream decoration constructor. + * + * @param in + * @throws IOException + * @see ObjectInputStream#ObjectInputStream(InputStream) + */ + public BukkitObjectInputStream(InputStream in) throws IOException { + super(in); + super.enableResolveObject(true); + } + + @Override + protected Object resolveObject(Object obj) throws IOException { + if (obj instanceof Wrapper) { + try { + (obj = ConfigurationSerialization.deserializeObject(((Wrapper) obj).map)).getClass(); // NPE + } catch (Throwable ex) { + throw newIOException("Failed to deserialize object", ex); + } + } + + return super.resolveObject(obj); + } + + private static IOException newIOException(String string, Throwable cause) { + IOException exception = new IOException(string); + exception.initCause(cause); + return exception; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/io/BukkitObjectOutputStream.java b/vspigot-api/src/main/java/org/bukkit/util/io/BukkitObjectOutputStream.java new file mode 100644 index 0000000..c11e202 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/io/BukkitObjectOutputStream.java @@ -0,0 +1,53 @@ +package org.bukkit.util.io; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; + +import org.bukkit.configuration.serialization.ConfigurationSerializable; + +/** + * This class is designed to be used in conjunction with the {@link + * ConfigurationSerializable} API. It translates objects to an internal + * implementation for later deserialization using {@link + * BukkitObjectInputStream}. + *

            + * Behavior of implementations extending this class is not guaranteed across + * future versions. + */ +public class BukkitObjectOutputStream extends ObjectOutputStream { + + /** + * Constructor provided to mirror super functionality. + * + * @throws IOException + * @throws SecurityException + * @see ObjectOutputStream#ObjectOutputStream() + */ + protected BukkitObjectOutputStream() throws IOException, SecurityException { + super(); + super.enableReplaceObject(true); + } + + /** + * Object output stream decoration constructor. + * + * @param out + * @throws IOException + * @see ObjectOutputStream#ObjectOutputStream(OutputStream) + */ + public BukkitObjectOutputStream(OutputStream out) throws IOException { + super(out); + super.enableReplaceObject(true); + } + + @Override + protected Object replaceObject(Object obj) throws IOException { + if (!(obj instanceof Serializable) && (obj instanceof ConfigurationSerializable)) { + obj = Wrapper.newWrapper((ConfigurationSerializable) obj); + } + + return super.replaceObject(obj); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/io/Wrapper.java b/vspigot-api/src/main/java/org/bukkit/util/io/Wrapper.java new file mode 100644 index 0000000..e45605b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/io/Wrapper.java @@ -0,0 +1,23 @@ +package org.bukkit.util.io; + +import java.io.Serializable; +import java.util.Map; + +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; + +import com.google.common.collect.ImmutableMap; + +class Wrapper & Serializable> implements Serializable { + private static final long serialVersionUID = -986209235411767547L; + + final T map; + + static Wrapper> newWrapper(ConfigurationSerializable obj) { + return new Wrapper>(ImmutableMap.builder().put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, ConfigurationSerialization.getAlias(obj.getClass())).putAll(obj.serialize()).build()); + } + + private Wrapper(T map) { + this.map = map; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/noise/NoiseGenerator.java b/vspigot-api/src/main/java/org/bukkit/util/noise/NoiseGenerator.java new file mode 100644 index 0000000..72c92f3 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/noise/NoiseGenerator.java @@ -0,0 +1,176 @@ +package org.bukkit.util.noise; + +/** + * Base class for all noise generators + */ +public abstract class NoiseGenerator { + protected final int perm[] = new int[512]; + protected double offsetX; + protected double offsetY; + protected double offsetZ; + + /** + * Speedy floor, faster than (int)Math.floor(x) + * + * @param x Value to floor + * @return Floored value + */ + public static int floor(double x) { + return x >= 0 ? (int) x : (int) x - 1; + } + + protected static double fade(double x) { + return x * x * x * (x * (x * 6 - 15) + 10); + } + + protected static double lerp(double x, double y, double z) { + return y + x * (z - y); + } + + protected static double grad(int hash, double x, double y, double z) { + hash &= 15; + double u = hash < 8 ? x : y; + double v = hash < 4 ? y : hash == 12 || hash == 14 ? x : z; + return ((hash & 1) == 0 ? u : -u) + ((hash & 2) == 0 ? v : -v); + } + + /** + * Computes and returns the 1D noise for the given coordinate in 1D space + * + * @param x X coordinate + * @return Noise at given location, from range -1 to 1 + */ + public double noise(double x) { + return noise(x, 0, 0); + } + + /** + * Computes and returns the 2D noise for the given coordinates in 2D space + * + * @param x X coordinate + * @param y Y coordinate + * @return Noise at given location, from range -1 to 1 + */ + public double noise(double x, double y) { + return noise(x, y, 0); + } + + /** + * Computes and returns the 3D noise for the given coordinates in 3D space + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @return Noise at given location, from range -1 to 1 + */ + public abstract double noise(double x, double y, double z); + + /** + * Generates noise for the 1D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public double noise(double x, int octaves, double frequency, double amplitude) { + return noise(x, 0, 0, octaves, frequency, amplitude); + } + + /** + * Generates noise for the 1D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @param normalized If true, normalize the value to [-1, 1] + * @return Resulting noise + */ + public double noise(double x, int octaves, double frequency, double amplitude, boolean normalized) { + return noise(x, 0, 0, octaves, frequency, amplitude, normalized); + } + + /** + * Generates noise for the 2D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public double noise(double x, double y, int octaves, double frequency, double amplitude) { + return noise(x, y, 0, octaves, frequency, amplitude); + } + + /** + * Generates noise for the 2D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @param normalized If true, normalize the value to [-1, 1] + * @return Resulting noise + */ + public double noise(double x, double y, int octaves, double frequency, double amplitude, boolean normalized) { + return noise(x, y, 0, octaves, frequency, amplitude, normalized); + } + + /** + * Generates noise for the 3D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param z Z-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public double noise(double x, double y, double z, int octaves, double frequency, double amplitude) { + return noise(x, y, z, octaves, frequency, amplitude, false); + } + + /** + * Generates noise for the 3D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param z Z-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @param normalized If true, normalize the value to [-1, 1] + * @return Resulting noise + */ + public double noise(double x, double y, double z, int octaves, double frequency, double amplitude, boolean normalized) { + double result = 0; + double amp = 1; + double freq = 1; + double max = 0; + + for (int i = 0; i < octaves; i++) { + result += noise(x * freq, y * freq, z * freq) * amp; + max += amp; + freq *= frequency; + amp *= amplitude; + } + + if (normalized) { + result /= max; + } + + return result; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/noise/OctaveGenerator.java b/vspigot-api/src/main/java/org/bukkit/util/noise/OctaveGenerator.java new file mode 100644 index 0000000..a87304b --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/noise/OctaveGenerator.java @@ -0,0 +1,199 @@ +package org.bukkit.util.noise; + +/** + * Creates noise using unbiased octaves + */ +public abstract class OctaveGenerator { + protected final NoiseGenerator[] octaves; + protected double xScale = 1; + protected double yScale = 1; + protected double zScale = 1; + + protected OctaveGenerator(NoiseGenerator[] octaves) { + this.octaves = octaves; + } + + /** + * Sets the scale used for all coordinates passed to this generator. + *

            + * This is the equivalent to setting each coordinate to the specified + * value. + * + * @param scale New value to scale each coordinate by + */ + public void setScale(double scale) { + setXScale(scale); + setYScale(scale); + setZScale(scale); + } + + /** + * Gets the scale used for each X-coordinates passed + * + * @return X scale + */ + public double getXScale() { + return xScale; + } + + /** + * Sets the scale used for each X-coordinates passed + * + * @param scale New X scale + */ + public void setXScale(double scale) { + xScale = scale; + } + + /** + * Gets the scale used for each Y-coordinates passed + * + * @return Y scale + */ + public double getYScale() { + return yScale; + } + + /** + * Sets the scale used for each Y-coordinates passed + * + * @param scale New Y scale + */ + public void setYScale(double scale) { + yScale = scale; + } + + /** + * Gets the scale used for each Z-coordinates passed + * + * @return Z scale + */ + public double getZScale() { + return zScale; + } + + /** + * Sets the scale used for each Z-coordinates passed + * + * @param scale New Z scale + */ + public void setZScale(double scale) { + zScale = scale; + } + + /** + * Gets a clone of the individual octaves used within this generator + * + * @return Clone of the individual octaves + */ + public NoiseGenerator[] getOctaves() { + return octaves.clone(); + } + + /** + * Generates noise for the 1D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public double noise(double x, double frequency, double amplitude) { + return noise(x, 0, 0, frequency, amplitude); + } + + /** + * Generates noise for the 1D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @param normalized If true, normalize the value to [-1, 1] + * @return Resulting noise + */ + public double noise(double x, double frequency, double amplitude, boolean normalized) { + return noise(x, 0, 0, frequency, amplitude, normalized); + } + + /** + * Generates noise for the 2D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public double noise(double x, double y, double frequency, double amplitude) { + return noise(x, y, 0, frequency, amplitude); + } + + /** + * Generates noise for the 2D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @param normalized If true, normalize the value to [-1, 1] + * @return Resulting noise + */ + public double noise(double x, double y, double frequency, double amplitude, boolean normalized) { + return noise(x, y, 0, frequency, amplitude, normalized); + } + + /** + * Generates noise for the 3D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param z Z-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public double noise(double x, double y, double z, double frequency, double amplitude) { + return noise(x, y, z, frequency, amplitude, false); + } + + /** + * Generates noise for the 3D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param z Z-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @param normalized If true, normalize the value to [-1, 1] + * @return Resulting noise + */ + public double noise(double x, double y, double z, double frequency, double amplitude, boolean normalized) { + double result = 0; + double amp = 1; + double freq = 1; + double max = 0; + + x *= xScale; + y *= yScale; + z *= zScale; + + for (NoiseGenerator octave : octaves) { + result += octave.noise(x * freq, y * freq, z * freq) * amp; + max += amp; + freq *= frequency; + amp *= amplitude; + } + + if (normalized) { + result /= max; + } + + return result; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/noise/PerlinNoiseGenerator.java b/vspigot-api/src/main/java/org/bukkit/util/noise/PerlinNoiseGenerator.java new file mode 100644 index 0000000..5e034c1 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/noise/PerlinNoiseGenerator.java @@ -0,0 +1,217 @@ +package org.bukkit.util.noise; + +import java.util.Random; +import org.bukkit.World; + +/** + * Generates noise using the "classic" perlin generator + * + * @see SimplexNoiseGenerator "Improved" and faster version with slighly + * different results + */ +public class PerlinNoiseGenerator extends NoiseGenerator { + protected static final int grad3[][] = {{1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0}, + {1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1}, + {0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1}}; + private static final PerlinNoiseGenerator instance = new PerlinNoiseGenerator(); + + protected PerlinNoiseGenerator() { + int p[] = {151, 160, 137, 91, 90, 15, 131, 13, 201, + 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, + 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, + 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, + 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, + 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, + 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, + 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, + 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, + 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, + 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, + 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, + 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, + 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, + 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, + 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, + 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, + 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180}; + + for (int i = 0; i < 512; i++) { + perm[i] = p[i & 255]; + } + } + + /** + * Creates a seeded perlin noise generator for the given world + * + * @param world World to construct this generator for + */ + public PerlinNoiseGenerator(World world) { + this(new Random(world.getSeed())); + } + + /** + * Creates a seeded perlin noise generator for the given seed + * + * @param seed Seed to construct this generator for + */ + public PerlinNoiseGenerator(long seed) { + this(new Random(seed)); + } + + /** + * Creates a seeded perlin noise generator with the given Random + * + * @param rand Random to construct with + */ + public PerlinNoiseGenerator(Random rand) { + offsetX = rand.nextDouble() * 256; + offsetY = rand.nextDouble() * 256; + offsetZ = rand.nextDouble() * 256; + + for (int i = 0; i < 256; i++) { + perm[i] = rand.nextInt(256); + } + + for (int i = 0; i < 256; i++) { + int pos = rand.nextInt(256 - i) + i; + int old = perm[i]; + + perm[i] = perm[pos]; + perm[pos] = old; + perm[i + 256] = perm[i]; + } + } + + /** + * Computes and returns the 1D unseeded perlin noise for the given + * coordinates in 1D space + * + * @param x X coordinate + * @return Noise at given location, from range -1 to 1 + */ + public static double getNoise(double x) { + return instance.noise(x); + } + + /** + * Computes and returns the 2D unseeded perlin noise for the given + * coordinates in 2D space + * + * @param x X coordinate + * @param y Y coordinate + * @return Noise at given location, from range -1 to 1 + */ + public static double getNoise(double x, double y) { + return instance.noise(x, y); + } + + /** + * Computes and returns the 3D unseeded perlin noise for the given + * coordinates in 3D space + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @return Noise at given location, from range -1 to 1 + */ + public static double getNoise(double x, double y, double z) { + return instance.noise(x, y, z); + } + + /** + * Gets the singleton unseeded instance of this generator + * + * @return Singleton + */ + public static PerlinNoiseGenerator getInstance() { + return instance; + } + + @Override + public double noise(double x, double y, double z) { + x += offsetX; + y += offsetY; + z += offsetZ; + + int floorX = floor(x); + int floorY = floor(y); + int floorZ = floor(z); + + // Find unit cube containing the point + int X = floorX & 255; + int Y = floorY & 255; + int Z = floorZ & 255; + + // Get relative xyz coordinates of the point within the cube + x -= floorX; + y -= floorY; + z -= floorZ; + + // Compute fade curves for xyz + double fX = fade(x); + double fY = fade(y); + double fZ = fade(z); + + // Hash coordinates of the cube corners + int A = perm[X] + Y; + int AA = perm[A] + Z; + int AB = perm[A + 1] + Z; + int B = perm[X + 1] + Y; + int BA = perm[B] + Z; + int BB = perm[B + 1] + Z; + + return lerp(fZ, lerp(fY, lerp(fX, grad(perm[AA], x, y, z), + grad(perm[BA], x - 1, y, z)), + lerp(fX, grad(perm[AB], x, y - 1, z), + grad(perm[BB], x - 1, y - 1, z))), + lerp(fY, lerp(fX, grad(perm[AA + 1], x, y, z - 1), + grad(perm[BA + 1], x - 1, y, z - 1)), + lerp(fX, grad(perm[AB + 1], x, y - 1, z - 1), + grad(perm[BB + 1], x - 1, y - 1, z - 1)))); + } + + /** + * Generates noise for the 1D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public static double getNoise(double x, int octaves, double frequency, double amplitude) { + return instance.noise(x, octaves, frequency, amplitude); + } + + /** + * Generates noise for the 2D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public static double getNoise(double x, double y, int octaves, double frequency, double amplitude) { + return instance.noise(x, y, octaves, frequency, amplitude); + } + + /** + * Generates noise for the 3D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param z Z-coordinate + * @param octaves Number of octaves to use + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public static double getNoise(double x, double y, double z, int octaves, double frequency, double amplitude) { + return instance.noise(x, y, z, octaves, frequency, amplitude); + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/noise/PerlinOctaveGenerator.java b/vspigot-api/src/main/java/org/bukkit/util/noise/PerlinOctaveGenerator.java new file mode 100644 index 0000000..55b7ad7 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/noise/PerlinOctaveGenerator.java @@ -0,0 +1,50 @@ +package org.bukkit.util.noise; + +import java.util.Random; +import org.bukkit.World; + +/** + * Creates perlin noise through unbiased octaves + */ +public class PerlinOctaveGenerator extends OctaveGenerator { + + /** + * Creates a perlin octave generator for the given world + * + * @param world World to construct this generator for + * @param octaves Amount of octaves to create + */ + public PerlinOctaveGenerator(World world, int octaves) { + this(new Random(world.getSeed()), octaves); + } + + /** + * Creates a perlin octave generator for the given world + * + * @param seed Seed to construct this generator for + * @param octaves Amount of octaves to create + */ + public PerlinOctaveGenerator(long seed, int octaves) { + this(new Random(seed), octaves); + } + + /** + * Creates a perlin octave generator for the given {@link Random} + * + * @param rand Random object to construct this generator for + * @param octaves Amount of octaves to create + */ + public PerlinOctaveGenerator(Random rand, int octaves) { + super(createOctaves(rand, octaves)); + } + + private static NoiseGenerator[] createOctaves(Random rand, int octaves) { + NoiseGenerator[] result = new NoiseGenerator[octaves]; + + for (int i = 0; i < octaves; i++) { + result[i] = new PerlinNoiseGenerator(rand); + } + + return result; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/noise/SimplexNoiseGenerator.java b/vspigot-api/src/main/java/org/bukkit/util/noise/SimplexNoiseGenerator.java new file mode 100644 index 0000000..b052f3c --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/noise/SimplexNoiseGenerator.java @@ -0,0 +1,520 @@ +package org.bukkit.util.noise; + +import java.util.Random; +import org.bukkit.World; + +/** + * Generates simplex-based noise. + *

            + * This is a modified version of the freely published version in the paper by + * Stefan Gustavson at + * + * http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf + */ +public class SimplexNoiseGenerator extends PerlinNoiseGenerator { + protected static final double SQRT_3 = Math.sqrt(3); + protected static final double SQRT_5 = Math.sqrt(5); + protected static final double F2 = 0.5 * (SQRT_3 - 1); + protected static final double G2 = (3 - SQRT_3) / 6; + protected static final double G22 = G2 * 2.0 - 1; + protected static final double F3 = 1.0 / 3.0; + protected static final double G3 = 1.0 / 6.0; + protected static final double F4 = (SQRT_5 - 1.0) / 4.0; + protected static final double G4 = (5.0 - SQRT_5) / 20.0; + protected static final double G42 = G4 * 2.0; + protected static final double G43 = G4 * 3.0; + protected static final double G44 = G4 * 4.0 - 1.0; + protected static final int grad4[][] = {{0, 1, 1, 1}, {0, 1, 1, -1}, {0, 1, -1, 1}, {0, 1, -1, -1}, + {0, -1, 1, 1}, {0, -1, 1, -1}, {0, -1, -1, 1}, {0, -1, -1, -1}, + {1, 0, 1, 1}, {1, 0, 1, -1}, {1, 0, -1, 1}, {1, 0, -1, -1}, + {-1, 0, 1, 1}, {-1, 0, 1, -1}, {-1, 0, -1, 1}, {-1, 0, -1, -1}, + {1, 1, 0, 1}, {1, 1, 0, -1}, {1, -1, 0, 1}, {1, -1, 0, -1}, + {-1, 1, 0, 1}, {-1, 1, 0, -1}, {-1, -1, 0, 1}, {-1, -1, 0, -1}, + {1, 1, 1, 0}, {1, 1, -1, 0}, {1, -1, 1, 0}, {1, -1, -1, 0}, + {-1, 1, 1, 0}, {-1, 1, -1, 0}, {-1, -1, 1, 0}, {-1, -1, -1, 0}}; + protected static final int simplex[][] = { + {0, 1, 2, 3}, {0, 1, 3, 2}, {0, 0, 0, 0}, {0, 2, 3, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 2, 3, 0}, + {0, 2, 1, 3}, {0, 0, 0, 0}, {0, 3, 1, 2}, {0, 3, 2, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 3, 2, 0}, + {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, + {1, 2, 0, 3}, {0, 0, 0, 0}, {1, 3, 0, 2}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {2, 3, 0, 1}, {2, 3, 1, 0}, + {1, 0, 2, 3}, {1, 0, 3, 2}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {2, 0, 3, 1}, {0, 0, 0, 0}, {2, 1, 3, 0}, + {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, + {2, 0, 1, 3}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {3, 0, 1, 2}, {3, 0, 2, 1}, {0, 0, 0, 0}, {3, 1, 2, 0}, + {2, 1, 0, 3}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {3, 1, 0, 2}, {0, 0, 0, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}}; + protected static double offsetW; + private static final SimplexNoiseGenerator instance = new SimplexNoiseGenerator(); + + protected SimplexNoiseGenerator() { + super(); + } + + /** + * Creates a seeded simplex noise generator for the given world + * + * @param world World to construct this generator for + */ + public SimplexNoiseGenerator(World world) { + this(new Random(world.getSeed())); + } + + /** + * Creates a seeded simplex noise generator for the given seed + * + * @param seed Seed to construct this generator for + */ + public SimplexNoiseGenerator(long seed) { + this(new Random(seed)); + } + + /** + * Creates a seeded simplex noise generator with the given Random + * + * @param rand Random to construct with + */ + public SimplexNoiseGenerator(Random rand) { + super(rand); + offsetW = rand.nextDouble() * 256; + } + + protected static double dot(int g[], double x, double y) { + return g[0] * x + g[1] * y; + } + + protected static double dot(int g[], double x, double y, double z) { + return g[0] * x + g[1] * y + g[2] * z; + } + + protected static double dot(int g[], double x, double y, double z, double w) { + return g[0] * x + g[1] * y + g[2] * z + g[3] * w; + } + + /** + * Computes and returns the 1D unseeded simplex noise for the given + * coordinates in 1D space + * + * @param xin X coordinate + * @return Noise at given location, from range -1 to 1 + */ + public static double getNoise(double xin) { + return instance.noise(xin); + } + + /** + * Computes and returns the 2D unseeded simplex noise for the given + * coordinates in 2D space + * + * @param xin X coordinate + * @param yin Y coordinate + * @return Noise at given location, from range -1 to 1 + */ + public static double getNoise(double xin, double yin) { + return instance.noise(xin, yin); + } + + /** + * Computes and returns the 3D unseeded simplex noise for the given + * coordinates in 3D space + * + * @param xin X coordinate + * @param yin Y coordinate + * @param zin Z coordinate + * @return Noise at given location, from range -1 to 1 + */ + public static double getNoise(double xin, double yin, double zin) { + return instance.noise(xin, yin, zin); + } + + /** + * Computes and returns the 4D simplex noise for the given coordinates in + * 4D space + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @param w W coordinate + * @return Noise at given location, from range -1 to 1 + */ + public static double getNoise(double x, double y, double z, double w) { + return instance.noise(x, y, z, w); + } + + @Override + public double noise(double xin, double yin, double zin) { + xin += offsetX; + yin += offsetY; + zin += offsetZ; + + double n0, n1, n2, n3; // Noise contributions from the four corners + + // Skew the input space to determine which simplex cell we're in + double s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D + int i = floor(xin + s); + int j = floor(yin + s); + int k = floor(zin + s); + double t = (i + j + k) * G3; + double X0 = i - t; // Unskew the cell origin back to (x,y,z) space + double Y0 = j - t; + double Z0 = k - t; + double x0 = xin - X0; // The x,y,z distances from the cell origin + double y0 = yin - Y0; + double z0 = zin - Z0; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if (x0 >= y0) { + if (y0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } // X Y Z order + else if (x0 >= z0) { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } // X Z Y order + else { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } // Z X Y order + } else { // x0 y0) { + i1 = 1; + j1 = 0; + } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else { + i1 = 0; + j1 = 1; + } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + + double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + double y1 = y0 - j1 + G2; + double x2 = x0 + G22; // Offsets for last corner in (x,y) unskewed coords + double y2 = y0 + G22; + + // Work out the hashed gradient indices of the three simplex corners + int ii = i & 255; + int jj = j & 255; + int gi0 = perm[ii + perm[jj]] % 12; + int gi1 = perm[ii + i1 + perm[jj + j1]] % 12; + int gi2 = perm[ii + 1 + perm[jj + 1]] % 12; + + // Calculate the contribution from the three corners + double t0 = 0.5 - x0 * x0 - y0 * y0; + if (t0 < 0) { + n0 = 0.0; + } else { + t0 *= t0; + n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + + double t1 = 0.5 - x1 * x1 - y1 * y1; + if (t1 < 0) { + n1 = 0.0; + } else { + t1 *= t1; + n1 = t1 * t1 * dot(grad3[gi1], x1, y1); + } + + double t2 = 0.5 - x2 * x2 - y2 * y2; + if (t2 < 0) { + n2 = 0.0; + } else { + t2 *= t2; + n2 = t2 * t2 * dot(grad3[gi2], x2, y2); + } + + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); + } + + /** + * Computes and returns the 4D simplex noise for the given coordinates in + * 4D space + * + * @param x X coordinate + * @param y Y coordinate + * @param z Z coordinate + * @param w W coordinate + * @return Noise at given location, from range -1 to 1 + */ + public double noise(double x, double y, double z, double w) { + x += offsetX; + y += offsetY; + z += offsetZ; + w += offsetW; + + double n0, n1, n2, n3, n4; // Noise contributions from the five corners + + // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in + double s = (x + y + z + w) * F4; // Factor for 4D skewing + int i = floor(x + s); + int j = floor(y + s); + int k = floor(z + s); + int l = floor(w + s); + + double t = (i + j + k + l) * G4; // Factor for 4D unskewing + double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space + double Y0 = j - t; + double Z0 = k - t; + double W0 = l - t; + double x0 = x - X0; // The x,y,z,w distances from the cell origin + double y0 = y - Y0; + double z0 = z - Z0; + double w0 = w - W0; + + // For the 4D case, the simplex is a 4D shape I won't even try to describe. + // To find out which of the 24 possible simplices we're in, we need to + // determine the magnitude ordering of x0, y0, z0 and w0. + // The method below is a good way of finding the ordering of x,y,z,w and + // then find the correct traversal order for the simplex we’re in. + // First, six pair-wise comparisons are performed between each possible pair + // of the four coordinates, and the results are used to add up binary bits + // for an integer index. + int c1 = (x0 > y0) ? 32 : 0; + int c2 = (x0 > z0) ? 16 : 0; + int c3 = (y0 > z0) ? 8 : 0; + int c4 = (x0 > w0) ? 4 : 0; + int c5 = (y0 > w0) ? 2 : 0; + int c6 = (z0 > w0) ? 1 : 0; + int c = c1 + c2 + c3 + c4 + c5 + c6; + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; + j1 = simplex[c][1] >= 3 ? 1 : 0; + k1 = simplex[c][2] >= 3 ? 1 : 0; + l1 = simplex[c][3] >= 3 ? 1 : 0; + + // The number 2 in the "simplex" array is at the second largest coordinate. + i2 = simplex[c][0] >= 2 ? 1 : 0; + j2 = simplex[c][1] >= 2 ? 1 : 0; + k2 = simplex[c][2] >= 2 ? 1 : 0; + l2 = simplex[c][3] >= 2 ? 1 : 0; + + // The number 1 in the "simplex" array is at the second smallest coordinate. + i3 = simplex[c][0] >= 1 ? 1 : 0; + j3 = simplex[c][1] >= 1 ? 1 : 0; + k3 = simplex[c][2] >= 1 ? 1 : 0; + l3 = simplex[c][3] >= 1 ? 1 : 0; + + // The fifth corner has all coordinate offsets = 1, so no need to look that up. + + double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + double y1 = y0 - j1 + G4; + double z1 = z0 - k1 + G4; + double w1 = w0 - l1 + G4; + + double x2 = x0 - i2 + G42; // Offsets for third corner in (x,y,z,w) coords + double y2 = y0 - j2 + G42; + double z2 = z0 - k2 + G42; + double w2 = w0 - l2 + G42; + + double x3 = x0 - i3 + G43; // Offsets for fourth corner in (x,y,z,w) coords + double y3 = y0 - j3 + G43; + double z3 = z0 - k3 + G43; + double w3 = w0 - l3 + G43; + + double x4 = x0 + G44; // Offsets for last corner in (x,y,z,w) coords + double y4 = y0 + G44; + double z4 = z0 + G44; + double w4 = w0 + G44; + + // Work out the hashed gradient indices of the five simplex corners + int ii = i & 255; + int jj = j & 255; + int kk = k & 255; + int ll = l & 255; + + int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32; + int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32; + int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32; + int gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32; + int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32; + + // Calculate the contribution from the five corners + double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; + if (t0 < 0) { + n0 = 0.0; + } else { + t0 *= t0; + n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); + } + + double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; + if (t1 < 0) { + n1 = 0.0; + } else { + t1 *= t1; + n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); + } + + double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; + if (t2 < 0) { + n2 = 0.0; + } else { + t2 *= t2; + n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); + } + + double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; + if (t3 < 0) { + n3 = 0.0; + } else { + t3 *= t3; + n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); + } + + double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; + if (t4 < 0) { + n4 = 0.0; + } else { + t4 *= t4; + n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); + } + + // Sum up and scale the result to cover the range [-1,1] + return 27.0 * (n0 + n1 + n2 + n3 + n4); + } + + /** + * Gets the singleton unseeded instance of this generator + * + * @return Singleton + */ + public static SimplexNoiseGenerator getInstance() { + return instance; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/noise/SimplexOctaveGenerator.java b/vspigot-api/src/main/java/org/bukkit/util/noise/SimplexOctaveGenerator.java new file mode 100644 index 0000000..61e66aa --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/noise/SimplexOctaveGenerator.java @@ -0,0 +1,129 @@ +package org.bukkit.util.noise; + +import java.util.Random; +import org.bukkit.World; + +/** + * Creates simplex noise through unbiased octaves + */ +public class SimplexOctaveGenerator extends OctaveGenerator { + private double wScale = 1; + + /** + * Creates a simplex octave generator for the given world + * + * @param world World to construct this generator for + * @param octaves Amount of octaves to create + */ + public SimplexOctaveGenerator(World world, int octaves) { + this(new Random(world.getSeed()), octaves); + } + + /** + * Creates a simplex octave generator for the given world + * + * @param seed Seed to construct this generator for + * @param octaves Amount of octaves to create + */ + public SimplexOctaveGenerator(long seed, int octaves) { + this(new Random(seed), octaves); + } + + /** + * Creates a simplex octave generator for the given {@link Random} + * + * @param rand Random object to construct this generator for + * @param octaves Amount of octaves to create + */ + public SimplexOctaveGenerator(Random rand, int octaves) { + super(createOctaves(rand, octaves)); + } + + @Override + public void setScale(double scale) { + super.setScale(scale); + setWScale(scale); + } + + /** + * Gets the scale used for each W-coordinates passed + * + * @return W scale + */ + public double getWScale() { + return wScale; + } + + /** + * Sets the scale used for each W-coordinates passed + * + * @param scale New W scale + */ + public void setWScale(double scale) { + wScale = scale; + } + + /** + * Generates noise for the 3D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param z Z-coordinate + * @param w W-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @return Resulting noise + */ + public double noise(double x, double y, double z, double w, double frequency, double amplitude) { + return noise(x, y, z, w, frequency, amplitude, false); + } + + /** + * Generates noise for the 3D coordinates using the specified number of + * octaves and parameters + * + * @param x X-coordinate + * @param y Y-coordinate + * @param z Z-coordinate + * @param w W-coordinate + * @param frequency How much to alter the frequency by each octave + * @param amplitude How much to alter the amplitude by each octave + * @param normalized If true, normalize the value to [-1, 1] + * @return Resulting noise + */ + public double noise(double x, double y, double z, double w, double frequency, double amplitude, boolean normalized) { + double result = 0; + double amp = 1; + double freq = 1; + double max = 0; + + x *= xScale; + y *= yScale; + z *= zScale; + w *= wScale; + + for (NoiseGenerator octave : octaves) { + result += ((SimplexNoiseGenerator) octave).noise(x * freq, y * freq, z * freq, w * freq) * amp; + max += amp; + freq *= frequency; + amp *= amplitude; + } + + if (normalized) { + result /= max; + } + + return result; + } + + private static NoiseGenerator[] createOctaves(Random rand, int octaves) { + NoiseGenerator[] result = new NoiseGenerator[octaves]; + + for (int i = 0; i < octaves; i++) { + result[i] = new SimplexNoiseGenerator(rand); + } + + return result; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/permissions/BroadcastPermissions.java b/vspigot-api/src/main/java/org/bukkit/util/permissions/BroadcastPermissions.java new file mode 100644 index 0000000..092370e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/permissions/BroadcastPermissions.java @@ -0,0 +1,22 @@ +package org.bukkit.util.permissions; + +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; + +public final class BroadcastPermissions { + private static final String ROOT = "bukkit.broadcast"; + private static final String PREFIX = ROOT + "."; + + private BroadcastPermissions() {} + + public static Permission registerPermissions(Permission parent) { + Permission broadcasts = DefaultPermissions.registerPermission(ROOT, "Allows the user to receive all broadcast messages", parent); + + DefaultPermissions.registerPermission(PREFIX + "admin", "Allows the user to receive administrative broadcasts", PermissionDefault.OP, broadcasts); + DefaultPermissions.registerPermission(PREFIX + "user", "Allows the user to receive user broadcasts", PermissionDefault.TRUE, broadcasts); + + broadcasts.recalculatePermissibles(); + + return broadcasts; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/permissions/CommandPermissions.java b/vspigot-api/src/main/java/org/bukkit/util/permissions/CommandPermissions.java new file mode 100644 index 0000000..4638c91 --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/permissions/CommandPermissions.java @@ -0,0 +1,117 @@ +package org.bukkit.util.permissions; + +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; + +public final class CommandPermissions { + private static final String ROOT = "bukkit.command"; + private static final String PREFIX = ROOT + "."; + + private CommandPermissions() {} + + private static Permission registerWhitelist(Permission parent) { + Permission whitelist = DefaultPermissions.registerPermission(PREFIX + "whitelist", "Allows the user to modify the server whitelist", PermissionDefault.OP, parent); + + DefaultPermissions.registerPermission(PREFIX + "whitelist.add", "Allows the user to add a player to the server whitelist", whitelist); + DefaultPermissions.registerPermission(PREFIX + "whitelist.remove", "Allows the user to remove a player from the server whitelist", whitelist); + DefaultPermissions.registerPermission(PREFIX + "whitelist.reload", "Allows the user to reload the server whitelist", whitelist); + DefaultPermissions.registerPermission(PREFIX + "whitelist.enable", "Allows the user to enable the server whitelist", whitelist); + DefaultPermissions.registerPermission(PREFIX + "whitelist.disable", "Allows the user to disable the server whitelist", whitelist); + DefaultPermissions.registerPermission(PREFIX + "whitelist.list", "Allows the user to list all the users on the server whitelist", whitelist); + + whitelist.recalculatePermissibles(); + + return whitelist; + } + + private static Permission registerBan(Permission parent) { + Permission ban = DefaultPermissions.registerPermission(PREFIX + "ban", "Allows the user to ban people", PermissionDefault.OP, parent); + + DefaultPermissions.registerPermission(PREFIX + "ban.player", "Allows the user to ban players", ban); + DefaultPermissions.registerPermission(PREFIX + "ban.ip", "Allows the user to ban IP addresses", ban); + + ban.recalculatePermissibles(); + + return ban; + } + + private static Permission registerUnban(Permission parent) { + Permission unban = DefaultPermissions.registerPermission(PREFIX + "unban", "Allows the user to unban people", PermissionDefault.OP, parent); + + DefaultPermissions.registerPermission(PREFIX + "unban.player", "Allows the user to unban players", unban); + DefaultPermissions.registerPermission(PREFIX + "unban.ip", "Allows the user to unban IP addresses", unban); + + unban.recalculatePermissibles(); + + return unban; + } + + private static Permission registerOp(Permission parent) { + Permission op = DefaultPermissions.registerPermission(PREFIX + "op", "Allows the user to change operators", PermissionDefault.OP, parent); + + DefaultPermissions.registerPermission(PREFIX + "op.give", "Allows the user to give a player operator status", op); + DefaultPermissions.registerPermission(PREFIX + "op.take", "Allows the user to take a players operator status", op); + + op.recalculatePermissibles(); + + return op; + } + + private static Permission registerSave(Permission parent) { + Permission save = DefaultPermissions.registerPermission(PREFIX + "save", "Allows the user to save the worlds", PermissionDefault.OP, parent); + + DefaultPermissions.registerPermission(PREFIX + "save.enable", "Allows the user to enable automatic saving", save); + DefaultPermissions.registerPermission(PREFIX + "save.disable", "Allows the user to disable automatic saving", save); + DefaultPermissions.registerPermission(PREFIX + "save.perform", "Allows the user to perform a manual save", save); + + save.recalculatePermissibles(); + + return save; + } + + private static Permission registerTime(Permission parent) { + Permission time = DefaultPermissions.registerPermission(PREFIX + "time", "Allows the user to alter the time", PermissionDefault.OP, parent); + + DefaultPermissions.registerPermission(PREFIX + "time.add", "Allows the user to fast-forward time", time); + DefaultPermissions.registerPermission(PREFIX + "time.set", "Allows the user to change the time", time); + + time.recalculatePermissibles(); + + return time; + } + + public static Permission registerPermissions(Permission parent) { + Permission commands = DefaultPermissions.registerPermission(ROOT, "Gives the user the ability to use all CraftBukkit commands", parent); + + registerWhitelist(commands); + registerBan(commands); + registerUnban(commands); + registerOp(commands); + registerSave(commands); + registerTime(commands); + + DefaultPermissions.registerPermission(PREFIX + "kill", "Allows the user to commit suicide", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "me", "Allows the user to perform a chat action", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "tell", "Allows the user to privately message another player", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "say", "Allows the user to talk as the console", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "give", "Allows the user to give items to players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "teleport", "Allows the user to teleport players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "stop", "Allows the user to stop the server", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "list", "Allows the user to list all online players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "help", "Allows the user to view the vanilla help menu", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "plugins", "Allows the user to view the list of plugins running on this server", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "reload", "Allows the user to reload the server settings", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "version", "Allows the user to view the version of the server", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "gamemode", "Allows the user to change the gamemode of another player", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "xp", "Allows the user to give themselves or others arbitrary values of experience", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "toggledownfall", "Allows the user to toggle rain on/off for a given world", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "defaultgamemode", "Allows the user to change the default gamemode of the server", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "seed", "Allows the user to view the seed of the world", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "effect", "Allows the user to add/remove effects on players", PermissionDefault.OP, commands); + + commands.recalculatePermissibles(); + + return commands; + } +} diff --git a/vspigot-api/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java b/vspigot-api/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java new file mode 100644 index 0000000..8c0df8e --- /dev/null +++ b/vspigot-api/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java @@ -0,0 +1,82 @@ +package org.bukkit.util.permissions; + +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; + +public final class DefaultPermissions { + private static final String ROOT = "craftbukkit"; + private static final String LEGACY_PREFIX = "craft"; + + private DefaultPermissions() {} + + public static Permission registerPermission(Permission perm) { + return registerPermission(perm, true); + } + + public static Permission registerPermission(Permission perm, boolean withLegacy) { + Permission result = perm; + + try { + Bukkit.getPluginManager().addPermission(perm); + } catch (IllegalArgumentException ex) { + result = Bukkit.getPluginManager().getPermission(perm.getName()); + } + + if (withLegacy) { + Permission legacy = new Permission(LEGACY_PREFIX + result.getName(), result.getDescription(), PermissionDefault.FALSE); + legacy.getChildren().put(result.getName(), true); + registerPermission(perm, false); + } + + return result; + } + + public static Permission registerPermission(Permission perm, Permission parent) { + parent.getChildren().put(perm.getName(), true); + return registerPermission(perm); + } + + public static Permission registerPermission(String name, String desc) { + Permission perm = registerPermission(new Permission(name, desc)); + return perm; + } + + public static Permission registerPermission(String name, String desc, Permission parent) { + Permission perm = registerPermission(name, desc); + parent.getChildren().put(perm.getName(), true); + return perm; + } + + public static Permission registerPermission(String name, String desc, PermissionDefault def) { + Permission perm = registerPermission(new Permission(name, desc, def)); + return perm; + } + + public static Permission registerPermission(String name, String desc, PermissionDefault def, Permission parent) { + Permission perm = registerPermission(name, desc, def); + parent.getChildren().put(perm.getName(), true); + return perm; + } + + public static Permission registerPermission(String name, String desc, PermissionDefault def, Map children) { + Permission perm = registerPermission(new Permission(name, desc, def, children)); + return perm; + } + + public static Permission registerPermission(String name, String desc, PermissionDefault def, Map children, Permission parent) { + Permission perm = registerPermission(name, desc, def, children); + parent.getChildren().put(perm.getName(), true); + return perm; + } + + public static void registerCorePermissions() { + Permission parent = registerPermission(ROOT, "Gives the user the ability to use all CraftBukkit utilities and commands"); + + CommandPermissions.registerPermissions(parent); + BroadcastPermissions.registerPermissions(parent); + + parent.recalculatePermissibles(); + } +} diff --git a/vspigot-api/src/main/java/org/spigotmc/CustomTimingsHandler.java b/vspigot-api/src/main/java/org/spigotmc/CustomTimingsHandler.java new file mode 100644 index 0000000..7ccd45f --- /dev/null +++ b/vspigot-api/src/main/java/org/spigotmc/CustomTimingsHandler.java @@ -0,0 +1,212 @@ +package org.spigotmc; + +import org.bukkit.command.defaults.TimingsCommand; +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; +import org.bukkit.plugin.TimedRegisteredListener; +import java.io.PrintStream; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.bukkit.Bukkit; +import org.bukkit.World; + +import java.util.ArrayDeque; // Poweruser +/** + * Provides custom timing sections for /timings merged. + */ +public class CustomTimingsHandler +{ + + protected static Queue HANDLERS = new ConcurrentLinkedQueue(); // Poweruser - protected + /*========================================================================*/ + private final String name; + private final CustomTimingsHandler parent; + private long count = 0; + private long start = 0; + private long timingDepth = 0; + private long totalTime = 0; + private long curTickTotal = 0; + private long violations = 0; + // Poweruser start + private ArrayDeque currentTimings; + private long currentTimingsSum; + // Poweruser end + + public CustomTimingsHandler(String name) + { + this( name, null ); + } + + public CustomTimingsHandler(String name, CustomTimingsHandler parent) + { + this.name = name; + this.parent = parent; + // Poweruser start + this.currentTimings = new ArrayDeque(); + this.currentTimingsSum = 0L; + // Poweruser end + HANDLERS.add( this ); + } + + /** + * Prints the timings and extra data to the given stream. + * + * @param printStream + */ + public static void printTimings(PrintStream printStream) + { + printStream.println( "Minecraft" ); + for ( CustomTimingsHandler timings : HANDLERS ) + { + long time = timings.totalTime; + long count = timings.count; + if ( count == 0 ) + { + continue; + } + long avg = time / count; + + printStream.println( " " + timings.name + " Time: " + time + " Count: " + count + " Avg: " + avg + " Violations: " + timings.violations ); + } + printStream.println( "# Version " + Bukkit.getVersion() ); + int entities = 0; + int livingEntities = 0; + for ( World world : Bukkit.getWorlds() ) + { + entities += world.getEntities().size(); + livingEntities += world.getLivingEntities().size(); + } + printStream.println( "# Entities " + entities ); + printStream.println( "# LivingEntities " + livingEntities ); + } + + /** + * Resets all timings. + */ + public static void reload() + { + if ( Bukkit.getPluginManager().useTimings() ) + { + for ( CustomTimingsHandler timings : HANDLERS ) + { + timings.reset(); + } + } + TimingsCommand.timingStart = System.nanoTime(); + } + + /** + * Ticked every tick by CraftBukkit to count the number of times a timer + * caused TPS loss. + */ + public static void tick() + { + if ( Bukkit.getPluginManager().useTimings() ) + { + for ( CustomTimingsHandler timings : HANDLERS ) + { + timings.updateAverageCalculation(); // Poweruser + if ( timings.curTickTotal > 50000000L ) // Poweruser - add L, mark number as long + { + timings.violations += Math.ceil( timings.curTickTotal / 50000000L ); // Poweruser - add L, mark number as long + } + timings.curTickTotal = 0; + timings.timingDepth = 0; // incase reset messes this up + } + } + } + + /** + * Starts timing to track a section of code. + */ + public void startTiming() + { + // If second condtion fails we are already timing + if ( Bukkit.getPluginManager().useTimings() && ++timingDepth == 1 ) + { + start = System.nanoTime(); + if ( parent != null && ++parent.timingDepth == 1 ) + { + parent.start = start; + } + } + } + + /** + * Stops timing a section of code. + */ + public void stopTiming() + { + if ( Bukkit.getPluginManager().useTimings() ) + { + if ( --timingDepth != 0 || start == 0 ) + { + return; + } + long diff = System.nanoTime() - start; + totalTime += diff; + curTickTotal += diff; + count++; + start = 0; + if ( parent != null ) + { + parent.stopTiming(); + } + } + } + + /** + * Reset this timer, setting all values to zero. + */ + public void reset() + { + count = 0; + violations = 0; + curTickTotal = 0; + totalTime = 0; + start = 0; + timingDepth = 0; + // Poweruser start + this.currentTimings.clear(); + this.currentTimingsSum = 0L; + // Poweruser end + } + + // Poweruser start + private void updateAverageCalculation() { + this.currentTimingsSum += this.curTickTotal; + this.currentTimings.add(this.curTickTotal); + while(this.currentTimings.size() > 20) { + Long value = this.currentTimings.poll(); + this.currentTimingsSum -= value.longValue(); + } + } + + /** + * Returns the average tick time over the last 20 ticks + */ + public long getRecentAverage() { + if(!this.currentTimings.isEmpty()) { + return this.currentTimingsSum / this.currentTimings.size(); + } + return 0L; + } + + public long getCurrentTickTotal() { + return this.curTickTotal; + } + + public long getCurrentCount() { + return this.count; + } + + public String getName() { + return this.name; + } + // Poweruser end +} diff --git a/vspigot-api/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java b/vspigot-api/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java new file mode 100644 index 0000000..24d4942 --- /dev/null +++ b/vspigot-api/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java @@ -0,0 +1,39 @@ +package org.spigotmc.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; + +/** + * Called when an entity stops riding another entity. + * + */ +public class EntityDismountEvent extends EntityEvent +{ + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Entity dismounted; + + public EntityDismountEvent(Entity what, Entity dismounted) + { + super( what ); + this.dismounted = dismounted; + } + + public Entity getDismounted() + { + return dismounted; + } + + @Override + public HandlerList getHandlers() + { + return handlers; + } + + public static HandlerList getHandlerList() + { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/spigotmc/event/entity/EntityMountEvent.java b/vspigot-api/src/main/java/org/spigotmc/event/entity/EntityMountEvent.java new file mode 100644 index 0000000..16aa2a7 --- /dev/null +++ b/vspigot-api/src/main/java/org/spigotmc/event/entity/EntityMountEvent.java @@ -0,0 +1,52 @@ +package org.spigotmc.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; + +/** + * Called when an entity attempts to ride another entity. + * + */ +public class EntityMountEvent extends EntityEvent implements Cancellable +{ + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Entity mount; + + public EntityMountEvent(Entity what, Entity mount) + { + super( what ); + this.mount = mount; + } + + public Entity getMount() + { + return mount; + } + + @Override + public boolean isCancelled() + { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) + { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() + { + return handlers; + } + + public static HandlerList getHandlerList() + { + return handlers; + } +} diff --git a/vspigot-api/src/main/java/org/spigotmc/event/player/PlayerSpawnLocationEvent.java b/vspigot-api/src/main/java/org/spigotmc/event/player/PlayerSpawnLocationEvent.java new file mode 100644 index 0000000..dd3f58c --- /dev/null +++ b/vspigot-api/src/main/java/org/spigotmc/event/player/PlayerSpawnLocationEvent.java @@ -0,0 +1,50 @@ +package org.spigotmc.event.player; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; + +/** + * Called when player is about to spawn in a world after joining the server. + */ +public class PlayerSpawnLocationEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private Location spawnLocation; + + public PlayerSpawnLocationEvent(final Player who, Location spawnLocation) { + super(who); + this.spawnLocation = spawnLocation; + } + + + /** + * Gets player's spawn location. + * If the player {@link Player#hasPlayedBefore()}, it's going to default to the location inside player.dat file. + * For new players, the default spawn location is spawn of the main Bukkit world. + * + * @return the spawn location + */ + public Location getSpawnLocation() { + return spawnLocation; + } + + /** + * Sets player's spawn location. + * + * @param location the spawn location + */ + public void setSpawnLocation(Location location) { + this.spawnLocation = location; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/main/javadoc/org/bukkit/block/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/block/package-info.java new file mode 100644 index 0000000..fb7719b --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/block/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes used to manipulate the voxels in a {@link org.bukkit.World world}, + * including special states. + *

            + */ +package org.bukkit.block; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/command/defaults/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/command/defaults/package-info.java new file mode 100644 index 0000000..f296730 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/command/defaults/package-info.java @@ -0,0 +1,7 @@ +/** + * Commands for emulating the Minecraft commands and other necessary ones for + * use by a Bukkit implementation. + *

            + */ +package org.bukkit.command.defaults; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/command/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/command/package-info.java new file mode 100644 index 0000000..f06407a --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/command/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes relating to handling specialized non-chat player input. + *

            + */ +package org.bukkit.command; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/configuration/file/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/configuration/file/package-info.java new file mode 100644 index 0000000..9b1161b --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/configuration/file/package-info.java @@ -0,0 +1,8 @@ +/** + * Classes dedicated facilitating {@link + * org.bukkit.configuration.Configuration configurations} to be read and + * stored on the filesystem. + *

            + */ +package org.bukkit.configuration.file; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/configuration/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/configuration/package-info.java new file mode 100644 index 0000000..fc439db --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/configuration/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes dedicated to handling a plugin's runtime configuration. + *

            + */ +package org.bukkit.configuration; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/configuration/serialization/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/configuration/serialization/package-info.java new file mode 100644 index 0000000..b33306f --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/configuration/serialization/package-info.java @@ -0,0 +1,8 @@ +/** + * Classes dedicated to being able to perform serialization specialized for + * the Bukkit {@link org.bukkit.configuration.Configuration configuration} + * implementation. + *

            + */ +package org.bukkit.configuration.serialization; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/conversations/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/conversations/package-info.java new file mode 100644 index 0000000..2259e92 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/conversations/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes dedicated to facilitate direct player-to-plugin communication. + *

            + */ +package org.bukkit.conversations; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/enchantments/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/enchantments/package-info.java new file mode 100644 index 0000000..a686c82 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/enchantments/package-info.java @@ -0,0 +1,8 @@ +/** + * Classes relating to the specialized enhancements to {@link + * org.bukkit.inventory.ItemStack item stacks}, as part of the {@link + * org.bukkit.inventory.meta.ItemMeta meta data}. + *

            + */ +package org.bukkit.enchantments; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/entity/minecart/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/entity/minecart/package-info.java new file mode 100644 index 0000000..9988caa --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/entity/minecart/package-info.java @@ -0,0 +1,6 @@ +/** + * Interfaces for various {@link org.bukkit.entity.Minecart} types. + *

            + */ +package org.bukkit.entity.minecart; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/entity/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/entity/package-info.java new file mode 100644 index 0000000..581209b --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/entity/package-info.java @@ -0,0 +1,7 @@ +/** + * Interfaces for non-voxel objects that can exist in a {@link + * org.bukkit.World world}, including all players, monsters, projectiles, etc. + *

            + */ +package org.bukkit.entity; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/block/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/block/package-info.java new file mode 100644 index 0000000..dd53485 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/block/package-info.java @@ -0,0 +1,8 @@ +/** + * {@link org.bukkit.event.Event Events} relating to when a {@link + * org.bukkit.block.Block block} is changed or interacts with the {@link + * org.bukkit.World world}. + *

            + */ +package org.bukkit.event.block; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/enchantment/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/enchantment/package-info.java new file mode 100644 index 0000000..7be6f83 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/enchantment/package-info.java @@ -0,0 +1,7 @@ +/** + * {@link org.bukkit.event.Event Events} triggered from an {@link + * org.bukkit.inventory.EnchantingInventory enchantment table}. + *

            + */ +package org.bukkit.event.enchantment; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/entity/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/entity/package-info.java new file mode 100644 index 0000000..36a1a09 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/entity/package-info.java @@ -0,0 +1,8 @@ +/** + * {@link org.bukkit.event.Event Events} relating to {@link + * org.bukkit.entity.Entity entities}, excluding some directly referencing + * some more specific entity types. + *

            + */ +package org.bukkit.event.entity; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/hanging/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/hanging/package-info.java new file mode 100644 index 0000000..63e9e7e --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/hanging/package-info.java @@ -0,0 +1,7 @@ +/** + * {@link org.bukkit.event.Event Events} relating to {@link + * org.bukkit.entity.Hanging entities that hang}. + *

            + */ +package org.bukkit.event.hanging; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/inventory/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/inventory/package-info.java new file mode 100644 index 0000000..7cb5a63 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/inventory/package-info.java @@ -0,0 +1,7 @@ +/** + * {@link org.bukkit.event.Event Events} relating to {@link + * org.bukkit.inventory.Inventory inventory} manipulation. + *

            + */ +package org.bukkit.event.inventory; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/package-info.java new file mode 100644 index 0000000..ab3011a --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes dedicated to handling triggered code executions. + *

            + */ +package org.bukkit.event; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/painting/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/painting/package-info.java new file mode 100644 index 0000000..39a8bb7 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/painting/package-info.java @@ -0,0 +1,8 @@ +/** + * {@link org.bukkit.event.Event Events} relating to {@link + * org.bukkit.entity.Painting paintings}, but deprecated for more general + * {@link org.bukkit.event.hanging hanging} events. + *

            + */ +package org.bukkit.event.painting; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/player/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/player/package-info.java new file mode 100644 index 0000000..c5a4954 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/player/package-info.java @@ -0,0 +1,7 @@ +/** + * {@link org.bukkit.event.Event Events} relating to {@link + * org.bukkit.entity.Player players}. + *

            + */ +package org.bukkit.event.player; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/server/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/server/package-info.java new file mode 100644 index 0000000..a2e1699 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/server/package-info.java @@ -0,0 +1,7 @@ +/** + * {@link org.bukkit.event.Event Events} relating to programmatic state + * changes on the server. + *

            + */ +package org.bukkit.event.server; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/vehicle/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/vehicle/package-info.java new file mode 100644 index 0000000..07c470a --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/vehicle/package-info.java @@ -0,0 +1,7 @@ +/** + * {@link org.bukkit.event.Event Events} relating to {@link + * org.bukkit.entity.Vehicle vehicular entities}. + *

            + */ +package org.bukkit.event.vehicle; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/weather/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/weather/package-info.java new file mode 100644 index 0000000..f2e3f49 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/weather/package-info.java @@ -0,0 +1,6 @@ +/** + * {@link org.bukkit.event.Event Events} relating to weather. + *

            + */ +package org.bukkit.event.weather; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/event/world/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/event/world/package-info.java new file mode 100644 index 0000000..3b075f6 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/event/world/package-info.java @@ -0,0 +1,7 @@ +/** + * {@link org.bukkit.event.Event Events} triggered by various {@link + * org.bukkit.World world} states or changes. + *

            + */ +package org.bukkit.event.world; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/generator/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/generator/package-info.java new file mode 100644 index 0000000..54d1c29 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/generator/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes to facilitate {@link org.bukkit.World world} generation + * implementation. + *

            + */ +package org.bukkit.generator; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/help/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/help/package-info.java new file mode 100644 index 0000000..691dfcd --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/help/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes used to manipulate the default command and topic assistance system. + *

            + */ +package org.bukkit.help; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/inventory/meta/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/inventory/meta/package-info.java new file mode 100644 index 0000000..f39d93a --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/inventory/meta/package-info.java @@ -0,0 +1,7 @@ +/** + * The interfaces used when manipulating extra data can can be stored inside + * {@link org.bukkit.inventory.ItemStack item stacks}. + *

            + */ +package org.bukkit.inventory.meta; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/inventory/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/inventory/package-info.java new file mode 100644 index 0000000..79e26ca --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/inventory/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes involved in manipulating player inventories and item interactions. + *

            + */ +package org.bukkit.inventory; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/map/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/map/package-info.java new file mode 100644 index 0000000..9e08d6e --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/map/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes to facilitate plugin handling of {@link org.bukkit.Material#MAP + * map} displays. + *

            + */ +package org.bukkit.map; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/material/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/material/package-info.java new file mode 100644 index 0000000..d2fa49f --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/material/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes that represents various voxel types and states. + *

            + */ +package org.bukkit.material; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/metadata/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/metadata/package-info.java new file mode 100644 index 0000000..44227e6 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/metadata/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes dedicated to providing a layer of plugin specified data on various + * Minecraft concepts. + *

            + */ +package org.bukkit.metadata; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/package-info.java new file mode 100644 index 0000000..f85c88d --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/package-info.java @@ -0,0 +1,6 @@ +/** + * More generalized classes in the API. + *

            + */ +package org.bukkit; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/permissions/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/permissions/package-info.java new file mode 100644 index 0000000..3a5befe --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/permissions/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes dedicated to providing binary state properties to players. + *

            + */ +package org.bukkit.permissions; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/plugin/doc-files/permissions-example_plugin.yml b/vspigot-api/src/main/javadoc/org/bukkit/plugin/doc-files/permissions-example_plugin.yml new file mode 100644 index 0000000..34d0381 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/plugin/doc-files/permissions-example_plugin.yml @@ -0,0 +1,64 @@ +name: ScrapBukkit +main: com.dinnerbone.bukkit.scrap.ScrapBukkit +version: 1.0.0 +website: http://www.bukkit.org +author: The Bukkit Team +description: > + Miscellaneous administrative commands for Bukkit. + This plugin is one of the default plugins shipped with Bukkit. +# commands: snipped + +permissions: + scrapbukkit.*: + description: Gives all permissions for Scrapbukkit + default: op + children: + scrapbukkit.remove: + description: Allows the player to remove items from anyones inventory + children: + scrapbukkit.remove.self: + description: Allows the player to remove items from their own inventory + scrapbukkit.remove.other: + description: Allows the player to remove items from other peoples inventory + + scrapbukkit.time: + description: Allows the player to view and change the time + children: + scrapbukkit.time.view: + description: Allows the player to view the time + default: true + scrapbukkit.time.change: + description: Allows the player to change the time + + scrapbukkit.tp: + description: Allows the player to teleport anyone to anyone else + children: + scrapbukkit.tp.here: + description: Allows the player to teleport other players to themselves + scrapbukkit.tp.self: + description: Allows the player to teleport themselves to another player + scrapbukkit.tp.other: + description: Allows the player to teleport anyone to another player + + scrapbukkit.give: + description: Allows the player to give items + children: + scrapbukkit.give.self: + description: Allows the player to give themselves items + scrapbukkit.give.other: + description: Allows the player to give other players items + + scrapbukkit.clear: + description: Allows the player to clear inventories + children: + scrapbukkit.clear.self: + description: Allows the player to clear their own inventory + scrapbukkit.clear.other: + description: Allows the player to clear other players inventory + + scrapbukkit.some.standard.perm: {} + scrapbukkit.some.other.perm: true + scrapbukkit.some.bad.perm: false + # This is defined here individually, as simply giving it an association will not cause it to exist at runtime + scrapbukkit.some.bad.perm: {} + scrapbukkit.some.other.perm: {} diff --git a/vspigot-api/src/main/javadoc/org/bukkit/plugin/java/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/plugin/java/package-info.java new file mode 100644 index 0000000..8d6f2a5 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/plugin/java/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes for handling {@link org.bukkit.plugin.Plugin plugins} written in + * java. + *

            + */ +package org.bukkit.plugin.java; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/plugin/messaging/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/plugin/messaging/package-info.java new file mode 100644 index 0000000..c94ac66 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/plugin/messaging/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes dedicated to specialized plugin to client protocols. + *

            + */ +package org.bukkit.plugin.messaging; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/plugin/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/plugin/package-info.java new file mode 100644 index 0000000..087a0a4 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/plugin/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes specifically relating to loading software modules at runtime. + *

            + */ +package org.bukkit.plugin; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/potion/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/potion/package-info.java new file mode 100644 index 0000000..9b13a77 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/potion/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes to represent various {@link org.bukkit.Material#POTION potion} + * properties and manipulation. + *

            + */ +package org.bukkit.potion; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/projectiles/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/projectiles/package-info.java new file mode 100644 index 0000000..0e39662 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/projectiles/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes to represent the source of a projectile + *

            + */ +package org.bukkit.projectiles; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/scheduler/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/scheduler/package-info.java new file mode 100644 index 0000000..87059ea --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/scheduler/package-info.java @@ -0,0 +1,7 @@ +/** + * Classes dedicated to letting {@link org.bukkit.plugin.Plugin plugins} run + * code at specific time intervals, including thread safety. + *

            + */ +package org.bukkit.scheduler; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/scoreboard/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/scoreboard/package-info.java new file mode 100644 index 0000000..aad5e31 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/scoreboard/package-info.java @@ -0,0 +1,6 @@ +/** + * Interfaces used to manage the client side score display system. + *

            + */ +package org.bukkit.scoreboard; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/util/io/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/util/io/package-info.java new file mode 100644 index 0000000..8e46cca --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/util/io/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes used to facilitate stream processing for specific Bukkit concepts. + *

            + */ +package org.bukkit.util.io; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/util/noise/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/util/noise/package-info.java new file mode 100644 index 0000000..f77e35d --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/util/noise/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes dedicated to facilitating deterministic noise. + *

            + */ +package org.bukkit.util.noise; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/util/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/util/package-info.java new file mode 100644 index 0000000..a9c4c9d --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/util/package-info.java @@ -0,0 +1,7 @@ +/** + * Multi and single purpose classes to facilitate various programmatic + * concepts. + *

            + */ +package org.bukkit.util; + diff --git a/vspigot-api/src/main/javadoc/org/bukkit/util/permissions/package-info.java b/vspigot-api/src/main/javadoc/org/bukkit/util/permissions/package-info.java new file mode 100644 index 0000000..9a40b29 --- /dev/null +++ b/vspigot-api/src/main/javadoc/org/bukkit/util/permissions/package-info.java @@ -0,0 +1,7 @@ +/** + * Static methods for miscellaneous {@link org.bukkit.permissions.Permission + * permission} functionality. + *

            + */ +package org.bukkit.util.permissions; + diff --git a/vspigot-api/src/main/javadoc/overview.html b/vspigot-api/src/main/javadoc/overview.html new file mode 100644 index 0000000..e96bf35 --- /dev/null +++ b/vspigot-api/src/main/javadoc/overview.html @@ -0,0 +1,17 @@ + +

            Bukkit, the plugin development framework.

            + +

            + The documentation is for developing plugins and is split into the + respective packages for each subject matter. This documentation does not + cover running a server, contributing code back to the project, or setting + up a workspace. Working knowledge of the Java language is a prerequisite + for developing plugins. +

            + For basic plugin development, see the {@link org.bukkit.plugin plugin + package}. It covers the basic requirements of a plugin jar. +

            + For handling events and triggered code, see the {@link org.bukkit.event + event package}. +

            + diff --git a/vspigot-api/src/site/apt/index.apt b/vspigot-api/src/site/apt/index.apt new file mode 100644 index 0000000..0a7880a --- /dev/null +++ b/vspigot-api/src/site/apt/index.apt @@ -0,0 +1,9 @@ +Welcome to the Bukkit Maven mini-site! + + This website is automatically generated by Maven 3. + As such, it is updated when the main build system (http://ci.bukkit.org) executes the site lifecycle on the Maven job. + For more information about Bukkit, please see the links above. + +Javadocs + + For Javadocs, please select the "Project Reports" then "JavaDocs" option on the left-hand menu. diff --git a/vspigot-api/src/site/site.xml b/vspigot-api/src/site/site.xml new file mode 100644 index 0000000..ff24fab --- /dev/null +++ b/vspigot-api/src/site/site.xml @@ -0,0 +1,29 @@ + + + + Bukkit + http://bukkit.org/logo.png + http://bukkit.org/ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vspigot-api/src/test/java/org/bukkit/ArtTest.java b/vspigot-api/src/test/java/org/bukkit/ArtTest.java new file mode 100644 index 0000000..29fe34a --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/ArtTest.java @@ -0,0 +1,45 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class ArtTest { + + @Test(expected = IllegalArgumentException.class) + public void getByNullName() { + Art.getByName(null); + } + + @Test + public void getById() { + for (Art art : Art.values()) { + assertThat(Art.getById(art.getId()), is(art)); + } + } + + @Test + public void getByName() { + for (Art art : Art.values()) { + assertThat(Art.getByName(art.toString()), is(art)); + } + } + + @Test + public void dimensionSanityCheck() { + for (Art art : Art.values()) { + assertThat(art.getBlockHeight(), is(greaterThan(0))); + assertThat(art.getBlockWidth(), is(greaterThan(0))); + } + } + + @Test + public void getByNameWithMixedCase() { + Art subject = Art.values()[0]; + String name = subject.toString().replace('E', 'e'); + + assertThat(Art.getByName(name), is(subject)); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/BukkitMirrorTest.java b/vspigot-api/src/test/java/org/bukkit/BukkitMirrorTest.java new file mode 100644 index 0000000..219788f --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/BukkitMirrorTest.java @@ -0,0 +1,75 @@ +package org.bukkit; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; + +@RunWith(Parameterized.class) +public class BukkitMirrorTest { + + @Parameters(name="{index}: {1}") + public static List data() { + return Lists.transform(Arrays.asList(Server.class.getDeclaredMethods()), new Function() { + @Override + public Object[] apply(Method input) { + return new Object[] { + input, + input.toGenericString().substring("public abstract ".length()).replace("(", "{").replace(")", "}") + }; + } + }); + } + + @Parameter(0) + public Method server; + + @Parameter(1) + public String name; + + private Method bukkit; + + @Before + public void makeBukkit() throws Throwable { + bukkit = Bukkit.class.getDeclaredMethod(server.getName(), server.getParameterTypes()); + } + + @Test + public void isStatic() throws Throwable { + assertThat(Modifier.isStatic(bukkit.getModifiers()), is(true)); + } + + @Test + public void isDeprecated() throws Throwable { + assertThat(bukkit.isAnnotationPresent(Deprecated.class), is(server.isAnnotationPresent(Deprecated.class))); + } + + @Test + public void returnType() throws Throwable { + assertThat(bukkit.getReturnType(), is((Object) server.getReturnType())); + assertThat(bukkit.getGenericReturnType(), is(server.getGenericReturnType())); + } + + @Test + public void parameterTypes() throws Throwable { + assertThat(bukkit.getGenericParameterTypes(), is(server.getGenericParameterTypes())); + } + + @Test + public void declaredException() throws Throwable { + assertThat(bukkit.getGenericExceptionTypes(), is(server.getGenericExceptionTypes())); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/ChatColorTest.java b/vspigot-api/src/test/java/org/bukkit/ChatColorTest.java new file mode 100644 index 0000000..80108a5 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/ChatColorTest.java @@ -0,0 +1,83 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class ChatColorTest { + + @Test + public void getByChar() { + for (ChatColor color : ChatColor.values()) { + assertThat(ChatColor.getByChar(color.getChar()), is(color)); + } + } + + @Test(expected = IllegalArgumentException.class) + public void getByStringWithNull() { + ChatColor.getByChar((String) null); + } + + @Test(expected = IllegalArgumentException.class) + public void getByStringWithEmpty() { + ChatColor.getByChar(""); + } + + @Test + public void getByNull() { + assertThat(ChatColor.stripColor(null), is(nullValue())); + } + + @Test + public void getByString() { + for (ChatColor color : ChatColor.values()) { + assertThat(ChatColor.getByChar(String.valueOf(color.getChar())), is(color)); + } + } + + @Test + public void stripColorOnNullString() { + assertThat(ChatColor.stripColor(null), is(nullValue())); + } + + @Test + public void stripColor() { + StringBuilder subject = new StringBuilder(); + StringBuilder expected = new StringBuilder(); + + final String filler = "test"; + for (ChatColor color : ChatColor.values()) { + subject.append(color).append(filler); + expected.append(filler); + } + + assertThat(ChatColor.stripColor(subject.toString()), is(expected.toString())); + } + + @Test + public void toStringWorks() { + for (ChatColor color : ChatColor.values()) { + assertThat(String.format("%c%c", ChatColor.COLOR_CHAR, color.getChar()), is(color.toString())); + } + } + + @Test + public void translateAlternateColorCodes() { + String s = "&0&1&2&3&4&5&6&7&8&9&A&a&B&b&C&c&D&d&E&e&F&f&K&k & more"; + String t = ChatColor.translateAlternateColorCodes('&', s); + String u = ChatColor.BLACK.toString() + ChatColor.DARK_BLUE + ChatColor.DARK_GREEN + ChatColor.DARK_AQUA + ChatColor.DARK_RED + ChatColor.DARK_PURPLE + ChatColor.GOLD + ChatColor.GRAY + ChatColor.DARK_GRAY + ChatColor.BLUE + ChatColor.GREEN + ChatColor.GREEN + ChatColor.AQUA + ChatColor.AQUA + ChatColor.RED + ChatColor.RED + ChatColor.LIGHT_PURPLE + ChatColor.LIGHT_PURPLE + ChatColor.YELLOW + ChatColor.YELLOW + ChatColor.WHITE + ChatColor.WHITE + ChatColor.MAGIC + ChatColor.MAGIC + " & more"; + assertThat(t, is(u)); + } + + @Test + public void getChatColors() { + String s = String.format("%c%ctest%c%ctest%c", ChatColor.COLOR_CHAR, ChatColor.RED.getChar(), ChatColor.COLOR_CHAR, ChatColor.ITALIC.getChar(), ChatColor.COLOR_CHAR); + String expected = ChatColor.RED.toString() + ChatColor.ITALIC; + assertThat(ChatColor.getLastColors(s), is(expected)); + + s = String.format("%c%ctest%c%ctest", ChatColor.COLOR_CHAR, ChatColor.RED.getChar(), ChatColor.COLOR_CHAR, ChatColor.BLUE.getChar()); + assertThat(ChatColor.getLastColors(s), is(ChatColor.BLUE.toString())); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/ChatPaginatorTest.java b/vspigot-api/src/test/java/org/bukkit/ChatPaginatorTest.java new file mode 100644 index 0000000..0a8acae --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/ChatPaginatorTest.java @@ -0,0 +1,166 @@ +package org.bukkit; + +import org.bukkit.util.ChatPaginator; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + */ +public class ChatPaginatorTest { + @Test + public void testWordWrap1() { + String rawString = ChatColor.RED + "123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is(ChatColor.RED + "123456789 123456789")); + assertThat(lines[1], is(ChatColor.RED.toString() + "123456789")); + } + + @Test + public void testWordWrap2() { + String rawString = "123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 22); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "123456789 123456789")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "123456789")); + } + + @Test + public void testWordWrap3() { + String rawString = ChatColor.RED + "123456789 " + ChatColor.RED + ChatColor.RED + "123456789 " + ChatColor.RED + "123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 16); + + assertThat(lines.length, is(3)); + assertThat(lines[0], is(ChatColor.RED + "123456789")); + assertThat(lines[1], is(ChatColor.RED.toString() + ChatColor.RED + "123456789")); + assertThat(lines[2], is(ChatColor.RED + "123456789")); + } + + @Test + public void testWordWrap4() { + String rawString = "123456789 123456789 123456789 12345"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "123456789 123456789")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "123456789 12345")); + } + + @Test + public void testWordWrap5() { + String rawString = "123456789\n123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "123456789")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "123456789 123456789")); + } + + @Test + public void testWordWrap6() { + String rawString = "12345678 23456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "12345678 23456789")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "123456789")); + } + + @Test + public void testWordWrap7() { + String rawString = "12345678 23456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "12345678 23456789")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "123456789")); + } + + @Test + public void testWordWrap8() { + String rawString = "123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 6); + + assertThat(lines.length, is(6)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "123456")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "789")); + assertThat(lines[2], is(ChatColor.WHITE.toString() + "123456")); + assertThat(lines[3], is(ChatColor.WHITE.toString() + "789")); + assertThat(lines[4], is(ChatColor.WHITE.toString() + "123456")); + assertThat(lines[5], is(ChatColor.WHITE.toString() + "789")); + } + + @Test + public void testWordWrap9() { + String rawString = "1234 123456789 123456789 123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 6); + + assertThat(lines.length, is(7)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "1234")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "123456")); + assertThat(lines[2], is(ChatColor.WHITE.toString() + "789")); + assertThat(lines[3], is(ChatColor.WHITE.toString() + "123456")); + assertThat(lines[4], is(ChatColor.WHITE.toString() + "789")); + assertThat(lines[5], is(ChatColor.WHITE.toString() + "123456")); + assertThat(lines[6], is(ChatColor.WHITE.toString() + "789")); + } + + @Test + public void testWordWrap10() { + String rawString = "123456789\n123456789"; + String[] lines = ChatPaginator.wordWrap(rawString, 19); + + assertThat(lines.length, is(2)); + assertThat(lines[0], is(ChatColor.WHITE.toString() + "123456789")); + assertThat(lines[1], is(ChatColor.WHITE.toString() + "123456789")); + } + + @Test + public void testWordWrap11() { + String rawString = ChatColor.RED + "a a a " + ChatColor.BLUE + "a a"; + String[] lines = ChatPaginator.wordWrap(rawString, 9); + + assertThat(lines.length, is(1)); + assertThat(lines[0], is(ChatColor.RED + "a a a " + ChatColor.BLUE + "a a")); + } + + @Test + public void testPaginate1() { + String rawString = "1234 123456789 123456789 123456789"; + ChatPaginator.ChatPage page = ChatPaginator.paginate(rawString, 1, 6, 2); + + assertThat(page.getPageNumber(), is(1)); + assertThat(page.getTotalPages(), is(4)); + assertThat(page.getLines().length, is(2)); + assertThat(page.getLines()[0], is(ChatColor.WHITE.toString() + "1234")); + assertThat(page.getLines()[1], is(ChatColor.WHITE.toString() + "123456")); + } + + @Test + public void testPaginate2() { + String rawString = "1234 123456789 123456789 123456789"; + ChatPaginator.ChatPage page = ChatPaginator.paginate(rawString, 2, 6, 2); + + assertThat(page.getPageNumber(), is(2)); + assertThat(page.getTotalPages(), is(4)); + assertThat(page.getLines().length, is(2)); + assertThat(page.getLines()[0], is(ChatColor.WHITE.toString() + "789")); + assertThat(page.getLines()[1], is(ChatColor.WHITE.toString() + "123456")); + } + + @Test + public void testPaginate3() { + String rawString = "1234 123456789 123456789 123456789"; + ChatPaginator.ChatPage page = ChatPaginator.paginate(rawString, 4, 6, 2); + + assertThat(page.getPageNumber(), is(4)); + assertThat(page.getTotalPages(), is(4)); + assertThat(page.getLines().length, is(1)); + assertThat(page.getLines()[0], is(ChatColor.WHITE.toString() + "789")); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/CoalTypeTest.java b/vspigot-api/src/test/java/org/bukkit/CoalTypeTest.java new file mode 100644 index 0000000..7eb11ce --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/CoalTypeTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class CoalTypeTest { + @Test + public void getByData() { + for (CoalType coalType : CoalType.values()) { + assertThat(CoalType.getByData(coalType.getData()), is(coalType)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/ColorTest.java b/vspigot-api/src/test/java/org/bukkit/ColorTest.java new file mode 100644 index 0000000..8d9557a --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/ColorTest.java @@ -0,0 +1,365 @@ +package org.bukkit; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Test; + +@SuppressWarnings("javadoc") +public class ColorTest { + static class TestColor { + static int id = 0; + final String name; + final int rgb; + final int bgr; + final int r; + final int g; + final int b; + + TestColor(int rgb, int bgr, int r, int g, int b) { + this.rgb = rgb; + this.bgr = bgr; + this.r = r; + this.g = g; + this.b = b; + this.name = id + ":" + Integer.toHexString(rgb).toUpperCase() + "_" + Integer.toHexString(bgr).toUpperCase() + "-r" + Integer.toHexString(r).toUpperCase() + "-g" + Integer.toHexString(g).toUpperCase() + "-b" + Integer.toHexString(b).toUpperCase(); + } + } + + static TestColor[] examples = new TestColor[] { + /* 0xRRGGBB, 0xBBGGRR, 0xRR, 0xGG, 0xBB */ + new TestColor(0xFFFFFF, 0xFFFFFF, 0xFF, 0xFF, 0xFF), + new TestColor(0xFFFFAA, 0xAAFFFF, 0xFF, 0xFF, 0xAA), + new TestColor(0xFF00FF, 0xFF00FF, 0xFF, 0x00, 0xFF), + new TestColor(0x67FF22, 0x22FF67, 0x67, 0xFF, 0x22), + new TestColor(0x000000, 0x000000, 0x00, 0x00, 0x00) + }; + + @Test + public void testSerialization() throws Throwable { + for (TestColor testColor : examples) { + Color base = Color.fromRGB(testColor.rgb); + + YamlConfiguration toSerialize = new YamlConfiguration(); + toSerialize.set("color", base); + String serialized = toSerialize.saveToString(); + + YamlConfiguration deserialized = new YamlConfiguration(); + deserialized.loadFromString(serialized); + + assertThat(testColor.name + " on " + serialized, base, is(deserialized.getColor("color"))); + } + } + + // Equality tests + @Test + public void testEqualities() { + for (TestColor testColor : examples) { + Color fromRGB = Color.fromRGB(testColor.rgb); + Color fromBGR = Color.fromBGR(testColor.bgr); + Color fromRGBs = Color.fromRGB(testColor.r, testColor.g, testColor.b); + Color fromBGRs = Color.fromBGR(testColor.b, testColor.g, testColor.r); + + assertThat(testColor.name, fromRGB, is(fromRGBs)); + assertThat(testColor.name, fromRGB, is(fromBGR)); + assertThat(testColor.name, fromRGB, is(fromBGRs)); + assertThat(testColor.name, fromRGBs, is(fromBGR)); + assertThat(testColor.name, fromRGBs, is(fromBGRs)); + assertThat(testColor.name, fromBGR, is(fromBGRs)); + } + } + + @Test + public void testInequalities() { + for (int i = 1; i < examples.length; i++) { + TestColor testFrom = examples[i]; + Color from = Color.fromRGB(testFrom.rgb); + for (int j = i - 1; j >= 0; j--) { + TestColor testTo = examples[j]; + Color to = Color.fromRGB(testTo.rgb); + String name = testFrom.name + " to " + testTo.name; + assertThat(name, from, is(not(to))); + + Color transform = from.setRed(testTo.r).setBlue(testTo.b).setGreen(testTo.g); + assertThat(name, transform, is(not(sameInstance(from)))); + assertThat(name, transform, is(to)); + } + } + } + + // RGB tests + @Test + public void testRGB() { + for (TestColor testColor : examples) { + assertThat(testColor.name, Color.fromRGB(testColor.rgb).asRGB(), is(testColor.rgb)); + assertThat(testColor.name, Color.fromBGR(testColor.bgr).asRGB(), is(testColor.rgb)); + assertThat(testColor.name, Color.fromRGB(testColor.r, testColor.g, testColor.b).asRGB(), is(testColor.rgb)); + assertThat(testColor.name, Color.fromBGR(testColor.b, testColor.g, testColor.r).asRGB(), is(testColor.rgb)); + } + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidRGB1() { + Color.fromRGB(0x01000000); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidRGB2() { + Color.fromRGB(Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidRGB3() { + Color.fromRGB(Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidRGB4() { + Color.fromRGB(-1); + } + + // BGR tests + @Test + public void testBGR() { + for (TestColor testColor : examples) { + assertThat(testColor.name, Color.fromRGB(testColor.rgb).asBGR(), is(testColor.bgr)); + assertThat(testColor.name, Color.fromBGR(testColor.bgr).asBGR(), is(testColor.bgr)); + assertThat(testColor.name, Color.fromRGB(testColor.r, testColor.g, testColor.b).asBGR(), is(testColor.bgr)); + assertThat(testColor.name, Color.fromBGR(testColor.b, testColor.g, testColor.r).asBGR(), is(testColor.bgr)); + } + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidBGR1() { + Color.fromBGR(0x01000000); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidBGR2() { + Color.fromBGR(Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidBGR3() { + Color.fromBGR(Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidBGR4() { + Color.fromBGR(-1); + } + + // Red tests + @Test + public void testRed() { + for (TestColor testColor : examples) { + assertThat(testColor.name, Color.fromRGB(testColor.rgb).getRed(), is(testColor.r)); + assertThat(testColor.name, Color.fromBGR(testColor.bgr).getRed(), is(testColor.r)); + assertThat(testColor.name, Color.fromRGB(testColor.r, testColor.g, testColor.b).getRed(), is(testColor.r)); + assertThat(testColor.name, Color.fromBGR(testColor.b, testColor.g, testColor.r).getRed(), is(testColor.r)); + } + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR01() { + Color.fromRGB(-1, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR02() { + Color.fromRGB(Integer.MAX_VALUE, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR03() { + Color.fromRGB(Integer.MIN_VALUE, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR04() { + Color.fromRGB(0x100, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR05() { + Color.fromBGR(0x00, 0x00, -1); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR06() { + Color.fromBGR(0x00, 0x00, Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR07() { + Color.fromBGR(0x00, 0x00, Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR08() { + Color.fromBGR(0x00, 0x00, 0x100); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR09() { + Color.WHITE.setRed(-1); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR10() { + Color.WHITE.setRed(Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR11() { + Color.WHITE.setRed(Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidR12() { + Color.WHITE.setRed(0x100); + } + + // Blue tests + @Test + public void testBlue() { + for (TestColor testColor : examples) { + assertThat(testColor.name, Color.fromRGB(testColor.rgb).getBlue(), is(testColor.b)); + assertThat(testColor.name, Color.fromBGR(testColor.bgr).getBlue(), is(testColor.b)); + assertThat(testColor.name, Color.fromRGB(testColor.r, testColor.g, testColor.b).getBlue(), is(testColor.b)); + assertThat(testColor.name, Color.fromBGR(testColor.b, testColor.g, testColor.r).getBlue(), is(testColor.b)); + } + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB01() { + Color.fromRGB(0x00, 0x00, -1); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB02() { + Color.fromRGB(0x00, 0x00, Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB03() { + Color.fromRGB(0x00, 0x00, Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB04() { + Color.fromRGB(0x00, 0x00, 0x100); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB05() { + Color.fromBGR(-1, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB06() { + Color.fromBGR(Integer.MAX_VALUE, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB07() { + Color.fromBGR(Integer.MIN_VALUE, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB08() { + Color.fromBGR(0x100, 0x00, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB09() { + Color.WHITE.setBlue(-1); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB10() { + Color.WHITE.setBlue(Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB11() { + Color.WHITE.setBlue(Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidB12() { + Color.WHITE.setBlue(0x100); + } + + // Green tests + @Test + public void testGreen() { + for (TestColor testColor : examples) { + assertThat(testColor.name, Color.fromRGB(testColor.rgb).getGreen(), is(testColor.g)); + assertThat(testColor.name, Color.fromBGR(testColor.bgr).getGreen(), is(testColor.g)); + assertThat(testColor.name, Color.fromRGB(testColor.r, testColor.g, testColor.b).getGreen(), is(testColor.g)); + assertThat(testColor.name, Color.fromBGR(testColor.b, testColor.g, testColor.r).getGreen(), is(testColor.g)); + } + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG01() { + Color.fromRGB(0x00, -1, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG02() { + Color.fromRGB(0x00, Integer.MAX_VALUE, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG03() { + Color.fromRGB(0x00, Integer.MIN_VALUE, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG04() { + Color.fromRGB(0x00, 0x100, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG05() { + Color.fromBGR(0x00, -1, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG06() { + Color.fromBGR(0x00, Integer.MAX_VALUE, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG07() { + Color.fromBGR(0x00, Integer.MIN_VALUE, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG08() { + Color.fromBGR(0x00, 0x100, 0x00); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG09() { + Color.WHITE.setGreen(-1); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG10() { + Color.WHITE.setGreen(Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG11() { + Color.WHITE.setGreen(Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testInvalidG12() { + Color.WHITE.setGreen(0x100); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/CropStateTest.java b/vspigot-api/src/test/java/org/bukkit/CropStateTest.java new file mode 100644 index 0000000..f792a1c --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/CropStateTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class CropStateTest { + @Test + public void getByData() { + for (CropState cropState : CropState.values()) { + assertThat(CropState.getByData(cropState.getData()), is(cropState)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/DifficultyTest.java b/vspigot-api/src/test/java/org/bukkit/DifficultyTest.java new file mode 100644 index 0000000..293e283 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/DifficultyTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class DifficultyTest { + @Test + public void getByValue() { + for (Difficulty difficulty : Difficulty.values()) { + assertThat(Difficulty.getByValue(difficulty.getValue()), is(difficulty)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/DyeColorTest.java b/vspigot-api/src/test/java/org/bukkit/DyeColorTest.java new file mode 100644 index 0000000..9e30fbf --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/DyeColorTest.java @@ -0,0 +1,70 @@ +package org.bukkit; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.material.Colorable; +import org.bukkit.material.Dye; +import org.bukkit.material.Wool; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class DyeColorTest { + + @Parameters(name= "{index}: {0}") + public static List data() { + List list = new ArrayList(); + for (DyeColor dye : DyeColor.values()) { + list.add(new Object[] {dye}); + } + return list; + } + + @Parameter public DyeColor dye; + + @Test + @SuppressWarnings("deprecation") + public void getByData() { + byte data = dye.getData(); + + DyeColor byData = DyeColor.getByData(data); + assertThat(byData, is(dye)); + } + + @Test + public void getByWoolData() { + byte data = dye.getWoolData(); + + DyeColor byData = DyeColor.getByWoolData(data); + assertThat(byData, is(dye)); + } + + @Test + public void getByDyeData() { + byte data = dye.getDyeData(); + + DyeColor byData = DyeColor.getByDyeData(data); + assertThat(byData, is(dye)); + } + + @Test + public void getDyeDyeColor() { + testColorable(new Dye(Material.INK_SACK, dye.getDyeData())); + } + + @Test + public void getWoolDyeColor() { + testColorable(new Wool(Material.WOOL, dye.getWoolData())); + } + + private void testColorable(final Colorable colorable) { + assertThat(colorable.getColor(), is(this.dye)); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/EffectTest.java b/vspigot-api/src/test/java/org/bukkit/EffectTest.java new file mode 100644 index 0000000..5217aec --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/EffectTest.java @@ -0,0 +1,19 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class EffectTest { + @Test + public void getById() { + for (Effect effect : Effect.values()) { + if (effect.getType() != Effect.Type.PARTICLE) { + assertThat(Effect.getById(effect.getId()), is(effect)); + } else { + assertThat(Effect.getByName(effect.getName()), is(effect)); + } + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/EntityEffectTest.java b/vspigot-api/src/test/java/org/bukkit/EntityEffectTest.java new file mode 100644 index 0000000..0c92d5c --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/EntityEffectTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class EntityEffectTest { + @Test + public void getByData() { + for (EntityEffect entityEffect : EntityEffect.values()) { + assertThat(EntityEffect.getByData(entityEffect.getData()), is(entityEffect)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/GameModeTest.java b/vspigot-api/src/test/java/org/bukkit/GameModeTest.java new file mode 100644 index 0000000..f671fae --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/GameModeTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class GameModeTest { + @Test + public void getByValue() { + for (GameMode gameMode : GameMode.values()) { + assertThat(GameMode.getByValue(gameMode.getValue()), is(gameMode)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/GrassSpeciesTest.java b/vspigot-api/src/test/java/org/bukkit/GrassSpeciesTest.java new file mode 100644 index 0000000..9e332c7 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/GrassSpeciesTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class GrassSpeciesTest { + @Test + public void getByData() { + for (GrassSpecies grassSpecies : GrassSpecies.values()) { + assertThat(GrassSpecies.getByData(grassSpecies.getData()), is(grassSpecies)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/InstrumentTest.java b/vspigot-api/src/test/java/org/bukkit/InstrumentTest.java new file mode 100644 index 0000000..f1a0570 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/InstrumentTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class InstrumentTest { + @Test + public void getByType() { + for (Instrument instrument : Instrument.values()) { + assertThat(Instrument.getByType(instrument.getType()), is(instrument)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/LocationTest.java b/vspigot-api/src/test/java/org/bukkit/LocationTest.java new file mode 100644 index 0000000..fa24776 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/LocationTest.java @@ -0,0 +1,196 @@ +package org.bukkit; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.List; +import java.util.Random; + +import org.bukkit.util.Vector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.collect.ImmutableList; + +@RunWith(Parameterized.class) +public class LocationTest { + private static final double δ = 1.0 / 1000000; + /** + *
            +     * a² + b² = c², a = b
            +     * => 2∙(a²) = 2∙(b²) = c², c = 1
            +     * => 2∙(a²) = 1
            +     * => a² = 1/2
            +     * => a = √(1/2) ∎
            +     * 
            + */ + private static final double HALF_UNIT = Math.sqrt(1 / 2f); + /** + *
            +     * a² + b² = c², c = √(1/2)
            +     * => a² + b² = √(1/2)², a = b
            +     * => 2∙(a²) = 2∙(b²) = 1/2
            +     * => a² = 1/4
            +     * => a = √(1/4) ∎
            +     * 
            + */ + private static final double HALF_HALF_UNIT = Math.sqrt(1 / 4f); + + @Parameters(name= "{index}: {0}") + public static List data() { + Random RANDOM = new Random(1l); // Test is deterministic + int r = 0; + return ImmutableList.of( + new Object[] { "X", + 1, 0, 0, + 270, 0 + }, + new Object[] { "-X", + -1, 0, 0, + 90, 0 + }, + new Object[] { "Z", + 0, 0, 1, + 0, 0 + }, + new Object[] { "-Z", + 0, 0, -1, + 180, 0 + }, + new Object[] { "Y", + 0, 1, 0, + 0, -90 // Zero is here as a "default" value + }, + new Object[] { "-Y", + 0, -1, 0, + 0, 90 // Zero is here as a "default" value + }, + new Object[] { "X Z", + HALF_UNIT, 0, HALF_UNIT, + (270 + 360) / 2, 0 + }, + new Object[] { "X -Z", + HALF_UNIT, 0, -HALF_UNIT, + (270 + 180) / 2, 0 + }, + new Object[] { "-X -Z", + -HALF_UNIT, 0, -HALF_UNIT, + (90 + 180) / 2, 0 + }, + new Object[] { "-X Z", + -HALF_UNIT, 0, HALF_UNIT, + (90 + 0) / 2, 0 + }, + new Object[] { "X Y Z", + HALF_HALF_UNIT, HALF_UNIT, HALF_HALF_UNIT, + (270 + 360) / 2, -45 + }, + new Object[] { "-X -Y -Z", + -HALF_HALF_UNIT, -HALF_UNIT, -HALF_HALF_UNIT, + (90 + 180) / 2, 45 + }, + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++), + getRandom(RANDOM, r++) + ); + } + + private static Object[] getRandom(Random random, int index) { + final double YAW_FACTOR = 360; + final double YAW_OFFSET = 0; + final double PITCH_FACTOR = 180; + final double PITCH_OFFSET = -90; + final double CARTESIAN_FACTOR = 256; + final double CARTESIAN_OFFSET = -128; + + Vector vector; + Location location; + if (random.nextBoolean()) { + float pitch = (float) (random.nextDouble() * PITCH_FACTOR + PITCH_OFFSET); + float yaw = (float) (random.nextDouble() * YAW_FACTOR + YAW_OFFSET); + + location = getEmptyLocation(); + location.setPitch(pitch); + location.setYaw(yaw); + + vector = location.getDirection(); + } else { + double x = random.nextDouble() * CARTESIAN_FACTOR + CARTESIAN_OFFSET; + double y = random.nextDouble() * CARTESIAN_FACTOR + CARTESIAN_OFFSET; + double z = random.nextDouble() * CARTESIAN_FACTOR + CARTESIAN_OFFSET; + + location = getEmptyLocation(); + vector = new Vector(x, y, z).normalize(); + + location.setDirection(vector); + } + + return new Object[] { "R" + index, + vector.getX(), vector.getY(), vector.getZ(), + location.getYaw(), location.getPitch() + }; + } + + @Parameter(0) + public String nane; + @Parameter(1) + public double x; + @Parameter(2) + public double y; + @Parameter(3) + public double z; + @Parameter(4) + public float yaw; + @Parameter(5) + public float pitch; + + @Test + public void testExpectedPitchYaw() { + Location location = getEmptyLocation().setDirection(getVector()); + + assertThat((double) location.getYaw(), is(closeTo(yaw, δ))); + assertThat((double) location.getPitch(), is(closeTo(pitch, δ))); + } + + @Test + public void testExpectedXYZ() { + Vector vector = getLocation().getDirection(); + + assertThat(vector.getX(), is(closeTo(x, δ))); + assertThat(vector.getY(), is(closeTo(y, δ))); + assertThat(vector.getZ(), is(closeTo(z, δ))); + } + + private Vector getVector() { + return new Vector(x, y, z); + } + + private static Location getEmptyLocation() { + return new Location(null, 0, 0, 0); + } + + private Location getLocation() { + Location location = getEmptyLocation(); + location.setYaw(yaw); + location.setPitch(pitch); + return location; + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/MaterialTest.java b/vspigot-api/src/test/java/org/bukkit/MaterialTest.java new file mode 100644 index 0000000..cbca840 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/MaterialTest.java @@ -0,0 +1,85 @@ +package org.bukkit; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.bukkit.material.MaterialData; +import org.junit.Test; + +public class MaterialTest { + @Test + public void getByName() { + for (Material material : Material.values()) { + assertThat(Material.getMaterial(material.toString()), is(material)); + } + } + + @Test + public void getById() throws Throwable { + for (Material material : Material.values()) { + if (material.getClass().getField(material.name()).getAnnotation(Deprecated.class) != null) { + continue; + } + assertThat(Material.getMaterial(material.getId()), is(material)); + } + } + + @Test + public void isBlock() { + for (Material material : Material.values()) { + if (material.getId() > 255) continue; + + assertTrue(String.format("[%d] %s", material.getId(), material.toString()), material.isBlock()); + } + } + + @Test + public void getByOutOfRangeId() { + assertThat(Material.getMaterial(Integer.MAX_VALUE), is(nullValue())); + assertThat(Material.getMaterial(Integer.MIN_VALUE), is(nullValue())); + } + + @Test + public void getByNameNull() { + assertThat(Material.getMaterial(null), is(nullValue())); + } + + @Test + public void getData() { + for (Material material : Material.values()) { + Class clazz = material.getData(); + + assertThat(material.getNewData((byte) 0), is(instanceOf(clazz))); + } + } + + @Test(expected = IllegalArgumentException.class) + public void matchMaterialByNull() { + Material.matchMaterial(null); + } + + @Test + public void matchMaterialById() throws Throwable { + for (Material material : Material.values()) { + if (material.getClass().getField(material.name()).getAnnotation(Deprecated.class) != null) { + continue; + } + assertThat(Material.matchMaterial(String.valueOf(material.getId())), is(material)); + } + } + + @Test + public void matchMaterialByName() { + for (Material material : Material.values()) { + assertThat(Material.matchMaterial(material.toString()), is(material)); + } + } + + @Test + public void matchMaterialByLowerCaseAndSpaces() { + for (Material material : Material.values()) { + String name = material.toString().replaceAll("_", " ").toLowerCase(); + assertThat(Material.matchMaterial(name), is(material)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/NoteTest.java b/vspigot-api/src/test/java/org/bukkit/NoteTest.java new file mode 100644 index 0000000..4759041 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/NoteTest.java @@ -0,0 +1,156 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Collection; + +import org.junit.Test; + +import com.google.common.collect.Lists; + +public class NoteTest { + @Test + public void getToneByData() { + for (Note.Tone tone : Note.Tone.values()) { + assertThat(Note.Tone.getById(tone.getId()), is(tone)); + } + } + + @Test + public void verifySharpedData() { + for (Note.Tone tone : Note.Tone.values()) { + if (!tone.isSharpable()) return; + + assertFalse(tone.isSharped(tone.getId(false))); + assertTrue(tone.isSharped(tone.getId(true))); + } + } + + @Test + public void verifyUnknownToneData() { + Collection tones = Lists.newArrayList(); + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + tones.add((byte) i); + } + + for (Note.Tone tone : Note.Tone.values()) { + if (tone.isSharpable()) tones.remove(tone.getId(true)); + tones.remove(tone.getId()); + } + + for (Byte data : tones) { + assertThat(Note.Tone.getById(data), is(nullValue())); + + for (Note.Tone tone : Note.Tone.values()) { + try { + tone.isSharped(data); + + fail(data + " should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertNotNull(e); + } + } + } + } + + @Test(expected = IllegalArgumentException.class) + public void createNoteBelowMin() { + new Note((byte) -1); + } + + @Test(expected = IllegalArgumentException.class) + public void createNoteAboveMax() { + new Note((byte) 25); + } + + @Test(expected = IllegalArgumentException.class) + public void createNoteOctaveBelowMax() { + new Note((byte) -1, Note.Tone.A, true); + } + + @Test(expected = IllegalArgumentException.class) + public void createNoteOctaveAboveMax() { + new Note((byte) 3, Note.Tone.A, true); + } + + @Test + public void createNoteOctaveNonSharpable() { + Note note = new Note((byte) 0, Note.Tone.B, true); + assertFalse(note.isSharped()); + assertThat(note.getTone(), is(Note.Tone.C)); + } + + @Test + public void createNoteFlat() { + Note note = Note.flat(0, Note.Tone.D); + assertTrue(note.isSharped()); + assertThat(note.getTone(), is(Note.Tone.C)); + } + + @Test + public void createNoteFlatNonFlattenable() { + Note note = Note.flat(0, Note.Tone.C); + assertFalse(note.isSharped()); + assertThat(note.getTone(), is(Note.Tone.B)); + } + + @Test + public void testFlatWrapping() { + Note note = Note.flat(1, Note.Tone.G); + assertTrue(note.isSharped()); + assertThat(note.getTone(), is(Note.Tone.F)); + } + + @Test + public void testFlatWrapping2() { + Note note = new Note(1, Note.Tone.G, false).flattened(); + assertTrue(note.isSharped()); + assertThat(note.getTone(), is(Note.Tone.F)); + } + + @Test + public void testSharpWrapping() { + Note note = new Note(1, Note.Tone.F, false).sharped(); + assertTrue(note.isSharped()); + assertThat(note.getTone(), is(Note.Tone.F)); + assertEquals(note.getOctave(), 2); + } + + @Test(expected=IllegalArgumentException.class) + public void testSharpWrapping2() { + new Note(2, Note.Tone.F, true).sharped(); + } + + @Test + public void testHighest() { + Note note = new Note(2, Note.Tone.F, true); + assertEquals(note.getId(), (byte)24); + } + + @Test + public void testLowest() { + Note note = new Note(0, Note.Tone.F, true); + assertEquals(note.getId(), (byte)0); + } + + @Test + public void doo() { + for (int i = 1; i <= 24; i++) { + Note note = new Note((byte) i); + int octave = note.getOctave(); + Note.Tone tone = note.getTone(); + boolean sharped = note.isSharped(); + + Note newNote = new Note(octave, tone, sharped); + assertThat(newNote, is(note)); + assertThat(newNote.getId(), is(note.getId())); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/TestServer.java b/vspigot-api/src/test/java/org/bukkit/TestServer.java new file mode 100644 index 0000000..2d84032 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/TestServer.java @@ -0,0 +1,100 @@ +package org.bukkit; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Map; +import java.util.logging.Logger; + +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.SimplePluginManager; + +import com.google.common.collect.ImmutableMap; + +public class TestServer implements InvocationHandler { + private static interface MethodHandler { + Object handle(TestServer server, Object[] args); + } + + private static final Map methods; + + static { + try { + ImmutableMap.Builder methodMap = ImmutableMap.builder(); + methodMap.put( + Server.class.getMethod("isPrimaryThread"), + new MethodHandler() { + public Object handle(TestServer server, Object[] args) { + return Thread.currentThread().equals(server.creatingThread); + } + } + ); + methodMap.put( + Server.class.getMethod("getPluginManager"), + new MethodHandler() { + public Object handle(TestServer server, Object[] args) { + return server.pluginManager; + } + } + ); + methodMap.put( + Server.class.getMethod("getLogger"), + new MethodHandler() { + final Logger logger = Logger.getLogger(TestServer.class.getCanonicalName()); + public Object handle(TestServer server, Object[] args) { + return logger; + } + } + ); + methodMap.put( + Server.class.getMethod("getName"), + new MethodHandler() { + public Object handle(TestServer server, Object[] args) { + return TestServer.class.getSimpleName(); + } + } + ); + methodMap.put( + Server.class.getMethod("getVersion"), + new MethodHandler() { + public Object handle(TestServer server, Object[] args) { + return "Version_" + TestServer.class.getPackage().getImplementationVersion(); + } + } + ); + methodMap.put( + Server.class.getMethod("getBukkitVersion"), + new MethodHandler() { + public Object handle(TestServer server, Object[] args) { + return "BukkitVersion_" + TestServer.class.getPackage().getImplementationVersion(); + } + } + ); + methods = methodMap.build(); + + TestServer server = new TestServer(); + Server instance = Proxy.getProxyClass(Server.class.getClassLoader(), Server.class).asSubclass(Server.class).getConstructor(InvocationHandler.class).newInstance(server); + Bukkit.setServer(instance); + server.pluginManager = new SimplePluginManager(instance, new SimpleCommandMap(instance)); + } catch (Throwable t) { + throw new Error(t); + } + } + + private Thread creatingThread = Thread.currentThread(); + private PluginManager pluginManager; + private TestServer() {}; + + public static Server getInstance() { + return Bukkit.getServer(); + } + + public Object invoke(Object proxy, Method method, Object[] args) { + MethodHandler handler = methods.get(method); + if (handler != null) { + return handler.handle(this, args); + } + throw new UnsupportedOperationException(String.valueOf(method)); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/TreeSpeciesTest.java b/vspigot-api/src/test/java/org/bukkit/TreeSpeciesTest.java new file mode 100644 index 0000000..4a99730 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/TreeSpeciesTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class TreeSpeciesTest { + @Test + public void getByData() { + for (TreeSpecies treeSpecies : TreeSpecies.values()) { + assertThat(TreeSpecies.getByData(treeSpecies.getData()), is(treeSpecies)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/WorldTypeTest.java b/vspigot-api/src/test/java/org/bukkit/WorldTypeTest.java new file mode 100644 index 0000000..d31c205 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/WorldTypeTest.java @@ -0,0 +1,15 @@ +package org.bukkit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class WorldTypeTest { + @Test + public void getByName() { + for (WorldType worldType : WorldType.values()) { + assertThat(WorldType.getByName(worldType.getName()), is(worldType)); + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java b/vspigot-api/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java new file mode 100644 index 0000000..6dab477 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/configuration/ConfigurationSectionTest.java @@ -0,0 +1,548 @@ +package org.bukkit.configuration; + +import org.bukkit.Material; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; +import org.junit.Test; +import static org.junit.Assert.*; + +public abstract class ConfigurationSectionTest { + public abstract ConfigurationSection getConfigurationSection(); + + @Test + public void testGetKeys() { + ConfigurationSection section = getConfigurationSection(); + + section.set("key", true); + section.set("subsection.subkey", true); + section.set("subsection.subkey2", true); + section.set("subsection.subsubsection.key", true); + section.set("key2", true); + section.set("42", true); + + assertArrayEquals(new String[] { "key", "subsection", "key2", "42" }, section.getKeys(false).toArray()); + assertArrayEquals(new String[] { "key", "subsection", "subsection.subkey", "subsection.subkey2", "subsection.subsubsection", "subsection.subsubsection.key", "key2", "42" }, section.getKeys(true).toArray()); + assertArrayEquals(new String[] { "subkey", "subkey2", "subsubsection", "subsubsection.key" }, section.getConfigurationSection("subsection").getKeys(true).toArray()); + } + + @Test + public void testGetKeysWithDefaults() { + ConfigurationSection section = getConfigurationSection(); + section.getRoot().options().copyDefaults(true); + + section.set("key", true); + section.addDefault("subsection.subkey", true); + section.addDefault("subsection.subkey2", true); + section.addDefault("subsection.subsubsection.key", true); + section.addDefault("key2", true); + + assertArrayEquals(new String[] { "subsection", "key2", "key" }, section.getKeys(false).toArray()); + assertArrayEquals(new String[] { "subsection", "subsection.subkey", "subsection.subkey2", "subsection.subsubsection", "subsection.subsubsection.key", "key2", "key" }, section.getKeys(true).toArray()); + assertArrayEquals(new String[] { "subkey", "subkey2", "subsubsection", "subsubsection.key" }, section.getConfigurationSection("subsection").getKeys(true).toArray()); + } + + @Test + public void testGetValues() { + ConfigurationSection section = getConfigurationSection(); + + section.set("bool", true); + section.set("subsection.string", "test"); + section.set("subsection.long", Long.MAX_VALUE); + section.set("int", 42); + + Map shallowValues = section.getValues(false); + assertArrayEquals(new String[] { "bool", "subsection", "int" }, shallowValues.keySet().toArray()); + assertArrayEquals(new Object[] { true, section.getConfigurationSection("subsection"), 42 }, shallowValues.values().toArray()); + + Map deepValues = section.getValues(true); + assertArrayEquals(new String[] { "bool", "subsection", "subsection.string", "subsection.long", "int" }, deepValues.keySet().toArray()); + assertArrayEquals(new Object[] { true, section.getConfigurationSection("subsection"), "test", Long.MAX_VALUE, 42 }, deepValues.values().toArray()); + } + + @Test + public void testGetValuesWithDefaults() { + ConfigurationSection section = getConfigurationSection(); + section.getRoot().options().copyDefaults(true); + + section.set("bool", true); + section.set("subsection.string", "test"); + section.addDefault("subsection.long", Long.MAX_VALUE); + section.addDefault("int", 42); + + Map shallowValues = section.getValues(false); + assertArrayEquals(new String[] { "subsection", "int", "bool" }, shallowValues.keySet().toArray()); + assertArrayEquals(new Object[] { section.getConfigurationSection("subsection"), 42, true }, shallowValues.values().toArray()); + + Map deepValues = section.getValues(true); + assertArrayEquals(new String[] { "subsection", "subsection.long", "int", "bool", "subsection.string" }, deepValues.keySet().toArray()); + assertArrayEquals(new Object[] { section.getConfigurationSection("subsection"), Long.MAX_VALUE, 42, true, "test" }, deepValues.values().toArray()); + } + + @Test + public void testContains() { + ConfigurationSection section = getConfigurationSection(); + + section.set("exists", true); + + assertTrue(section.contains("exists")); + assertFalse(section.contains("doesnt-exist")); + } + + @Test + public void testIsSet() { + ConfigurationSection section = getConfigurationSection(); + + section.set("notDefault", true); + section.getRoot().addDefault("default", true); + section.getRoot().addDefault("defaultAndSet", true); + section.set("defaultAndSet", true); + + assertTrue(section.isSet("notDefault")); + assertFalse(section.isSet("default")); + assertTrue(section.isSet("defaultAndSet")); + } + + @Test + public void testGetCurrentPath() { + ConfigurationSection section = getConfigurationSection(); + + assertEquals(section.getName(), section.getCurrentPath()); + } + + @Test + public void testGetName() { + ConfigurationSection section = getConfigurationSection().createSection("subsection"); + + assertEquals("subsection", section.getName()); + assertEquals("", section.getRoot().getName()); + } + + @Test + public void testGetRoot() { + ConfigurationSection section = getConfigurationSection(); + + assertNotNull(section.getRoot()); + assertTrue(section.getRoot().contains(section.getCurrentPath())); + } + + @Test + public void testGetParent() { + ConfigurationSection section = getConfigurationSection(); + ConfigurationSection subsection = section.createSection("subsection"); + + assertEquals(section.getRoot(), section.getParent()); + assertEquals(section, subsection.getParent()); + } + + @Test + public void testGet_String() { + ConfigurationSection section = getConfigurationSection(); + + section.set("exists", "hello world"); + + assertEquals("hello world", section.getString("exists")); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGet_String_Object() { + ConfigurationSection section = getConfigurationSection(); + + section.set("exists", "Set Value"); + + assertEquals("Set Value", section.get("exists", "Default Value")); + assertEquals("Default Value", section.get("doesntExist", "Default Value")); + } + + @Test + public void testSet() { + ConfigurationSection section = getConfigurationSection(); + + section.set("exists", "hello world"); + + assertTrue(section.contains("exists")); + assertTrue(section.isSet("exists")); + assertEquals("hello world", section.get("exists")); + + section.set("exists", null); + + assertFalse(section.contains("exists")); + assertFalse(section.isSet("exists")); + } + + @Test + public void testCreateSection() { + ConfigurationSection section = getConfigurationSection(); + ConfigurationSection subsection = section.createSection("subsection"); + + assertEquals("subsection", subsection.getName()); + } + + @Test + public void testSectionMap() { + ConfigurationSection config = getConfigurationSection(); + Map testMap = new LinkedHashMap(); + + testMap.put("string", "Hello World"); + testMap.put("integer", 15); + + config.createSection("test.path", testMap); + + assertEquals(testMap, config.getConfigurationSection("test.path").getValues(false)); + } + + @Test + public void testGetString_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + String value = "Hello World"; + + section.set(key, value); + + assertEquals(value, section.getString(key)); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetString_String_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + String value = "Hello World"; + String def = "Default Value"; + + section.set(key, value); + + assertEquals(value, section.getString(key, def)); + assertEquals(def, section.getString("doesntExist", def)); + } + + @Test + public void testIsString() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + String value = "Hello World"; + + section.set(key, value); + + assertTrue(section.isString(key)); + assertFalse(section.isString("doesntExist")); + } + + @Test + public void testGetInt_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + int value = Integer.MAX_VALUE; + + section.set(key, value); + + assertEquals(value, section.getInt(key)); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetInt_String_Int() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + int value = Integer.MAX_VALUE; + int def = Integer.MIN_VALUE; + + section.set(key, value); + + assertEquals(value, section.getInt(key, def)); + assertEquals(def, section.getInt("doesntExist", def)); + } + + @Test + public void testIsInt() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + int value = Integer.MAX_VALUE; + + section.set(key, value); + + assertTrue(section.isInt(key)); + assertFalse(section.isInt("doesntExist")); + } + + @Test + public void testGetBoolean_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + boolean value = true; + + section.set(key, value); + + assertEquals(value, section.getBoolean(key)); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetBoolean_String_Boolean() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + boolean value = true; + boolean def = false; + + section.set(key, value); + + assertEquals(value, section.getBoolean(key, def)); + assertEquals(def, section.getBoolean("doesntExist", def)); + } + + @Test + public void testIsBoolean() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + boolean value = true; + + section.set(key, value); + + assertTrue(section.isBoolean(key)); + assertFalse(section.isBoolean("doesntExist")); + } + + @Test + public void testGetDouble_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + double value = Double.MAX_VALUE; + + section.set(key, value); + + assertEquals(value, section.getDouble(key), 1); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetDoubleFromInt() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + double value = 123; + + section.set(key, (int) value); + + assertEquals(value, section.getDouble(key), 1); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetDouble_String_Double() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + double value = Double.MAX_VALUE; + double def = Double.MIN_VALUE; + + section.set(key, value); + + assertEquals(value, section.getDouble(key, def), 1); + assertEquals(def, section.getDouble("doesntExist", def), 1); + } + + @Test + public void testIsDouble() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + double value = Double.MAX_VALUE; + + section.set(key, value); + + assertTrue(section.isDouble(key)); + assertFalse(section.isDouble("doesntExist")); + } + + @Test + public void testGetLong_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + long value = Long.MAX_VALUE; + + section.set(key, value); + + assertEquals(value, section.getLong(key)); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetLong_String_Long() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + long value = Long.MAX_VALUE; + long def = Long.MIN_VALUE; + + section.set(key, value); + + assertEquals(value, section.getLong(key, def)); + assertEquals(def, section.getLong("doesntExist", def)); + } + + @Test + public void testIsLong() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + long value = Long.MAX_VALUE; + + section.set(key, value); + + assertTrue(section.isLong(key)); + assertFalse(section.isLong("doesntExist")); + } + + @Test + public void testGetList_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + Map map = new HashMap(); + + map.put("one", 1); + map.put("two", "two"); + map.put("three", 3.14); + + List value = Arrays.asList("One", "Two", "Three", 4, "5", 6.0, true, "false", map); + + section.set(key, value); + + assertEquals(value, section.getList(key)); + assertEquals(Arrays.asList((Object) "One", "Two", "Three", "4", "5", "6.0", "true", "false"), section.getStringList(key)); + assertEquals(Arrays.asList((Object) 4, 5, 6), section.getIntegerList(key)); + assertEquals(Arrays.asList((Object) true, false), section.getBooleanList(key)); + assertEquals(Arrays.asList((Object) 4.0, 5.0, 6.0), section.getDoubleList(key)); + assertEquals(Arrays.asList((Object) 4.0f, 5.0f, 6.0f), section.getFloatList(key)); + assertEquals(Arrays.asList((Object) 4l, 5l, 6l), section.getLongList(key)); + assertEquals(Arrays.asList((Object) (byte) 4, (byte) 5, (byte) 6), section.getByteList(key)); + assertEquals(Arrays.asList((Object) (short) 4, (short) 5, (short) 6), section.getShortList(key)); + assertEquals(map, section.getMapList(key).get(0)); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetList_String_List() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + List value = Arrays.asList("One", "Two", "Three"); + List def = Arrays.asList("A", "B", "C"); + + section.set(key, value); + + assertEquals(value, section.getList(key, def)); + assertEquals(def, section.getList("doesntExist", def)); + } + + @Test + public void testIsList() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + List value = Arrays.asList("One", "Two", "Three"); + + section.set(key, value); + + assertTrue(section.isList(key)); + assertFalse(section.isList("doesntExist")); + } + + @Test + public void testGetVector_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + Vector value = new Vector(Double.MIN_VALUE, Double.MAX_VALUE, 5); + + section.set(key, value); + + assertEquals(value, section.getVector(key)); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetVector_String_Vector() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + Vector value = new Vector(Double.MIN_VALUE, Double.MAX_VALUE, 5); + Vector def = new Vector(100, Double.MIN_VALUE, Double.MAX_VALUE); + + section.set(key, value); + + assertEquals(value, section.getVector(key, def)); + assertEquals(def, section.getVector("doesntExist", def)); + } + + @Test + public void testIsVector() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + Vector value = new Vector(Double.MIN_VALUE, Double.MAX_VALUE, 5); + + section.set(key, value); + + assertTrue(section.isVector(key)); + assertFalse(section.isVector("doesntExist")); + } + + @Test + public void testGetItemStack_String() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + ItemStack value = new ItemStack(Material.WOOD, 50, (short) 2); + + section.set(key, value); + + assertEquals(value, section.getItemStack(key)); + assertNull(section.getString("doesntExist")); + } + + @Test + public void testGetItemStack_String_ItemStack() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + ItemStack value = new ItemStack(Material.WOOD, 50, (short) 2); + ItemStack def = new ItemStack(Material.STONE, 1); + + section.set(key, value); + + assertEquals(value, section.getItemStack(key, def)); + assertEquals(def, section.getItemStack("doesntExist", def)); + } + + @Test + public void testIsItemStack() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + ItemStack value = new ItemStack(Material.WOOD, 50, (short) 2); + + section.set(key, value); + + assertTrue(section.isItemStack(key)); + assertFalse(section.isItemStack("doesntExist")); + } + + @Test + public void testGetConfigurationSection() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + + ConfigurationSection subsection = section.createSection(key); + + assertEquals(subsection, section.getConfigurationSection(key)); + } + + @Test + public void testIsConfigurationSection() { + ConfigurationSection section = getConfigurationSection(); + String key = "exists"; + + section.createSection(key); + + assertTrue(section.isConfigurationSection(key)); + assertFalse(section.isConfigurationSection("doesntExist")); + } + + public enum TestEnum { + HELLO, + WORLD, + BANANAS + } +} \ No newline at end of file diff --git a/vspigot-api/src/test/java/org/bukkit/configuration/ConfigurationTest.java b/vspigot-api/src/test/java/org/bukkit/configuration/ConfigurationTest.java new file mode 100644 index 0000000..e187d15 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/configuration/ConfigurationTest.java @@ -0,0 +1,156 @@ +package org.bukkit.configuration; + +import java.util.LinkedHashMap; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.bukkit.util.Vector; +import org.junit.Test; +import static org.junit.Assert.*; + +public abstract class ConfigurationTest { + public abstract Configuration getConfig(); + + public Map getTestValues() { + HashMap result = new LinkedHashMap(); + + result.put("integer", Integer.MIN_VALUE); + result.put("string", "String Value"); + result.put("long", Long.MAX_VALUE); + result.put("true-boolean", true); + result.put("false-boolean", false); + result.put("vector", new Vector(12345.67, 64, -12345.6789)); + result.put("list", Arrays.asList(1, 2, 3, 4, 5)); + result.put("42", "The Answer"); + + return result; + } + + /** + * Test of addDefault method, of class Configuration. + */ + @Test + public void testAddDefault() { + Configuration config = getConfig(); + Map values = getTestValues(); + + for (Map.Entry entry : values.entrySet()) { + String path = entry.getKey(); + Object object = entry.getValue(); + + config.addDefault(path, object); + + assertEquals(object, config.get(path)); + assertTrue(config.contains(path)); + assertFalse(config.isSet(path)); + assertTrue(config.getDefaults().isSet(path)); + } + } + + /** + * Test of addDefaults method, of class Configuration. + */ + @Test + public void testAddDefaults_Map() { + Configuration config = getConfig(); + Map values = getTestValues(); + + config.addDefaults(values); + + for (Map.Entry entry : values.entrySet()) { + String path = entry.getKey(); + Object object = entry.getValue(); + + assertEquals(object, config.get(path)); + assertTrue(config.contains(path)); + assertFalse(config.isSet(path)); + assertTrue(config.getDefaults().isSet(path)); + } + } + + /** + * Test of addDefaults method, of class Configuration. + */ + @Test + public void testAddDefaults_Configuration() { + Configuration config = getConfig(); + Map values = getTestValues(); + Configuration defaults = getConfig(); + + for (Map.Entry entry : values.entrySet()) { + defaults.set(entry.getKey(), entry.getValue()); + } + + config.addDefaults(defaults); + + for (Map.Entry entry : values.entrySet()) { + String path = entry.getKey(); + Object object = entry.getValue(); + + assertEquals(object, config.get(path)); + assertTrue(config.contains(path)); + assertFalse(config.isSet(path)); + assertTrue(config.getDefaults().isSet(path)); + } + } + + /** + * Test of setDefaults method, of class Configuration. + */ + @Test + public void testSetDefaults() { + Configuration config = getConfig(); + Map values = getTestValues(); + Configuration defaults = getConfig(); + + for (Map.Entry entry : values.entrySet()) { + defaults.set(entry.getKey(), entry.getValue()); + } + + config.setDefaults(defaults); + + for (Map.Entry entry : values.entrySet()) { + String path = entry.getKey(); + Object object = entry.getValue(); + + assertEquals(object, config.get(path)); + assertTrue(config.contains(path)); + assertFalse(config.isSet(path)); + assertTrue(config.getDefaults().isSet(path)); + } + } + + /** + * Test creation of ConfigurationSection + */ + @Test + public void testCreateSection() { + Configuration config = getConfig(); + + Set set = new HashSet(); + set.add("this"); + set.add("this.test.sub"); + set.add("this.test"); + set.add("this.test.other"); + + config.createSection("this.test.sub"); + config.createSection("this.test.other"); + + assertEquals(set, config.getKeys(true)); + } + + /** + * Test of getDefaults method, of class Configuration. + */ + @Test + public void testGetDefaults() { + Configuration config = getConfig(); + Configuration defaults = getConfig(); + + config.setDefaults(defaults); + + assertEquals(defaults, config.getDefaults()); + } +} \ No newline at end of file diff --git a/vspigot-api/src/test/java/org/bukkit/configuration/MemoryConfigurationTest.java b/vspigot-api/src/test/java/org/bukkit/configuration/MemoryConfigurationTest.java new file mode 100644 index 0000000..3de0ce9 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/configuration/MemoryConfigurationTest.java @@ -0,0 +1,8 @@ +package org.bukkit.configuration; + +public class MemoryConfigurationTest extends ConfigurationTest { + @Override + public Configuration getConfig() { + return new MemoryConfiguration(); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/configuration/MemorySectionTest.java b/vspigot-api/src/test/java/org/bukkit/configuration/MemorySectionTest.java new file mode 100644 index 0000000..be7768a --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/configuration/MemorySectionTest.java @@ -0,0 +1,8 @@ +package org.bukkit.configuration; + +public class MemorySectionTest extends ConfigurationSectionTest { + @Override + public ConfigurationSection getConfigurationSection() { + return new MemoryConfiguration().createSection("section"); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/configuration/file/FileConfigurationTest.java b/vspigot-api/src/test/java/org/bukkit/configuration/file/FileConfigurationTest.java new file mode 100644 index 0000000..ce0c2e5 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/configuration/file/FileConfigurationTest.java @@ -0,0 +1,209 @@ +package org.bukkit.configuration.file; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.util.Map; +import org.bukkit.configuration.MemoryConfigurationTest; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import static org.junit.Assert.*; + +public abstract class FileConfigurationTest extends MemoryConfigurationTest { + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Override + public abstract FileConfiguration getConfig(); + + public abstract String getTestValuesString(); + + public abstract String getTestHeaderInput(); + + public abstract String getTestHeaderResult(); + + @Test + public void testSave_File() throws Exception { + FileConfiguration config = getConfig(); + File file = testFolder.newFile("test.config"); + + for (Map.Entry entry : getTestValues().entrySet()) { + config.set(entry.getKey(), entry.getValue()); + } + + config.save(file); + + assertTrue(file.isFile()); + } + + @Test + public void testSave_String() throws Exception { + FileConfiguration config = getConfig(); + File file = testFolder.newFile("test.config"); + + for (Map.Entry entry : getTestValues().entrySet()) { + config.set(entry.getKey(), entry.getValue()); + } + + config.save(file.getAbsolutePath()); + + assertTrue(file.isFile()); + } + + @Test + public void testSaveToString() { + FileConfiguration config = getConfig(); + + for (Map.Entry entry : getTestValues().entrySet()) { + config.set(entry.getKey(), entry.getValue()); + } + + String result = config.saveToString(); + String expected = getTestValuesString(); + + assertEquals(expected, result); + } + + @Test + public void testLoad_File() throws Exception { + FileConfiguration config = getConfig(); + File file = testFolder.newFile("test.config"); + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + String saved = getTestValuesString(); + Map values = getTestValues(); + + try { + writer.write(saved); + } finally { + writer.close(); + } + + config.load(file); + + for (Map.Entry entry : values.entrySet()) { + assertEquals(entry.getValue(), config.get(entry.getKey())); + } + + assertEquals(values.keySet(), config.getKeys(true)); + } + + @Test + public void testLoad_String() throws Exception { + FileConfiguration config = getConfig(); + File file = testFolder.newFile("test.config"); + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + String saved = getTestValuesString(); + Map values = getTestValues(); + + try { + writer.write(saved); + } finally { + writer.close(); + } + + config.load(file.getAbsolutePath()); + + for (Map.Entry entry : values.entrySet()) { + assertEquals(entry.getValue(), config.get(entry.getKey())); + } + + assertEquals(values.keySet(), config.getKeys(true)); + } + + @Test + public void testLoadFromString() throws Exception { + FileConfiguration config = getConfig(); + Map values = getTestValues(); + String saved = getTestValuesString(); + + config.loadFromString(saved); + + for (Map.Entry entry : values.entrySet()) { + assertEquals(entry.getValue(), config.get(entry.getKey())); + } + + assertEquals(values.keySet(), config.getKeys(true)); + assertEquals(saved, config.saveToString()); + } + + @Test + public void testSaveToStringWithHeader() { + FileConfiguration config = getConfig(); + config.options().header(getTestHeaderInput()); + + for (Map.Entry entry : getTestValues().entrySet()) { + config.set(entry.getKey(), entry.getValue()); + } + + String result = config.saveToString(); + String expected = getTestHeaderResult() + "\n" + getTestValuesString(); + + assertEquals(expected, result); + } + + @Test + public void testParseHeader() throws Exception { + FileConfiguration config = getConfig(); + Map values = getTestValues(); + String saved = getTestValuesString(); + String header = getTestHeaderResult(); + String expected = getTestHeaderInput(); + + config.loadFromString(header + "\n" + saved); + + assertEquals(expected, config.options().header()); + + for (Map.Entry entry : values.entrySet()) { + assertEquals(entry.getValue(), config.get(entry.getKey())); + } + + assertEquals(values.keySet(), config.getKeys(true)); + assertEquals(header + "\n" + saved, config.saveToString()); + } + + @Test + public void testCopyHeader() throws Exception { + FileConfiguration config = getConfig(); + FileConfiguration defaults = getConfig(); + Map values = getTestValues(); + String saved = getTestValuesString(); + String header = getTestHeaderResult(); + String expected = getTestHeaderInput(); + + defaults.loadFromString(header); + config.loadFromString(saved); + config.setDefaults(defaults); + + assertNull(config.options().header()); + assertEquals(expected, defaults.options().header()); + + for (Map.Entry entry : values.entrySet()) { + assertEquals(entry.getValue(), config.get(entry.getKey())); + } + + assertEquals(values.keySet(), config.getKeys(true)); + assertEquals(header + "\n" + saved, config.saveToString()); + + config = getConfig(); + config.loadFromString(getTestHeaderResult() + saved); + assertEquals(getTestHeaderResult() + saved, config.saveToString()); + } + + @Test + public void testReloadEmptyConfig() throws Exception { + FileConfiguration config = getConfig(); + + assertEquals("", config.saveToString()); + + config = getConfig(); + config.loadFromString(""); + + assertEquals("", config.saveToString()); + + config = getConfig(); + config.loadFromString("\n\n"); // Should trim the first newlines of a header + + assertEquals("", config.saveToString()); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java b/vspigot-api/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java new file mode 100644 index 0000000..aa83af3 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java @@ -0,0 +1,56 @@ +package org.bukkit.configuration.file; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class YamlConfigurationTest extends FileConfigurationTest { + + @Override + public YamlConfiguration getConfig() { + return new YamlConfiguration(); + } + + @Override + public String getTestHeaderInput() { + return "This is a sample\nheader.\n\nNewline above should be commented.\n\n"; + } + + @Override + public String getTestHeaderResult() { + return "# This is a sample\n# header.\n# \n# Newline above should be commented.\n\n"; + } + + @Override + public String getTestValuesString() { + return "integer: -2147483648\n" + + "string: String Value\n" + + "long: 9223372036854775807\n" + + "true-boolean: true\n" + + "false-boolean: false\n" + + "vector:\n" + + " ==: Vector\n" + + " x: 12345.67\n" + + " y: 64.0\n" + + " z: -12345.6789\n" + + "list:\n" + + "- 1\n" + + "- 2\n" + + "- 3\n" + + "- 4\n" + + "- 5\n" + + "'42': The Answer\n"; + } + + @Test + public void testSaveToStringWithIndent() { + YamlConfiguration config = getConfig(); + config.options().indent(9); + + config.set("section.key", 1); + + String result = config.saveToString(); + String expected = "section:\n key: 1\n"; + + assertEquals(expected, result); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/conversations/ConversationContextTest.java b/vspigot-api/src/test/java/org/bukkit/conversations/ConversationContextTest.java new file mode 100644 index 0000000..dfc462b --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/conversations/ConversationContextTest.java @@ -0,0 +1,34 @@ +package org.bukkit.conversations; + +import org.junit.Test; +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.Map; + +/** + */ +public class ConversationContextTest { + @Test + public void TestFromWhom() { + Conversable conversable = new FakeConversable(); + ConversationContext context = new ConversationContext(null, conversable, new HashMap()); + assertEquals(conversable, context.getForWhom()); + } + + @Test + public void TestPlugin() { + Conversable conversable = new FakeConversable(); + ConversationContext context = new ConversationContext(null, conversable, new HashMap()); + assertEquals(null, context.getPlugin()); + } + + @Test + public void TestSessionData() { + Conversable conversable = new FakeConversable(); + Map session = new HashMap(); + session.put("key", "value"); + ConversationContext context = new ConversationContext(null, conversable, session); + assertEquals("value", context.getSessionData("key")); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/conversations/ConversationTest.java b/vspigot-api/src/test/java/org/bukkit/conversations/ConversationTest.java new file mode 100644 index 0000000..732caab --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/conversations/ConversationTest.java @@ -0,0 +1,116 @@ +package org.bukkit.conversations; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + */ +public class ConversationTest { + + @Test + public void testBaseConversationFlow() { + FakeConversable forWhom = new FakeConversable(); + Conversation conversation = new Conversation(null, forWhom, new FirstPrompt()); + + // Conversation not yet begun + assertNull(forWhom.lastSentMessage); + assertEquals(conversation.getForWhom(), forWhom); + assertTrue(conversation.isModal()); + + // Begin the conversation + conversation.begin(); + assertEquals("FirstPrompt", forWhom.lastSentMessage); + assertEquals(conversation, forWhom.begunConversation); + + // Send the first input + conversation.acceptInput("FirstInput"); + assertEquals("SecondPrompt", forWhom.lastSentMessage); + assertEquals(conversation, forWhom.abandonedConverstion); + } + + @Test + public void testConversationFactory() { + FakeConversable forWhom = new FakeConversable(); + NullConversationPrefix prefix = new NullConversationPrefix(); + ConversationFactory factory = new ConversationFactory(null) + .withFirstPrompt(new FirstPrompt()) + .withModality(false) + .withPrefix(prefix); + Conversation conversation = factory.buildConversation(forWhom); + + // Conversation not yet begun + assertNull(forWhom.lastSentMessage); + assertEquals(conversation.getForWhom(), forWhom); + assertFalse(conversation.isModal()); + assertEquals(conversation.getPrefix(), prefix); + + // Begin the conversation + conversation.begin(); + assertEquals("FirstPrompt", forWhom.lastSentMessage); + assertEquals(conversation, forWhom.begunConversation); + + // Send the first input + conversation.acceptInput("FirstInput"); + assertEquals("SecondPrompt", forWhom.lastSentMessage); + assertEquals(conversation, forWhom.abandonedConverstion); + } + + @Test + public void testEscapeSequence() { + FakeConversable forWhom = new FakeConversable(); + Conversation conversation = new Conversation(null, forWhom, new FirstPrompt()); + conversation.addConversationCanceller(new ExactMatchConversationCanceller("bananas")); + + // Begin the conversation + conversation.begin(); + assertEquals("FirstPrompt", forWhom.lastSentMessage); + assertEquals(conversation, forWhom.begunConversation); + + // Send the first input + conversation.acceptInput("bananas"); + assertEquals("bananas", forWhom.lastSentMessage); + assertEquals(conversation, forWhom.abandonedConverstion); + } + + @Test + public void testNotPlayer() { + FakeConversable forWhom = new FakeConversable(); + NullConversationPrefix prefix = new NullConversationPrefix(); + ConversationFactory factory = new ConversationFactory(null) + .thatExcludesNonPlayersWithMessage("bye"); + Conversation conversation = factory.buildConversation(forWhom); + + // Begin the conversation + conversation.begin(); + assertEquals("bye", forWhom.lastSentMessage); + assertEquals(conversation, forWhom.begunConversation); + assertEquals(conversation, forWhom.abandonedConverstion); + } + + private class FirstPrompt extends StringPrompt { + + public String getPromptText(ConversationContext context) { + return "FirstPrompt"; + } + + public Prompt acceptInput(ConversationContext context, String input) { + assertEquals("FirstInput", input); + context.setSessionData("data", 10); + return new SecondPrompt(); + } + } + + private class SecondPrompt extends MessagePrompt { + + @Override + protected Prompt getNextPrompt(ConversationContext context) { + return Prompt.END_OF_CONVERSATION; + } + + public String getPromptText(ConversationContext context) { + // Assert that session data passes from one prompt to the next + assertEquals(context.getSessionData("data"), 10); + return "SecondPrompt"; + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/conversations/FakeConversable.java b/vspigot-api/src/test/java/org/bukkit/conversations/FakeConversable.java new file mode 100644 index 0000000..87fb311 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/conversations/FakeConversable.java @@ -0,0 +1,105 @@ +package org.bukkit.conversations; + +import org.bukkit.Server; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +import java.util.Set; + +/** + */ +public class FakeConversable implements Conversable { + public String lastSentMessage; + public Conversation begunConversation; + public Conversation abandonedConverstion; + public ConversationAbandonedEvent abandonedConversationEvent; + + public boolean isConversing() { + return false; + } + + public void acceptConversationInput(String input) { + + } + + public boolean beginConversation(Conversation conversation) { + begunConversation = conversation; + conversation.outputNextPrompt(); + return true; + } + + public void abandonConversation(Conversation conversation) { + abandonedConverstion = conversation; + } + + public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) { + abandonedConverstion = conversation; + abandonedConversationEvent = details; + } + + public void sendRawMessage(String message) { + lastSentMessage = message; + } + + public Server getServer() { + return null; + } + + public String getName() { + return null; + } + + public boolean isPermissionSet(String name) { + return false; + } + + public boolean isPermissionSet(Permission perm) { + return false; + } + + public boolean hasPermission(String name) { + return false; + } + + public boolean hasPermission(Permission perm) { + return false; + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return null; + } + + public PermissionAttachment addAttachment(Plugin plugin) { + return null; + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return null; + } + + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return null; + } + + public void removeAttachment(PermissionAttachment attachment) { + + } + + public void recalculatePermissions() { + + } + + public Set getEffectivePermissions() { + return null; + } + + public boolean isOp() { + return false; + } + + public void setOp(boolean value) { + + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/conversations/ValidatingPromptTest.java b/vspigot-api/src/test/java/org/bukkit/conversations/ValidatingPromptTest.java new file mode 100644 index 0000000..d1c0f42 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/conversations/ValidatingPromptTest.java @@ -0,0 +1,115 @@ +package org.bukkit.conversations; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + */ +public class ValidatingPromptTest { + + @Test + public void TestBooleanPrompt() { + TestBooleanPrompt prompt = new TestBooleanPrompt(); + assertTrue(prompt.isInputValid(null, "true")); + assertFalse(prompt.isInputValid(null, "bananas")); + prompt.acceptInput(null, "true"); + assertTrue(prompt.result); + prompt.acceptInput(null, "no"); + assertFalse(prompt.result); + } + + @Test + public void TestFixedSetPrompt() { + TestFixedSetPrompt prompt = new TestFixedSetPrompt("foo", "bar"); + assertTrue(prompt.isInputValid(null, "foo")); + assertFalse(prompt.isInputValid(null, "cheese")); + prompt.acceptInput(null, "foo"); + assertEquals("foo", prompt.result); + } + + @Test + public void TestNumericPrompt() { + TestNumericPrompt prompt = new TestNumericPrompt(); + assertTrue(prompt.isInputValid(null, "1010220")); + assertFalse(prompt.isInputValid(null, "tomato")); + prompt.acceptInput(null, "1010220"); + assertEquals(1010220, prompt.result); + } + + @Test + public void TestRegexPrompt() { + TestRegexPrompt prompt = new TestRegexPrompt("a.c"); + assertTrue(prompt.isInputValid(null, "abc")); + assertTrue(prompt.isInputValid(null, "axc")); + assertFalse(prompt.isInputValid(null, "xyz")); + prompt.acceptInput(null, "abc"); + assertEquals("abc", prompt.result); + } + + //TODO: TestPlayerNamePrompt() + + private class TestBooleanPrompt extends BooleanPrompt { + public boolean result; + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, boolean input) { + result = input; + return null; + } + + public String getPromptText(ConversationContext context) { + return null; + } + } + + private class TestFixedSetPrompt extends FixedSetPrompt { + public String result; + + public TestFixedSetPrompt(String... fixedSet) { + super(fixedSet); + } + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, String input) { + result = input; + return null; + } + + public String getPromptText(ConversationContext context) { + return null; + } + } + + private class TestNumericPrompt extends NumericPrompt { + public Number result; + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, Number input) { + result = input; + return null; + } + + public String getPromptText(ConversationContext context) { + return null; + } + } + + private class TestRegexPrompt extends RegexPrompt { + public String result; + + public TestRegexPrompt(String pattern) { + super(pattern); + } + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, String input) { + result = input; + return null; + } + + public String getPromptText(ConversationContext context) { + return null; + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/event/PlayerChatTabCompleteEventTest.java b/vspigot-api/src/test/java/org/bukkit/event/PlayerChatTabCompleteEventTest.java new file mode 100644 index 0000000..619bf30 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/event/PlayerChatTabCompleteEventTest.java @@ -0,0 +1,28 @@ +package org.bukkit.event; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.bukkit.event.player.PlayerChatTabCompleteEvent; +import org.bukkit.plugin.messaging.TestPlayer; +import org.junit.Test; + +import com.google.common.collect.ImmutableList; + +public class PlayerChatTabCompleteEventTest { + + @Test + public void testGetLastToken() { + assertThat(getToken("Hello everyone!"), is("everyone!")); + assertThat(getToken(" welcome to the show..."), is("show...")); + assertThat(getToken("The whitespace is here "), is("")); + assertThat(getToken("Too much whitespace is here "), is("")); + assertThat(getToken("The_whitespace_is_missing"), is("The_whitespace_is_missing")); + assertThat(getToken(""), is("")); + assertThat(getToken(" "), is("")); + } + + private String getToken(String message) { + return new PlayerChatTabCompleteEvent(TestPlayer.getInstance(), message, ImmutableList.of()).getLastToken(); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/event/TestEvent.java b/vspigot-api/src/test/java/org/bukkit/event/TestEvent.java new file mode 100644 index 0000000..25904f5 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/event/TestEvent.java @@ -0,0 +1,19 @@ +package org.bukkit.event; + + +public class TestEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + + public TestEvent(boolean async) { + super(async); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java b/vspigot-api/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java new file mode 100644 index 0000000..5583b27 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java @@ -0,0 +1,42 @@ +package org.bukkit.metadata; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.TestPlugin; +import org.junit.Test; + +public class FixedMetadataValueTest { + private Plugin plugin = new TestPlugin("X"); + private FixedMetadataValue subject; + + @Test + public void testBasic() { + subject = new FixedMetadataValue(plugin, new Integer(50)); + assertSame(plugin, subject.getOwningPlugin()); + assertEquals(new Integer(50), subject.value()); + } + + @Test + public void testNumberTypes() { + subject = new FixedMetadataValue(plugin, new Integer(5)); + assertEquals(new Integer(5), subject.value()); + assertEquals(5, subject.asInt()); + assertEquals(true, subject.asBoolean()); + assertEquals(5, subject.asByte()); + assertEquals(5.0, subject.asFloat(), 0.1e-8); + assertEquals(5.0D, subject.asDouble(), 0.1e-8D); + assertEquals(5L, subject.asLong()); + assertEquals(5, subject.asShort()); + assertEquals("5", subject.asString()); + } + + @Test + public void testInvalidateDoesNothing() { + Object o = new Object(); + subject = new FixedMetadataValue(plugin, o); + subject.invalidate(); + assertSame(o, subject.value()); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/metadata/LazyMetadataValueTest.java b/vspigot-api/src/test/java/org/bukkit/metadata/LazyMetadataValueTest.java new file mode 100644 index 0000000..a3172db --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/metadata/LazyMetadataValueTest.java @@ -0,0 +1,135 @@ +package org.bukkit.metadata; + +import org.bukkit.plugin.TestPlugin; +import org.junit.Test; + +import java.util.concurrent.Callable; + +import static org.junit.Assert.*; + +public class LazyMetadataValueTest { + private LazyMetadataValue subject; + private TestPlugin plugin = new TestPlugin("x"); + + @Test + public void testLazyInt() { + int value = 10; + subject = makeSimpleCallable(value); + + assertEquals(value, subject.value()); + } + + @Test + public void testLazyDouble() { + double value = 10.5; + subject = makeSimpleCallable(value); + + assertEquals(value, (Double)subject.value(), 0.01); + } + + @Test + public void testLazyString() { + String value = "TEN"; + subject = makeSimpleCallable(value); + + assertEquals(value, subject.value()); + } + + @Test + public void testLazyBoolean() { + boolean value = false; + subject = makeSimpleCallable(value); + + assertEquals(value, subject.value()); + } + + @Test(expected=MetadataEvaluationException.class) + public void testEvalException() { + subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.CACHE_AFTER_FIRST_EVAL, new Callable() { + public Object call() throws Exception { + throw new RuntimeException("Gotcha!"); + } + }); + subject.value(); + } + + @Test + public void testCacheStrategyCacheAfterFirstEval() { + final Counter counter = new Counter(); + final int value = 10; + subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.CACHE_AFTER_FIRST_EVAL, new Callable() { + public Object call() throws Exception { + counter.increment(); + return value; + } + }); + + subject.value(); + subject.value(); + assertEquals(value, subject.value()); + assertEquals(1, counter.value()); + + subject.invalidate(); + subject.value(); + assertEquals(2, counter.value()); + } + + @Test + public void testCacheStrategyNeverCache() { + final Counter counter = new Counter(); + final int value = 10; + subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.NEVER_CACHE, new Callable() { + public Object call() throws Exception { + counter.increment(); + return value; + } + }); + + subject.value(); + subject.value(); + assertEquals(value, subject.value()); + assertEquals(3, counter.value()); + } + + @Test + public void testCacheStrategyEternally() { + final Counter counter = new Counter(); + final int value = 10; + subject = new LazyMetadataValue(plugin, LazyMetadataValue.CacheStrategy.CACHE_ETERNALLY, new Callable() { + public Object call() throws Exception { + counter.increment(); + return value; + } + }); + + subject.value(); + subject.value(); + assertEquals(value, subject.value()); + assertEquals(1, counter.value()); + + subject.invalidate(); + subject.value(); + assertEquals(value, subject.value()); + assertEquals(1, counter.value()); + } + + private LazyMetadataValue makeSimpleCallable(final Object value) { + return new LazyMetadataValue(plugin, new Callable() { + public Object call() throws Exception { + return value; + } + }); + } + + private class Counter { + private int c = 0; + + public void increment() { + c++; + } + + public int value() { + return c; + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/metadata/MetadataConversionTest.java b/vspigot-api/src/test/java/org/bukkit/metadata/MetadataConversionTest.java new file mode 100644 index 0000000..a595cc8 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/metadata/MetadataConversionTest.java @@ -0,0 +1,103 @@ +// Copyright (C) 2011 Ryan Michela +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package org.bukkit.metadata; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.TestPlugin; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + */ +public class MetadataConversionTest { + private Plugin plugin = new TestPlugin("x"); + private FixedMetadataValue subject; + + private void setSubject(Object value) { + subject = new FixedMetadataValue(plugin, value); + } + + @Test + public void testFromInt() { + setSubject(10); + + assertEquals(10, subject.asInt()); + assertEquals(10, subject.asFloat(), 0.000001); + assertEquals(10, subject.asDouble(), 0.000001); + assertEquals(10, subject.asLong()); + assertEquals(10, subject.asShort()); + assertEquals(10, subject.asByte()); + assertEquals(true, subject.asBoolean()); + assertEquals("10", subject.asString()); + } + + @Test + public void testFromFloat() { + setSubject(10.5); + + assertEquals(10, subject.asInt()); + assertEquals(10.5, subject.asFloat(), 0.000001); + assertEquals(10.5, subject.asDouble(), 0.000001); + assertEquals(10, subject.asLong()); + assertEquals(10, subject.asShort()); + assertEquals(10, subject.asByte()); + assertEquals(true, subject.asBoolean()); + assertEquals("10.5", subject.asString()); + } + + @Test + public void testFromNumericString() { + setSubject("10"); + + assertEquals(10, subject.asInt()); + assertEquals(10, subject.asFloat(), 0.000001); + assertEquals(10, subject.asDouble(), 0.000001); + assertEquals(10, subject.asLong()); + assertEquals(10, subject.asShort()); + assertEquals(10, subject.asByte()); + assertEquals(false, subject.asBoolean()); + assertEquals("10", subject.asString()); + } + + @Test + public void testFromNonNumericString() { + setSubject("true"); + + assertEquals(0, subject.asInt()); + assertEquals(0, subject.asFloat(), 0.000001); + assertEquals(0, subject.asDouble(), 0.000001); + assertEquals(0, subject.asLong()); + assertEquals(0, subject.asShort()); + assertEquals(0, subject.asByte()); + assertEquals(true, subject.asBoolean()); + assertEquals("true", subject.asString()); + } + + @Test + public void testFromNull() { + setSubject(null); + + assertEquals(0, subject.asInt()); + assertEquals(0, subject.asFloat(), 0.000001); + assertEquals(0, subject.asDouble(), 0.000001); + assertEquals(0, subject.asLong()); + assertEquals(0, subject.asShort()); + assertEquals(0, subject.asByte()); + assertEquals(false, subject.asBoolean()); + assertEquals("", subject.asString()); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/metadata/MetadataStoreTest.java b/vspigot-api/src/test/java/org/bukkit/metadata/MetadataStoreTest.java new file mode 100644 index 0000000..30f0368 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/metadata/MetadataStoreTest.java @@ -0,0 +1,143 @@ +package org.bukkit.metadata; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.concurrent.Callable; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.TestPlugin; +import org.junit.Test; + +public class MetadataStoreTest { + private Plugin pluginX = new TestPlugin("x"); + private Plugin pluginY = new TestPlugin("y"); + + StringMetadataStore subject = new StringMetadataStore(); + + @Test + public void testMetadataStore() { + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10)); + + assertTrue(subject.hasMetadata("subject", "key")); + List values = subject.getMetadata("subject", "key"); + assertEquals(10, values.get(0).value()); + } + + @Test + public void testMetadataNotPresent() { + assertFalse(subject.hasMetadata("subject", "key")); + List values = subject.getMetadata("subject", "key"); + assertTrue(values.isEmpty()); + } + + @Test + public void testInvalidateAll() { + final Counter counter = new Counter(); + + subject.setMetadata("subject", "key", new LazyMetadataValue(pluginX, new Callable() { + public Object call() throws Exception { + counter.increment(); + return 10; + } + })); + + assertTrue(subject.hasMetadata("subject", "key")); + subject.getMetadata("subject", "key").get(0).value(); + subject.invalidateAll(pluginX); + subject.getMetadata("subject", "key").get(0).value(); + assertEquals(2, counter.value()); + } + + @Test + public void testInvalidateAllButActuallyNothing() { + final Counter counter = new Counter(); + + subject.setMetadata("subject", "key", new LazyMetadataValue(pluginX, new Callable() { + public Object call() throws Exception { + counter.increment(); + return 10; + } + })); + + assertTrue(subject.hasMetadata("subject", "key")); + subject.getMetadata("subject", "key").get(0).value(); + subject.invalidateAll(pluginY); + subject.getMetadata("subject", "key").get(0).value(); + assertEquals(1, counter.value()); + } + + @Test + public void testMetadataReplace() { + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10)); + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginY, 10)); + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 20)); + + for (MetadataValue mv : subject.getMetadata("subject", "key")) { + if (mv.getOwningPlugin().equals(pluginX)) { + assertEquals(20, mv.value()); + } + if (mv.getOwningPlugin().equals(pluginY)) { + assertEquals(10, mv.value()); + } + } + } + + @Test + public void testMetadataRemove() { + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10)); + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginY, 20)); + subject.removeMetadata("subject", "key", pluginX); + + assertTrue(subject.hasMetadata("subject", "key")); + assertEquals(1, subject.getMetadata("subject", "key").size()); + assertEquals(20, subject.getMetadata("subject", "key").get(0).value()); + } + + @Test + public void testMetadataRemoveLast() { + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10)); + subject.removeMetadata("subject", "key", pluginX); + + assertFalse(subject.hasMetadata("subject", "key")); + assertEquals(0, subject.getMetadata("subject", "key").size()); + } + + @Test + public void testMetadataRemoveForNonExistingPlugin() { + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10)); + subject.removeMetadata("subject", "key", pluginY); + + assertTrue(subject.hasMetadata("subject", "key")); + assertEquals(1, subject.getMetadata("subject", "key").size()); + assertEquals(10, subject.getMetadata("subject", "key").get(0).value()); + } + + @Test + public void testHasMetadata() { + subject.setMetadata("subject", "key", new FixedMetadataValue(pluginX, 10)); + assertTrue(subject.hasMetadata("subject", "key")); + assertFalse(subject.hasMetadata("subject", "otherKey")); + } + + private class StringMetadataStore extends MetadataStoreBase implements MetadataStore { + @Override + protected String disambiguate(String subject, String metadataKey) { + return subject + ":" + metadataKey; + } + } + + private class Counter { + int c = 0; + + public void increment() { + c++; + } + + public int value() { + return c; + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/metadata/MetadataValueAdapterTest.java b/vspigot-api/src/test/java/org/bukkit/metadata/MetadataValueAdapterTest.java new file mode 100644 index 0000000..7d8a17f --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/metadata/MetadataValueAdapterTest.java @@ -0,0 +1,97 @@ +package org.bukkit.metadata; + +import static org.junit.Assert.assertEquals; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.TestPlugin; +import org.junit.Test; + +public class MetadataValueAdapterTest { + private TestPlugin plugin = new TestPlugin("x"); + + @Test + public void testAdapterBasics() { + IncrementingMetaValue mv = new IncrementingMetaValue(plugin); + // check getOwningPlugin + assertEquals(mv.getOwningPlugin(), this.plugin); + + // Check value-getting and invalidation. + assertEquals(new Integer(1), mv.value()); + assertEquals(new Integer(2), mv.value()); + mv.invalidate(); + assertEquals(new Integer(1), mv.value()); + } + + @Test + public void testAdapterConversions() { + IncrementingMetaValue mv = new IncrementingMetaValue(plugin); + + assertEquals(1, mv.asInt()); + assertEquals(2L, mv.asLong()); + assertEquals(3.0, mv.asFloat(), 0.001); + assertEquals(4, mv.asByte()); + assertEquals(5.0, mv.asDouble(), 0.001); + assertEquals(6, mv.asShort()); + assertEquals("7", mv.asString()); + } + + /** Boolean conversion is non-trivial, we want to test it thoroughly. */ + @Test + public void testBooleanConversion() { + // null is False. + assertEquals(false, simpleValue(null).asBoolean()); + + // String to boolean. + assertEquals(true, simpleValue("True").asBoolean()); + assertEquals(true, simpleValue("TRUE").asBoolean()); + assertEquals(false, simpleValue("false").asBoolean()); + + // Number to boolean. + assertEquals(true, simpleValue(1).asBoolean()); + assertEquals(true, simpleValue(5.0).asBoolean()); + assertEquals(false, simpleValue(0).asBoolean()); + assertEquals(false, simpleValue(0.1).asBoolean()); + + // Boolean as boolean, of course. + assertEquals(true, simpleValue(Boolean.TRUE).asBoolean()); + assertEquals(false, simpleValue(Boolean.FALSE).asBoolean()); + + // any object that is not null and not a Boolean, String, or Number is true. + assertEquals(true, simpleValue(new Object()).asBoolean()); + } + + /** Test String conversions return an empty string when given null. */ + @Test + public void testStringConversionNull() { + assertEquals("", simpleValue(null).asString()); + } + + /** Get a fixed value MetadataValue. */ + private MetadataValue simpleValue(Object value) { + return new FixedMetadataValue(plugin, value); + } + + /** + * A sample non-trivial MetadataValueAdapter implementation. + * + * The rationale for implementing an incrementing value is to have a value + * which changes with every call to value(). This is important for testing + * because we want to make sure all the tested conversions are calling the + * value() method exactly once and no caching is going on. + */ + class IncrementingMetaValue extends MetadataValueAdapter { + private int internalValue = 0; + + protected IncrementingMetaValue(Plugin owningPlugin) { + super(owningPlugin); + } + + public Object value() { + return ++internalValue; + } + + public void invalidate() { + internalValue = 0; + } + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/plugin/PluginManagerTest.java b/vspigot-api/src/test/java/org/bukkit/plugin/PluginManagerTest.java new file mode 100644 index 0000000..6b86128 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/plugin/PluginManagerTest.java @@ -0,0 +1,176 @@ +package org.bukkit.plugin; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.bukkit.TestServer; +import org.bukkit.event.Event; +import org.bukkit.event.TestEvent; +import org.bukkit.permissions.Permission; + +import org.junit.After; +import org.junit.Test; + +public class PluginManagerTest { + private class MutableObject { + volatile Object value = null; + } + + private static final PluginManager pm = TestServer.getInstance().getPluginManager(); + + private final MutableObject store = new MutableObject(); + + @Test + public void testAsyncSameThread() { + final Event event = new TestEvent(true); + try { + pm.callEvent(event); + } catch (IllegalStateException ex) { + assertThat(event.getEventName() + " cannot be triggered asynchronously from primary server thread.", is(ex.getMessage())); + return; + } + throw new IllegalStateException("No exception thrown"); + } + + @Test + public void testSyncSameThread() { + final Event event = new TestEvent(false); + pm.callEvent(event); + } + + @Test + public void testAsyncLocked() throws InterruptedException { + final Event event = new TestEvent(true); + Thread secondThread = new Thread( + new Runnable() { + public void run() { + try { + synchronized (pm) { + pm.callEvent(event); + } + } catch (Throwable ex) { + store.value = ex; + } + } + } + ); + secondThread.start(); + secondThread.join(); + assertThat(store.value, is(instanceOf(IllegalStateException.class))); + assertThat(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.", is(((Throwable) store.value).getMessage())); + } + + @Test + public void testAsyncUnlocked() throws InterruptedException { + final Event event = new TestEvent(true); + Thread secondThread = new Thread( + new Runnable() { + public void run() { + try { + pm.callEvent(event); + } catch (Throwable ex) { + store.value = ex; + } + }}); + secondThread.start(); + secondThread.join(); + if (store.value != null) { + throw new RuntimeException((Throwable) store.value); + } + } + + @Test + public void testSyncUnlocked() throws InterruptedException { + final Event event = new TestEvent(false); + Thread secondThread = new Thread( + new Runnable() { + public void run() { + try { + pm.callEvent(event); + } catch (Throwable ex) { + store.value = ex; + } + } + } + ); + secondThread.start(); + secondThread.join(); + if (store.value != null) { + throw new RuntimeException((Throwable) store.value); + } + } + + @Test + public void testSyncLocked() throws InterruptedException { + final Event event = new TestEvent(false); + Thread secondThread = new Thread( + new Runnable() { + public void run() { + try { + synchronized (pm) { + pm.callEvent(event); + } + } catch (Throwable ex) { + store.value = ex; + } + } + } + ); + secondThread.start(); + secondThread.join(); + if (store.value != null) { + throw new RuntimeException((Throwable) store.value); + } + } + + @Test + public void testRemovePermissionByNameLower() { + this.testRemovePermissionByName("lower"); + } + + @Test + public void testRemovePermissionByNameUpper() { + this.testRemovePermissionByName("UPPER"); + } + + @Test + public void testRemovePermissionByNameCamel() { + this.testRemovePermissionByName("CaMeL"); + } + + public void testRemovePermissionByPermissionLower() { + this.testRemovePermissionByPermission("lower"); + } + + @Test + public void testRemovePermissionByPermissionUpper() { + this.testRemovePermissionByPermission("UPPER"); + } + + @Test + public void testRemovePermissionByPermissionCamel() { + this.testRemovePermissionByPermission("CaMeL"); + } + + private void testRemovePermissionByName(final String name) { + final Permission perm = new Permission(name); + pm.addPermission(perm); + assertThat("Permission \"" + name + "\" was not added", pm.getPermission(name), is(perm)); + pm.removePermission(name); + assertThat("Permission \"" + name + "\" was not removed", pm.getPermission(name), is(nullValue())); + } + + private void testRemovePermissionByPermission(final String name) { + final Permission perm = new Permission(name); + pm.addPermission(perm); + assertThat("Permission \"" + name + "\" was not added", pm.getPermission(name), is(perm)); + pm.removePermission(perm); + assertThat("Permission \"" + name + "\" was not removed", pm.getPermission(name), is(nullValue())); + } + + @After + public void tearDown() { + pm.clearPlugins(); + assertThat(pm.getPermissions(), is(empty())); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/plugin/TestPlugin.java b/vspigot-api/src/test/java/org/bukkit/plugin/TestPlugin.java new file mode 100644 index 0000000..7e09892 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/plugin/TestPlugin.java @@ -0,0 +1,111 @@ +package org.bukkit.plugin; + +import java.io.File; +import java.io.InputStream; +import java.util.List; + +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.ChunkGenerator; + +import com.avaje.ebean.EbeanServer; + +public class TestPlugin extends PluginBase { + private boolean enabled = true; + + final private String pluginName; + + public TestPlugin(String pluginName) { + this.pluginName = pluginName; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public File getDataFolder() { + throw new UnsupportedOperationException("Not supported."); + } + + public PluginDescriptionFile getDescription() { + return new PluginDescriptionFile(pluginName, "1.0", "test.test"); + } + + public FileConfiguration getConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public InputStream getResource(String filename) { + throw new UnsupportedOperationException("Not supported."); + } + + public void saveConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public void saveDefaultConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public void saveResource(String resourcePath, boolean replace) { + throw new UnsupportedOperationException("Not supported."); + } + + public void reloadConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public PluginLogger getLogger() { + throw new UnsupportedOperationException("Not supported."); + } + + public PluginLoader getPluginLoader() { + throw new UnsupportedOperationException("Not supported."); + } + + public Server getServer() { + throw new UnsupportedOperationException("Not supported."); + } + + public boolean isEnabled() { + return enabled; + } + + public void onDisable() { + throw new UnsupportedOperationException("Not supported."); + } + + public void onLoad() { + throw new UnsupportedOperationException("Not supported."); + } + + public void onEnable() { + throw new UnsupportedOperationException("Not supported."); + } + + public boolean isNaggable() { + throw new UnsupportedOperationException("Not supported."); + } + + public void setNaggable(boolean canNag) { + throw new UnsupportedOperationException("Not supported."); + } + + public EbeanServer getDatabase() { + throw new UnsupportedOperationException("Not supported."); + } + + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + throw new UnsupportedOperationException("Not supported."); + } + + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } + + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/plugin/TimedRegisteredListenerTest.java b/vspigot-api/src/test/java/org/bukkit/plugin/TimedRegisteredListenerTest.java new file mode 100644 index 0000000..b206b1f --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/plugin/TimedRegisteredListenerTest.java @@ -0,0 +1,56 @@ +package org.bukkit.plugin; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.junit.Test; + +public class TimedRegisteredListenerTest { + + @Test + public void testEventClass() throws EventException { + Listener listener = new Listener() {}; + EventExecutor executor = new EventExecutor() { + public void execute(Listener listener, Event event) {} + }; + TestPlugin plugin = new TestPlugin("Test"); + + PlayerInteractEvent interactEvent = new PlayerInteractEvent(null, null, null, null, null); + PlayerMoveEvent moveEvent = new PlayerMoveEvent(null, null, null); + BlockBreakEvent breakEvent = new BlockBreakEvent(null, null); + + TimedRegisteredListener trl = new TimedRegisteredListener(listener, executor, EventPriority.NORMAL, plugin, false); + + // Ensure that the correct event type is reported for a single event + trl.callEvent(interactEvent); + assertThat(trl.getEventClass(), is((Object) PlayerInteractEvent.class)); + // Ensure that no superclass is used in lieu of the actual event, after two identical event types + trl.callEvent(interactEvent); + assertThat(trl.getEventClass(), is((Object) PlayerInteractEvent.class)); + // Ensure that the closest superclass of the two events is chosen + trl.callEvent(moveEvent); + assertThat(trl.getEventClass(), is((Object) PlayerEvent.class)); + // As above, so below + trl.callEvent(breakEvent); + assertThat(trl.getEventClass(), is((Object) Event.class)); + // In the name of being thorough, check that it never travels down the hierarchy again. + trl.callEvent(breakEvent); + assertThat(trl.getEventClass(), is((Object) Event.class)); + + trl = new TimedRegisteredListener(listener, executor, EventPriority.NORMAL, plugin, false); + + trl.callEvent(breakEvent); + assertThat(trl.getEventClass(), is((Object) BlockBreakEvent.class)); + // Test moving up the class hierarchy by more than one class at a time + trl.callEvent(moveEvent); + assertThat(trl.getEventClass(), is((Object) Event.class)); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java b/vspigot-api/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java new file mode 100644 index 0000000..644e6d1 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java @@ -0,0 +1,289 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.TestPlugin; +import java.util.Collection; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +public class StandardMessengerTest { + public StandardMessenger getMessenger() { + return new StandardMessenger(); + } + + private int count = 0; + public TestPlugin getPlugin() { + return new TestPlugin("" + count++); + } + + @Test + public void testIsReservedChannel() { + Messenger messenger = getMessenger(); + + assertTrue(messenger.isReservedChannel("REGISTER")); + assertFalse(messenger.isReservedChannel("register")); + assertTrue(messenger.isReservedChannel("UNREGISTER")); + assertFalse(messenger.isReservedChannel("unregister")); + assertFalse(messenger.isReservedChannel("notReserved")); + } + + @Test + public void testRegisterAndUnregisterOutgoingPluginChannel() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + messenger.registerOutgoingPluginChannel(plugin, "foo"); + assertTrue(messenger.isOutgoingChannelRegistered(plugin, "foo")); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "bar")); + + messenger.unregisterOutgoingPluginChannel(plugin, "foo"); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + } + + @Test(expected = ReservedChannelException.class) + public void testReservedOutgoingRegistration() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + messenger.registerOutgoingPluginChannel(plugin, "REGISTER"); + } + + @Test + public void testUnregisterOutgoingPluginChannel_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + messenger.registerOutgoingPluginChannel(plugin, "foo"); + messenger.registerOutgoingPluginChannel(plugin, "bar"); + assertTrue(messenger.isOutgoingChannelRegistered(plugin, "foo")); + assertTrue(messenger.isOutgoingChannelRegistered(plugin, "bar")); + + messenger.unregisterOutgoingPluginChannel(plugin); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "bar")); + } + + @Test + public void testRegisterIncomingPluginChannel() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener = new TestMessageListener("foo", "bar".getBytes()); + Player player = TestPlayer.getInstance(); + PluginMessageListenerRegistration registration = messenger.registerIncomingPluginChannel(plugin, "foo", listener); + + assertTrue(registration.isValid()); + assertTrue(messenger.isIncomingChannelRegistered(plugin, "foo")); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + assertTrue(listener.hasReceived()); + + messenger.unregisterIncomingPluginChannel(plugin, "foo", listener); + listener.reset(); + + assertFalse(registration.isValid()); + assertFalse(messenger.isIncomingChannelRegistered(plugin, "foo")); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + assertFalse(listener.hasReceived()); + } + + @Test(expected = ReservedChannelException.class) + public void testReservedIncomingRegistration() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + messenger.registerIncomingPluginChannel(plugin, "REGISTER", new TestMessageListener("foo", "bar".getBytes())); + } + + @Test(expected = IllegalArgumentException.class) + public void testDuplicateIncomingRegistration() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener = new TestMessageListener("foo", "bar".getBytes()); + + messenger.registerIncomingPluginChannel(plugin, "baz", listener); + messenger.registerIncomingPluginChannel(plugin, "baz", listener); + } + + @Test + public void testUnregisterIncomingPluginChannel_Plugin_String() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener1 = new TestMessageListener("foo", "bar".getBytes()); + TestMessageListener listener2 = new TestMessageListener("baz", "qux".getBytes()); + Player player = TestPlayer.getInstance(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin, "foo", listener1); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin, "baz", listener2); + + assertTrue(registration1.isValid()); + assertTrue(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertTrue(listener1.hasReceived()); + assertTrue(listener2.hasReceived()); + + messenger.unregisterIncomingPluginChannel(plugin, "foo"); + listener1.reset(); + listener2.reset(); + + assertFalse(registration1.isValid()); + assertTrue(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertFalse(listener1.hasReceived()); + assertTrue(listener2.hasReceived()); + } + + @Test + public void testUnregisterIncomingPluginChannel_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener1 = new TestMessageListener("foo", "bar".getBytes()); + TestMessageListener listener2 = new TestMessageListener("baz", "qux".getBytes()); + Player player = TestPlayer.getInstance(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin, "foo", listener1); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin, "baz", listener2); + + assertTrue(registration1.isValid()); + assertTrue(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertTrue(listener1.hasReceived()); + assertTrue(listener2.hasReceived()); + + messenger.unregisterIncomingPluginChannel(plugin); + listener1.reset(); + listener2.reset(); + + assertFalse(registration1.isValid()); + assertFalse(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertFalse(listener1.hasReceived()); + assertFalse(listener2.hasReceived()); + } + + @Test + public void testGetOutgoingChannels() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + + assertEquals(messenger.getOutgoingChannels()); + + messenger.registerOutgoingPluginChannel(plugin1, "foo"); + messenger.registerOutgoingPluginChannel(plugin1, "bar"); + messenger.registerOutgoingPluginChannel(plugin2, "baz"); + messenger.registerOutgoingPluginChannel(plugin2, "baz"); + + assertEquals(messenger.getOutgoingChannels(), "foo", "bar", "baz"); + } + + @Test + public void testGetOutgoingChannels_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + + messenger.registerOutgoingPluginChannel(plugin1, "foo"); + messenger.registerOutgoingPluginChannel(plugin1, "bar"); + messenger.registerOutgoingPluginChannel(plugin2, "baz"); + messenger.registerOutgoingPluginChannel(plugin2, "qux"); + + assertEquals(messenger.getOutgoingChannels(plugin1), "foo", "bar"); + assertEquals(messenger.getOutgoingChannels(plugin2), "baz", "qux"); + assertEquals(messenger.getOutgoingChannels(plugin3)); + } + + @Test + public void testGetIncomingChannels() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + + assertEquals(messenger.getIncomingChannels()); + + messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannels(), "foo", "bar", "baz"); + } + + @Test + public void testGetIncomingChannels_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + + messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "qux", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannels(plugin1), "foo", "bar"); + assertEquals(messenger.getIncomingChannels(plugin2), "baz", "qux"); + assertEquals(messenger.getIncomingChannels(plugin3)); + } + + @Test + public void testGetIncomingChannelRegistrations_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration3 = messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration4 = messenger.registerIncomingPluginChannel(plugin2, "qux", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannelRegistrations(plugin1), registration1, registration2); + assertEquals(messenger.getIncomingChannelRegistrations(plugin2), registration3, registration4); + assertEquals(messenger.getIncomingChannels(plugin3)); + } + + @Test + public void testGetIncomingChannelRegistrations_String() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration3 = messenger.registerIncomingPluginChannel(plugin2, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration4 = messenger.registerIncomingPluginChannel(plugin2, "bar", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannelRegistrations("foo"), registration1, registration3); + assertEquals(messenger.getIncomingChannelRegistrations("bar"), registration2, registration4); + assertEquals(messenger.getIncomingChannelRegistrations("baz")); + } + + @Test + public void testGetIncomingChannelRegistrations_Plugin_String() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration3 = messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration4 = messenger.registerIncomingPluginChannel(plugin2, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration5 = messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration6 = messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannelRegistrations(plugin1, "foo"), registration1, registration2); + assertEquals(messenger.getIncomingChannelRegistrations(plugin1, "bar"), registration3); + assertEquals(messenger.getIncomingChannelRegistrations(plugin2, "bar"), registration4); + assertEquals(messenger.getIncomingChannelRegistrations(plugin2, "baz"), registration5, registration6); + assertEquals(messenger.getIncomingChannelRegistrations(plugin1, "baz")); + assertEquals(messenger.getIncomingChannelRegistrations(plugin3, "qux")); + } + + private static void assertEquals(Collection actual, T... expected) { + assertThat("Size of the array", actual.size(), is(expected.length)); + assertThat(actual, hasItems(expected)); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/plugin/messaging/TestMessageListener.java b/vspigot-api/src/test/java/org/bukkit/plugin/messaging/TestMessageListener.java new file mode 100644 index 0000000..98860ec --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/plugin/messaging/TestMessageListener.java @@ -0,0 +1,29 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.entity.Player; +import static org.junit.Assert.*; + +public class TestMessageListener implements PluginMessageListener { + private final String channel; + private final byte[] message; + private boolean received = false; + + public TestMessageListener(String channel, byte[] message) { + this.channel = channel; + this.message = message; + } + + public void onPluginMessageReceived(String channel, Player player, byte[] message) { + assertEquals(this.channel, channel); + assertArrayEquals(this.message, message); + this.received = true; + } + + public boolean hasReceived() { + return received; + } + + public void reset() { + received = false; + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java b/vspigot-api/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java new file mode 100644 index 0000000..71f59c5 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java @@ -0,0 +1,50 @@ +package org.bukkit.plugin.messaging; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; + +import org.bukkit.entity.Player; + + +public class TestPlayer implements InvocationHandler { + private static interface MethodHandler { + Object handle(TestPlayer server, Object[] args); + } + private static final Constructor constructor; + private static final HashMap methods = new HashMap(); + static { + try { + /* + methods.put(Player.class.getMethod("methodName"), + new MethodHandler() { + public Object handle(TestPlayer server, Object[] args) { + } + }); + */ + constructor = Proxy.getProxyClass(Player.class.getClassLoader(), Player.class).asSubclass(Player.class).getConstructor(InvocationHandler.class); + } catch (Throwable t) { + throw new Error(t); + } + } + + private TestPlayer() {}; + + public static Player getInstance() { + try { + return constructor.newInstance(new TestPlayer()); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + public Object invoke(Object proxy, Method method, Object[] args) { + MethodHandler handler = methods.get(method); + if (handler != null) { + return handler.handle(this, args); + } + throw new UnsupportedOperationException(String.valueOf(method)); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/potion/PotionTest.java b/vspigot-api/src/test/java/org/bukkit/potion/PotionTest.java new file mode 100644 index 0000000..58252ae --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/potion/PotionTest.java @@ -0,0 +1,148 @@ +package org.bukkit.potion; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.is; + +import org.bukkit.Material; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; +import org.junit.Test; + +public class PotionTest { + @Test + public void applyToItemStack() { + Potion potion = new Potion(PotionType.POISON); + ItemStack stack = new ItemStack(Material.POTION, 1); + potion.apply(stack); + assertTrue(stack.getDurability() == potion.toDamageValue()); + } + + @Test + public void fromDamage() { + Potion potion = Potion.fromDamage(PotionType.POISON.getDamageValue()); + assertTrue(potion.getType() == PotionType.POISON); + potion = Potion.fromDamage(PotionType.POISON.getDamageValue() | SPLASH_BIT); + assertTrue(potion.getType() == PotionType.POISON && potion.isSplash()); + potion = Potion.fromDamage(0x25 /* Potion of Healing II */); + assertTrue(potion.getType() == PotionType.INSTANT_HEAL && potion.getLevel() == 2); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalApplyToItemStack() { + Potion potion = new Potion(PotionType.POISON); + potion.apply(new ItemStack(Material.AIR, 1)); + } + + @Test + public void ItemStackConversion() { + Potion potion = new Potion(PotionType.POISON); + ItemStack itemstack = potion.toItemStack(1); + assertThat(itemstack.getType(), is(Material.POTION)); + assertTrue(itemstack.getAmount() == 1); + assertTrue(itemstack.getDurability() == potion.toDamageValue()); + } + + @Test + public void setExtended() { + PotionEffectType.registerPotionEffectType(new PotionEffectType(19){ + @Override + public double getDurationModifier() { + return 1; + } + + @Override + public String getName() { + return "Poison"; + } + + @Override + public boolean isInstant() { + return false; + } + }); + Potion potion = new Potion(PotionType.POISON); + assertFalse(potion.hasExtendedDuration()); + potion.setHasExtendedDuration(true); + assertTrue(potion.hasExtendedDuration()); + assertTrue((potion.toDamageValue() & EXTENDED_BIT) != 0); + } + + @Test + public void setSplash() { + Potion potion = new Potion(PotionType.POISON); + assertFalse(potion.isSplash()); + potion.setSplash(true); + assertTrue(potion.isSplash()); + assertTrue((potion.toDamageValue() & SPLASH_BIT) != 0); + } + + @Test + public void setLevel() { + Potion potion = new Potion(PotionType.POISON); + assertEquals(1, potion.getLevel()); + potion.setLevel(2); + assertEquals(2, potion.getLevel()); + assertTrue((potion.toDamageValue() & 0x3F) == (PotionType.POISON.getDamageValue() | 0x20)); + } + + @Test(expected=IllegalArgumentException.class) + public void nullType() { + new Potion(null, 2); + } + + @Test(expected=IllegalArgumentException.class) + public void maxLevelConstruct() { + new Potion(PotionType.POISON, 3); + } + + @Test(expected=IllegalArgumentException.class) + public void maxLevelSet() { + Potion potion = new Potion(PotionType.POISON); + potion.setLevel(3); + } + + @Test(expected=IllegalArgumentException.class) + public void nullStack() { + Potion potion = new Potion(PotionType.POISON); + potion.apply((ItemStack) null); + } + + @Test(expected=IllegalArgumentException.class) + public void nullEntity() { + Potion potion = new Potion(PotionType.POISON); + potion.apply((LivingEntity) null); + } + + @Test + public void water() { + Potion potion = new Potion(PotionType.WATER); + assertEquals(0, potion.getLevel()); + assertFalse(potion.isSplash()); + assertFalse(potion.hasExtendedDuration()); + assertEquals(0, potion.toDamageValue()); + } + + @Test + public void mundane() { + Potion potion = new Potion(0); + assertFalse(potion.getType() == PotionType.WATER); + assertFalse(potion.toDamageValue() == 0); + assertEquals(8192, potion.toDamageValue()); + Potion potion2 = Potion.fromDamage(8192); + assertEquals(potion, potion2); + assertEquals(0, potion.getLevel()); + } + + @Test + public void awkward() { + Potion potion = new Potion(16); + assertEquals(16, potion.getNameId()); + assertFalse(potion.isSplash()); + assertFalse(potion.hasExtendedDuration()); + assertNull(potion.getType()); + assertEquals(16, potion.toDamageValue()); + } + + private static final int EXTENDED_BIT = 0x40; + private static final int SPLASH_BIT = 0x4000; +} diff --git a/vspigot-api/src/test/java/org/bukkit/util/StringUtilStartsWithTest.java b/vspigot-api/src/test/java/org/bukkit/util/StringUtilStartsWithTest.java new file mode 100644 index 0000000..b85a2b2 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/util/StringUtilStartsWithTest.java @@ -0,0 +1,86 @@ +package org.bukkit.util; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.collect.ImmutableList; + +@RunWith(Parameterized.class) +public class StringUtilStartsWithTest { + + @Parameters(name= "{index}: {0} startsWith {1} == {2}") + public static List data() { + return ImmutableList.of( + new Object[] { + "Apple", + "Apples", + false + }, + new Object[] { + "Apples", + "Apple", + true + }, + new Object[] { + "Apple", + "Apple", + true + }, + new Object[] { + "Apple", + "apples", + false + }, + new Object[] { + "apple", + "Apples", + false + }, + new Object[] { + "apple", + "apples", + false + }, + new Object[] { + "Apples", + "apPL", + true + }, + new Object[] { + "123456789", + "1234567", + true + }, + new Object[] { + "", + "", + true + }, + new Object[] { + "string", + "", + true + } + ); + } + + @Parameter(0) + public String base; + @Parameter(1) + public String prefix; + @Parameter(2) + public boolean result; + + @Test + public void testFor() { + assertThat(base + " starts with " + prefix + ": " + result, StringUtil.startsWithIgnoreCase(base, prefix), is(result)); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/util/StringUtilTest.java b/vspigot-api/src/test/java/org/bukkit/util/StringUtilTest.java new file mode 100644 index 0000000..9c8444c --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/util/StringUtilTest.java @@ -0,0 +1,61 @@ +package org.bukkit.util; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import com.google.common.collect.ImmutableList; + +public class StringUtilTest { + + @Test(expected=NullPointerException.class) + public void nullPrefixTest() { + StringUtil.startsWithIgnoreCase("String", null); + } + + @Test(expected=IllegalArgumentException.class) + public void nullStringTest() { + StringUtil.startsWithIgnoreCase(null, "String"); + } + + @Test(expected=IllegalArgumentException.class) + public void nullCollectionTest() { + StringUtil.copyPartialMatches("Token", ImmutableList.of(), null); + } + + @Test(expected=IllegalArgumentException.class) + public void nullIterableTest() { + StringUtil.copyPartialMatches("Token", null, new ArrayList()); + } + + @Test(expected=IllegalArgumentException.class) + public void nullTokenTest() { + StringUtil.copyPartialMatches(null, ImmutableList.of(), new ArrayList()); + } + + @Test + public void copyTokenTest() { + String token = "ab"; + Iterable original = ImmutableList.of("ab12", "aC561", "AB5195", "Ab76", "", "a"); + List expected = ImmutableList.of("ab12", "AB5195", "Ab76" ); + List list = new ArrayList(); + assertThat(StringUtil.copyPartialMatches(token, original, list), is(expected)); + assertThat(StringUtil.copyPartialMatches(token, original, list), is(sameInstance(list))); + assertThat(list.size(), is(expected.size() * 2)); + } + + @Test(expected=UnsupportedOperationException.class) + public void copyUnsupportedTest() { + StringUtil.copyPartialMatches("token", ImmutableList.of("token1", "token2"), ImmutableList.of()); + } + + @Test(expected=IllegalArgumentException.class) + public void copyNullTest() { + StringUtil.copyPartialMatches("token", Arrays.asList("token1", "token2", null), new ArrayList()); + } +} diff --git a/vspigot-api/src/test/java/org/bukkit/util/io/BukkitObjectStreamTest.java b/vspigot-api/src/test/java/org/bukkit/util/io/BukkitObjectStreamTest.java new file mode 100644 index 0000000..d0af4a0 --- /dev/null +++ b/vspigot-api/src/test/java/org/bukkit/util/io/BukkitObjectStreamTest.java @@ -0,0 +1,173 @@ +package org.bukkit.util.io; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.List; + +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.util.Vector; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; + +import com.google.common.collect.ImmutableList; + +@RunWith(Parameterized.class) +public class BukkitObjectStreamTest { + + @Parameters(name= "{index}: {0}") + public static List data() { + return ImmutableList.of( + new Object[] { + Color.class.getName(), + "rO0ABXNyADZjb20uZ29vZ2xlLmNvbW1vbi5jb2xsZWN0LkltbXV0YWJsZUxpc3QkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAVsACGVsZW1lbnRzdAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAABXNyABpvcmcuYnVra2l0LnV0aWwuaW8uV3JhcHBlcvJQR+zxEm8FAgABTAADbWFwdAAPTGphdmEvdXRpbC9NYXA7eHBzcgA1Y29tLmdvb2dsZS5jb21tb24uY29sbGVjdC5JbW11dGFibGVNYXAkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAlsABGtleXNxAH4AAVsABnZhbHVlc3EAfgABeHB1cQB+AAMAAAAEdAACPT10AANSRUR0AARCTFVFdAAFR1JFRU51cQB+AAMAAAAEdAAFQ29sb3JzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAA/3NxAH4AEQAAAP9zcQB+ABEAAAD/c3EAfgAFc3EAfgAIdXEAfgADAAAABHEAfgALcQB+AAxxAH4ADXEAfgAOdXEAfgADAAAABHEAfgAQc3EAfgARAAAAAHNxAH4AEQAAAIBzcQB+ABEAAACAc3EAfgAFc3EAfgAIdXEAfgADAAAABHEAfgALcQB+AAxxAH4ADXEAfgAOdXEAfgADAAAABHEAfgAQc3EAfgARAAAAgHNxAH4AEQAAAIBxAH4AGnNxAH4ABXNxAH4ACHVxAH4AAwAAAARxAH4AC3EAfgAMcQB+AA1xAH4ADnVxAH4AAwAAAARxAH4AEHNxAH4AEQAAAP9xAH4AGnEAfgAac3EAfgAFc3EAfgAIdXEAfgADAAAABHEAfgALcQB+AAxxAH4ADXEAfgAOdXEAfgADAAAABHEAfgAQc3EAfgARAAAA/3EAfgAac3EAfgARAAAApQ==", + ImmutableList.of( + Color.WHITE, + Color.TEAL, + Color.PURPLE, + Color.RED, + Color.ORANGE + ) + }, + new Object[] { + FireworkEffect.class.getName(), + "rO0ABXNyADZjb20uZ29vZ2xlLmNvbW1vbi5jb2xsZWN0LkltbXV0YWJsZUxpc3QkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAVsACGVsZW1lbnRzdAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAA3NyABpvcmcuYnVra2l0LnV0aWwuaW8uV3JhcHBlcvJQR+zxEm8FAgABTAADbWFwdAAPTGphdmEvdXRpbC9NYXA7eHBzcgA1Y29tLmdvb2dsZS5jb21tb24uY29sbGVjdC5JbW11dGFibGVNYXAkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAlsABGtleXNxAH4AAVsABnZhbHVlc3EAfgABeHB1cQB+AAMAAAAGdAACPT10AAdmbGlja2VydAAFdHJhaWx0AAZjb2xvcnN0AAtmYWRlLWNvbG9yc3QABHR5cGV1cQB+AAMAAAAGdAAIRmlyZXdvcmtzcgARamF2YS5sYW5nLkJvb2xlYW7NIHKA1Zz67gIAAVoABXZhbHVleHABc3EAfgATAHNxAH4AAHVxAH4AAwAAAAJzcQB+AAVzcQB+AAh1cQB+AAMAAAAEcQB+AAt0AANSRUR0AARCTFVFdAAFR1JFRU51cQB+AAMAAAAEdAAFQ29sb3JzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAHEAfgAicQB+ACJzcQB+AAVzcQB+AAh1cQB+AAMAAAAEcQB+AAtxAH4AG3EAfgAccQB+AB11cQB+AAMAAAAEcQB+AB9zcQB+ACAAAADAc3EAfgAgAAAAwHNxAH4AIAAAAMBzcQB+AAB1cQB+AAMAAAABc3EAfgAFc3EAfgAIdXEAfgADAAAABHEAfgALcQB+ABtxAH4AHHEAfgAddXEAfgADAAAABHEAfgAfc3EAfgAgAAAA/3NxAH4AIAAAAP9zcQB+ACAAAAD/dAAKQkFMTF9MQVJHRXNxAH4ABXNxAH4ACHVxAH4AAwAAAAZxAH4AC3EAfgAMcQB+AA1xAH4ADnEAfgAPcQB+ABB1cQB+AAMAAAAGcQB+ABJxAH4AFXEAfgAVc3EAfgAAdXEAfgADAAAAAXNxAH4ABXNxAH4ACHVxAH4AAwAAAARxAH4AC3EAfgAbcQB+ABxxAH4AHXVxAH4AAwAAAARxAH4AH3EAfgAic3EAfgAgAAAAgHEAfgAic3EAfgAAdXEAfgADAAAAAHQABEJBTExzcQB+AAVzcQB+AAh1cQB+AAMAAAAGcQB+AAtxAH4ADHEAfgANcQB+AA5xAH4AD3EAfgAQdXEAfgADAAAABnEAfgAScQB+ABRxAH4AFHNxAH4AAHVxAH4AAwAAAAFzcQB+AAVzcQB+AAh1cQB+AAMAAAAEcQB+AAtxAH4AG3EAfgAccQB+AB11cQB+AAMAAAAEcQB+AB9zcQB+ACAAAACAcQB+ACJxAH4AInEAfgA/dAAHQ1JFRVBFUg==", + ImmutableList.of( + FireworkEffect.builder() + .withColor(Color.BLACK, Color.SILVER) + .with(Type.BALL_LARGE) + .withFade(Color.WHITE) + .withFlicker() + .build(), + FireworkEffect.builder() + .withColor(Color.NAVY) + .build(), + FireworkEffect.builder() + .withColor(Color.MAROON) + .withTrail() + .withFlicker() + .with(Type.CREEPER) + .build() + ), + }, + new Object[] { + Vector.class.getName(), + "rO0ABXNyADZjb20uZ29vZ2xlLmNvbW1vbi5jb2xsZWN0LkltbXV0YWJsZUxpc3QkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAVsACGVsZW1lbnRzdAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAABHNyABpvcmcuYnVra2l0LnV0aWwuaW8uV3JhcHBlcvJQR+zxEm8FAgABTAADbWFwdAAPTGphdmEvdXRpbC9NYXA7eHBzcgA1Y29tLmdvb2dsZS5jb21tb24uY29sbGVjdC5JbW11dGFibGVNYXAkU2VyaWFsaXplZEZvcm0AAAAAAAAAAAIAAlsABGtleXNxAH4AAVsABnZhbHVlc3EAfgABeHB1cQB+AAMAAAAEdAACPT10AAF4dAABeXQAAXp1cQB+AAMAAAAEdAAGVmVjdG9yc3IAEGphdmEubGFuZy5Eb3VibGWAs8JKKWv7BAIAAUQABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAAAAAABzcQB+ABEAAAAAAAAAAHNxAH4AEQAAAAAAAAAAc3EAfgAFc3EAfgAIdXEAfgADAAAABHEAfgALcQB+AAxxAH4ADXEAfgAOdXEAfgADAAAABHEAfgAQc3EAfgARQIOFwo9cKPZzcQB+ABFAtCKcKPXCj3NxAH4AEUBzrpeNT987c3EAfgAFc3EAfgAIdXEAfgADAAAABHEAfgALcQB+AAxxAH4ADXEAfgAOdXEAfgADAAAABHEAfgAQc3EAfgARwEQTMzMzMzNzcQB+ABFASYAAAAAAAHNxAH4AEcCjqG3UQTVUc3EAfgAFc3EAfgAIdXEAfgADAAAABHEAfgALcQB+AAxxAH4ADXEAfgAOdXEAfgADAAAABHEAfgAQc3EAfgARQd/////AAABzcQB+ABHB4AAAAAAAAHNxAH4AEQAAAAAAAAAA", + ImmutableList.of( + new Vector(0, 0, 0), + new Vector(624.72, 5154.61, 314.912), + new Vector(-40.15, 51, -2516.21451), + new Vector(Integer.MAX_VALUE, Integer.MIN_VALUE, 0) + ) + }); + } + + @Parameter(0) + public String className; + + @Parameter(1) + public String preEncoded; + + @Parameter(2) + public List object; + + @Test + public void checkSerlialization() throws Throwable { + // If this test fails, you may start your trek to debug by commenting the '@Ignore' on the next method + // (and of course, you would read those comments too) + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oos = null; + try { + oos = new BukkitObjectOutputStream(out); + oos.writeObject(object); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (IOException e) { + } + } + } + + final byte[] preEncodedArray = Base64Coder.decode(preEncoded); + + final Object readBack; + final Object preEncoded; + + ObjectInputStream ois = null; + ObjectInputStream preois = null; + try { + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + ByteArrayInputStream preIn = new ByteArrayInputStream(preEncodedArray); + ois = new BukkitObjectInputStream(in); + preois = new BukkitObjectInputStream(preIn); + + readBack = ois.readObject(); + preEncoded = preois.readObject(); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (IOException ex) { + } + } + if (preois != null) { + try { + preois.close(); + } catch (IOException ex) { + } + } + } + + assertThat(object, is(readBack)); + assertThat(object, is(preEncoded)); + } + + @Ignore + @Test + public void preEncoded() throws Throwable { + // This test is placed in the case that a necessary change is made to change the encoding format + // Just remove the ignore (or run manually) and it'll give you the new pre-encoded values + + // It really does not matter if the encoded array is different per system (hence why this test is set to not run), + // as long as all systems can deserialize it. + + // The entire reason the pre-encoded string was added is to make a build (test) fail if someone accidentally makes it not backward-compatible + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oos = null; + try { + oos = new BukkitObjectOutputStream(out); + oos.writeObject(object); + oos.flush(); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (IOException e) { + } + } + } + + final String string = new String(Base64Coder.encode(out.toByteArray())); + try { + assertThat(preEncoded, is(string)); + } catch (Throwable t) { + System.out.println(className + ": \"" + string + "\""); + throw t; + } + } +} diff --git a/vspigot-server/.gitignore b/vspigot-server/.gitignore new file mode 100644 index 0000000..818c534 --- /dev/null +++ b/vspigot-server/.gitignore @@ -0,0 +1,36 @@ +# Eclipse stuff +.classpath +.project +.settings/ + +# netbeans +nbproject/ +nbactions.xml + +# we use maven! +build.xml + +# maven +target/ +dependency-reduced-pom.xml + +# vim +.*.sw[a-p] + +# various other potential build files +build/ +bin/ +dist/ +manifest.mf + +# Mac filesystem dust +.DS_Store/ + +# intellij +*.iml +*.ipr +*.iws +.idea/ + +# Linux temp files +*~ diff --git a/vspigot-server/CONTRIBUTING.md b/vspigot-server/CONTRIBUTING.md new file mode 100644 index 0000000..3031b97 --- /dev/null +++ b/vspigot-server/CONTRIBUTING.md @@ -0,0 +1,497 @@ +# How to Contribute + +The Bukkit project prides itself on being community built and driven. We love it when members of our community want to jump right in and get involved, so here's what you need to know. + +## Quick Guide +1. Create or find an issue to address on our [JIRA issue tracker](http://leaky.bukkit.org). +- Does your proposed change [fit Bukkit's goals](#does-the-change-fit-bukkits-goals)? +- Fork the repository if you haven't done so already. +- Make your changes in a new branch (if your change affects both Bukkit and CraftBukkit, we highly suggest you use the same name for your branches in both repos). +- Test your changes. +- Push to your fork and submit a pull request. +- **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +![Life Cycle of a Bukkit Improvement](http://i.imgur.com/Ed6T7AE.png) + +## Getting Started +- You'll need a free [JIRA account](http://leaky.bukkit.org) (on our issue tracker affectionately called Leaky). +- You'll need a free [GitHub account](https://github.com/signup/free). +- Make sure you have a JIRA ticket for your issue at hand. + * Either search the list of current issues and find an appropriate issue. + * Or create one yourself if one does not already exist. + * When creating an issue, make sure to clearly describe the issue (including steps to reproduce it if it is a bug). +- Fork the repository on GitHub. +- **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +## Does the Change Fit Bukkit's Goals? +As a rough guideline, ask yourself the following questions to determine if your proposed change fits the Bukkit project's goals. Please remember that this is only a rough guideline and may or may not reflect the definitive answer to this question. + +* Does it expose an implementation detail of the server software, the protocol or file formats? + + If your change revolves around an implementation detail then it is not proper API design. Examples of bad API design would be along the lines of a packet API, an NBT storage API, or basing an enum on implementation values. + +* Does it result in unexpected behaviour as defined by the Vanilla specification? + + One of the goals of the Bukkit project is to be an extended Minecraft vanilla server - meaning: if you choose to run the Bukkit server without any plugins, it should function exactly as the Minecraft server would with some rare exceptions. If your change alters the behaviour of the server in such a way that you would not have the same experience as you would in Vanilla, your change does not fit the Bukkit project's goals. + +* Does it expose an issue or vulnerability when operating within the Vanilla environment? + + One of the goals of the Bukkit project is to be able to operate within the limitations of the Vanilla environment. If your change results in or exposes the ability to, for example, crash the client when invalid data is set, it does not fit the Bukkit project's needs. + +If you answered yes to any of these questions, chances are high your change does not fit the Bukkit project's goals and will most likely not be accepted. Regardless, there are a few other important questions you need to ask yourself before you start working on a change: + +* Is this change reasonably supportable and maintainable? + +* Is this change future proof? + +## Making the Changes +* Create a branch on your fork where you'll be making your changes. + * Name your branch something relevant to the change you are looking to make. + * Note: if your changes affect both Bukkit and CraftBukkit, it is highly suggested you use the same branch name on both repos. + * To create a branch in Git; + * `git branch relevantBranchName` + * Then checkout the new branch with `git checkout relevantBranchName` +* Check for unnecessary whitespace with `git diff --check` before committing. +* Make sure your code meets [our requirements](#code-requirements). +* If the work you want to do involves editing Minecraft classes, be sure to read over the [Using Minecraft Internals](#using-minecraft-internals) section. +* Make sure your commit messages are in the [proper format](#commit-message-example). +* Test your changes to make sure it actually addresses the issue it should. +* Make sure your code compiles under Java 6, as that is what the project has to be built with. + +### Code Requirements +* We generally follow the [Sun/Oracle coding standards](http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html). + +* No tabs; use 4 spaces instead. + +* No trailing whitespaces. + +* No CRLF line endings, LF only, set your Gits 'core.autocrlf' to 'true'. + + These whitespace requirements are easily and often overlooked. They are critical formatting requirements designed to help simplify a shared heterogeneous development environment. Learn how your IDE functions in order to show you these characters and verify them. Analyse the git diff closely to verify every character and if the PR should include the character change. It is tedious and it is critical. + + Eclipse: http://stackoverflow.com/a/11596227/532590 + NetBeans: http://stackoverflow.com/a/1866385/532590 + +* No 80 column limit or 'weird' midstatement newlines. + +* Any major additions should have documentation ready and provided if applicable (this is usually the case). + +* Try to follow test driven development where applicable. + + Bukkit employs JUnit (http://www.vogella.com/articles/JUnit/article.html) for testing and PRs should attempt to integrate with that framework as appropriate. Bukkit is a large project and what seems simple to a PR author at the time of writing a PR may easily be overlooked later by other authors and updates. Including unit tests with your PR will help to ensure the PR can be easily maintained over time and encourage the Bukkit Team to pull the PR. + +* There needs to be a new line at the end of every file. + +* Imports should be organised by alphabetical order, separated and grouped by package. + + **For example:** + + ```java + import java.io.ByteArrayInputStream; + import java.io.DataInputStream; + import java.io.IOException; + import java.util.ArrayList; + import java.util.Iterator; + import java.util.Random; + import java.util.concurrent.Callable; + + // CraftBukkit start + import java.io.UnsupportedEncodingException; + import java.util.concurrent.ExecutionException; + import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + import java.util.logging.Level; + import java.util.HashSet; + + import org.bukkit.Bukkit; + import org.bukkit.Location; + import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.craftbukkit.inventory.CraftInventoryView; + import org.bukkit.craftbukkit.inventory.CraftItemStack; + import org.bukkit.craftbukkit.util.LazyPlayerSet; + import org.bukkit.craftbukkit.util.Waitable; + import org.bukkit.craftbukkit.entity.CraftPlayer; + import org.bukkit.craftbukkit.event.CraftEventFactory; + import org.bukkit.entity.Player; + import org.bukkit.event.Event; + import org.bukkit.event.block.Action; + import org.bukkit.event.block.SignChangeEvent; + import org.bukkit.event.player.AsyncPlayerChatEvent; + import org.bukkit.event.player.PlayerAnimationEvent; + import org.bukkit.event.player.PlayerChatEvent; + import org.bukkit.event.player.PlayerCommandPreprocessEvent; + import org.bukkit.event.player.PlayerInteractEntityEvent; + import org.bukkit.event.player.PlayerItemHeldEvent; + import org.bukkit.event.player.PlayerKickEvent; + import org.bukkit.event.player.PlayerMoveEvent; + import org.bukkit.event.player.PlayerTeleportEvent; + import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + import org.bukkit.event.player.PlayerToggleSneakEvent; + import org.bukkit.event.player.PlayerToggleSprintEvent; + import org.bukkit.event.inventory.*; + import org.bukkit.event.inventory.InventoryType.SlotType; + import org.bukkit.event.player.PlayerPortalEvent; + import org.bukkit.event.player.PlayerToggleFlightEvent; + import org.bukkit.inventory.CraftingInventory; + import org.bukkit.inventory.InventoryView; + // CraftBukkit end + ``` + +### Using Minecraft Internals +#### Importing a New Minecraft Class +When contributing to the Bukkit project, you will likely find that you need to edit a Minecraft class that isn't already found within the project. In this case, you need to look at [our mc-dev repository](https://github.com/Bukkit/mc-dev), find the class you need, add it to the CraftBukkit repo and include it in its own special commit separate from your other changes. The commit message of this special commit should simply be "Add x for diff visibility", where x is the name of the file you are adding from mc-dev. + +If, however, you need to import multiple files from mc-dev into the Bukkit project, they should all be contained in the same special commit with the commit message "Add files for diff visibility". Note how the commit message no longer specifically mentions any class names. + +#### Making Changes to Minecraft Classes +The Bukkit project employs a Minimal Diff policy to help guide when changes should be made to Minecraft classes and what those changes should be. This is to ensure that any changes made have the smallest impact possible on the update process we go through whenever a Minecraft update is released. As well as keeping the Minimal Diff policy in mind, every change made to a Minecraft class needs to be marked as such with the appropriate CraftBukkit comment. + +##### Minimal Diff Policy +The Minimal Diff policy is a really important part of the project as it reminds us that every change to the Minecraft Internals has an impact on our update process. When people think of the phrase "minimal diffs", they often take it to the extreme - they go completely out of their way to abstract the changes they are trying to make away from editing Minecraft's classes as much as possible. However, this isn't what we mean by "minimal diffs". Instead, when trying to understand the minimal diffs policy, it helps to keep in mind its end goal: to reduce the impact changes we make to Minecraft's internals have on our update process. + +Put simply, the Minimal Diffs Policy simply means to make the smallest change in a Minecraft class possible without duplicating logic. + +Here are a few tips you should keep in mind or common areas you should focus on: + +* Try to avoid duplicating logic or code when making changes. + +* Try to keep your changes easily discernible - don't nest or group several unrelated changes together. + +* If you only use an import once within a class, don't import it and use fully qualified names instead. + +* Try to employ "short circuiting" of logic if at all possible. This means that you should force a conditional to be the value needed to side-step the code block if you would like to ignore that block of code. + + **For example, to short circuit this:** + + ```java + if (!this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { + this.die(); + this.h(); + } + ``` + **You would do this:** + + ```java + if (false && !this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { // CraftBukkit - not needed + this.die(); + this.h(); + } + ``` + +* When adding a validation check, see if the Validate package we already use has a better, more concise method you can use instead. + + **For example, you should use:** + + ```java + Validate.notNull(sender, "Sender cannot be null"); + ``` + + **Instead of:** + + ```java + if (sender == null) { + throw new IllegalArgumentException("Sender cannot be null"); + } + ``` + +* When the change you are attempting to make involves removing code, instead of removing it outright, you should comment it out. + + **For example:** + + ```java + // CraftBukkit start - special case dropping so we can get info from the tile entity + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (world.random.nextFloat() < f) { + ItemStack itemstack = new ItemStack(Item.SKULL.id, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } + } + // CraftBukkit end + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild) { + l |= 8; + world.setData(i, j, k, l, 4); + } + + super.a(world, i, j, k, l, entityhuman); + } + + public void remove(World world, int i, int j, int k, int l, int i1) { + if (!world.isStatic) { + /* CraftBukkit start - drop item in code above, not here + if ((i1 & 8) == 0) { + ItemStack itemstack = new ItemStack(Item.SKULL.id, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } + // CraftBukkit end */ + + super.remove(world, i, j, k, l, i1); + } + } + ``` + +##### General Guidelines +When editing Minecraft's classes, we have a set of rules and guidelines that need to be followed to keep us sane when it comes time for us to update Bukkit. + +**CraftBukkit comments** +Changes to a Minecraft class should be clearly marked using CraftBukkit comments. Here are a few tips to help explain what kind of CraftBukkit comment to use and where to use them: + +* Regardless of what kind of CraftBukkit comment you use, please take care to be explicit and exact with your usage. If the "C" in "CraftBukkit" is capitalised in the example, you should capitalise it when you use it. If the "start" begins with a lowercase "s", you should make sure yours does too. + +* If the change only affects one line of code, you should use an end of line CraftBukkit comment. + + **Examples:** + + If the change is obvious when looking at the diff, then you just need a simple end of line CraftBukkit comment. + + ```java + if (true || minecraftserver.getAllowNether()) { // CraftBukkit + ``` + + If, however, the change is something important to note or difficult to discern, you should include a reason at the end of the end of line CraftBukkit comment. + + ```java + public int fireTicks; // CraftBukkit - private -> public + ``` + + If adding the CraftBukkit comment to the end of the line negatively affects the readability of the code, then you should place the CraftBukkit comment on a new line above the change you made. + + ```java + // CraftBukkit + if (!isEffect && !world.isStatic && world.difficulty >= 2 && world.areChunksLoaded(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2), 10)) { + ``` + +* If the change affects more than one line, you should use a multi-line CraftBukkit comment. + + **Examples:** + + The majority of the time multi-line changes should be accompanied by a reason since they're usually much more complicated than a single line change. We'd like to suggest you follow the same rule as above: if the change is something important to note or difficult to discern, you should include a reason at the end of the end of line CraftBukkit comment, however it is not always clear if this is the case. Looking through the code in the project, you'll see that we sometimes include a reason when we should have left it off and vice versa. + + ```java + // CraftBukkit start - special case dropping so we can get info from the tile entity + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (world.random.nextFloat() < f) { + ItemStack itemstack = new ItemStack(Item.SKULL.id, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } + } + // CraftBukkit end + ```` + + Otherwise, just use a multi-line CraftBukkit comment without a reason. + + ```java + // CraftBukkit start + BlockIgniteEvent event = new BlockIgniteEvent(this.cworld.getBlockAt(i, j, k), BlockIgniteEvent.IgniteCause.LIGHTNING, null); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + world.setTypeIdUpdate(i, j, k, Block.FIRE.id); + } + // CraftBukkit end + ``` + +* CraftBukkit comments should be on the same indentation level of the code block it is in. + + **For example:** + + ```java + if (j == 1) { + // CraftBukkit start - store a reference + ItemStack itemstack4 = playerinventory.getCarried(); + if (itemstack4.count > 0) { + entityhuman.drop(itemstack4.a(1)); + } + + if (itemstack4.count == 0) { + // CraftBukkit end + playerinventory.setCarried((ItemStack) null); + } + } + ``` + +**Other guidelines** + +* When adding imports to a Minecraft class, they should be organised by alphabetical order, separated and grouped by package. + + **For example:** + + ```java + import java.io.ByteArrayInputStream; + import java.io.DataInputStream; + import java.io.IOException; + import java.util.ArrayList; + import java.util.Iterator; + import java.util.Random; + import java.util.concurrent.Callable; + + // CraftBukkit start + import java.io.UnsupportedEncodingException; + import java.util.concurrent.ExecutionException; + import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + import java.util.logging.Level; + import java.util.HashSet; + + import org.bukkit.Bukkit; + import org.bukkit.Location; + import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.craftbukkit.inventory.CraftInventoryView; + import org.bukkit.craftbukkit.inventory.CraftItemStack; + import org.bukkit.craftbukkit.util.LazyPlayerSet; + import org.bukkit.craftbukkit.util.Waitable; + import org.bukkit.craftbukkit.entity.CraftPlayer; + import org.bukkit.craftbukkit.event.CraftEventFactory; + import org.bukkit.entity.Player; + import org.bukkit.event.Event; + import org.bukkit.event.block.Action; + import org.bukkit.event.block.SignChangeEvent; + import org.bukkit.event.player.AsyncPlayerChatEvent; + import org.bukkit.event.player.PlayerAnimationEvent; + import org.bukkit.event.player.PlayerChatEvent; + import org.bukkit.event.player.PlayerCommandPreprocessEvent; + import org.bukkit.event.player.PlayerInteractEntityEvent; + import org.bukkit.event.player.PlayerItemHeldEvent; + import org.bukkit.event.player.PlayerKickEvent; + import org.bukkit.event.player.PlayerMoveEvent; + import org.bukkit.event.player.PlayerTeleportEvent; + import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + import org.bukkit.event.player.PlayerToggleSneakEvent; + import org.bukkit.event.player.PlayerToggleSprintEvent; + import org.bukkit.event.inventory.*; + import org.bukkit.event.inventory.InventoryType.SlotType; + import org.bukkit.event.player.PlayerPortalEvent; + import org.bukkit.event.player.PlayerToggleFlightEvent; + import org.bukkit.inventory.CraftingInventory; + import org.bukkit.inventory.InventoryView; + // CraftBukkit end + ``` + +* Do not remove unused imports if they are not marked by CraftBukkit comments. + +### Commit Message Example +> Provide an example commit for CONTRIBUTING.md. Fixes BUKKIT-1 +> +> The CONTRIBUTING.md is missing an example commit message. Without this +> commit, we are unable to provide potential contributors with a helpful example, +> forcing developers to guess at what an acceptable commit message would look +> like. This commit fixes this issue by providing a clear and informative example +> for contributors to base their work off of. + +### Commit Message Expectations +The first line in a commit message is an imperative statement briefly explaining what the commit is achieving with an associated ticket number from our JIRA, in the form of BUKKIT-#. See the list of acceptable keywords to reference tickets with for more information on this. + +The body of the commit message needs to describe how the code behaves without this change, why this is a problem and how this commit addresses it. The body of the commit message should be restricted by a 78 character, plus newline, limit per line (meaning: once you hit about 78 characters, you should explicitly start a new line in the commit message). + +Acceptable keywords to reference tickets with: + +* **Fixes** BUKKIT-1 - this commit fixes the bug detailed in BUKKIT-1 +* **Adds** BUKKIT-2 - this commit adds the new feature requested by BUKKIT-2 + +You can reference multiple tickets in a single commit message, for example: "Fixes BUKKIT-1, BUKKIT-2" or "Adds BUKKIT-1, BUKKIT-2" without closing punctuation. + +## Submitting the Changes + +* Push your changes to a topic branch in your fork of the repository. +* Submit a pull request to the relevant repository in the Bukkit organization. + * Make sure your pull request meets [our expectations](#pull-request-formatting-expectations) before submitting. + * No merges should be included in any pull requests. +* Update your JIRA ticket to reflect that you have submitted a pull request and are ready for it to be reviewed. + * Include a link to the pull request in the ticket. +* Follow our [Tips to Get Your Pull Request Accepted](#tips-to-get-your-pull-request-accepted). +* **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +### Pull Request Formatting Expectations +#### Title +> [PR Type] Brief summary. Fixes BUKKIT-#### +> PR Type can be B for Bukkit, C for CraftBukkit, B+C for a PR in both sides +> +> Title Example: +> [B+C] Provide an example commit for CONTRIBUTING.md. Fixes BUKKIT-1 + +#### Description: +> ##### The Issue: +> Paragraphs explaining what the issue the PR is meant to be addressing. +> +> ##### Justification for this PR: +> Paragraphs providing justification for the PR +> +> ##### PR Breakdown: +> Paragraphs breaking down what the PR is doing, in detail. +> +> ##### Testing Results and Materials: +> Paragraphs describing what you did to test the code in this PR and links to pre-compiled test binaries and source. +> +> ##### Relevant PR(s): +> This should be links to accompanying PRs, or alternate PRs that attempted to perform the task. Each reference should have a reason attached as to why it is being referenced (for example: "Similar to PR ### but won't empty your Bukkits"). Accompanying PRs need no explanation, but still need to be linked. +> +> B-#### - https://github.com/Bukkit/Bukkit/pull/#### - Reason +> CB-#### - https://github.com/Bukkit/CraftBukkit/pull/#### - Reason +> +> ##### JIRA Ticket: +> BUKKIT-#### - https://bukkit.atlassian.net/browse/BUKKIT-#### +> +> ##### Pull Request Check List (For Your Use): +> +>**General:** +> +>- [ ] Fits Bukkit's Goals +>- [ ] Leaky Ticket Ready +>- [ ] Code Meets Requirements +>- [ ] Code is Documented +>- [ ] Code Addresses Leaky Ticket +>- [ ] Followed Pull Request Format +>- [ ] Tested Code +>- [ ] Included Test Material and Source +> +>**If Applicable:** +> +>- [ ] Importing New Minecraft Classes In Special Commit +>- [ ] Follows Minimal Diff Policy +>- [ ] Uses Proper CraftBukkit Comments +>- [ ] Imports Are Ordered, Separated and Organised Properly + +### Tips to Get Your Pull Request Accepted +Making sure you follow the above conventions is important, but just the beginning. Follow these tips to better the chances of your pull request being accepted and pulled. + +* Your change should [fit with Bukkit's goals](#does-the-change-fit-bukkits-goals). +* Make sure you follow all of our conventions to the letter. +* Make sure your code compiles under Java 6. +* Check for misplaced whitespaces. It may be invisible, but [we notice](https://github.com/Bukkit/CraftBukkit/pull/1070). +* Provide proper JavaDocs where appropriate. + * JavaDocs should detail every limitation, caveat and gotcha the code has. +* Provide proper accompanying documentation where appropriate. +* Test your code and provide testing material. + * For example: adding an event? Test it with a test plugin and provide us with that plugin and its source. +* Make sure to follow coding best practises. +* Your pull request should adhere to our [Pull Request Formatting Expectations](#pull-request-formatting-expectations). +* **Note:** The project is put under a code freeze leading up to the release of a Minecraft update in order to give the Bukkit team a static code base to work on. + +## Useful Resources +* [An example pull request demonstrating the things we look out for](https://github.com/Bukkit/CraftBukkit/pull/1070) +* [Handy gist version of our Pull Request Format Template](https://gist.github.com/EvilSeph/35bb477eaa1dffc5f1d7) +* [More information on contributing](http://wiki.bukkit.org/Getting_Involved) +* [Leaky, Our Issue Tracker (JIRA)](http://leaky.bukkit.org) +* [General GitHub documentation](http://help.github.com/) +* [GitHub pull request documentation](http://help.github.com/send-pull-requests/) +* [Join us on IRC - #bukkitdev @ irc.esper.net](http://wiki.bukkit.org/IRC) diff --git a/vspigot-server/LGPL.txt b/vspigot-server/LGPL.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/vspigot-server/LGPL.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vspigot-server/LICENCE.txt b/vspigot-server/LICENCE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/vspigot-server/LICENCE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/vspigot-server/README.md b/vspigot-server/README.md new file mode 100644 index 0000000..dc0e0c9 --- /dev/null +++ b/vspigot-server/README.md @@ -0,0 +1,19 @@ +Spigot +=========== + +A Spigot-API and Bukkit implementation + +Website: [http://spigotmc.org](http://spigotmc.org) +Bugs/Suggestions: [http://www.spigotmc.org/forums/bugs-feature-requests.8/](http://www.spigotmc.org/forums/bugs-feature-requests.8/) +Contributing Guidelines: [CONTRIBUTING.md](https://github.com/SpigotMC/Spigot-API/blob/master/CONTRIBUTING.md) + +Compilation +----------- + +We use maven to handle our dependencies. + +* Install [Maven 3](http://maven.apache.org/download.html) +* Check out and install [Spigot-API](http://github.com/SpigotMC/Spigot) + * *Note*: this is not needed as the repository we use has Spigot-API too, but you might have a newer one (with your own changes :D) +* Check out this repo and: `mvn clean package` +# ValorSpigot-Server diff --git a/vspigot-server/deprecation-mappings.at b/vspigot-server/deprecation-mappings.at new file mode 100644 index 0000000..3e84195 --- /dev/null +++ b/vspigot-server/deprecation-mappings.at @@ -0,0 +1,62 @@ +public+synthetic org/bukkit/Bukkit/getOnlinePlayers()[Lorg/bukkit/entity/Player; +public+synthetic org/bukkit/Server/getOnlinePlayers()[Lorg/bukkit/entity/Player; + +public+synthetic org/bukkit/entity/Damageable/damage(I)V +public+synthetic org/bukkit/entity/Damageable/damage(ILorg/bukkit/entity/Entity;)V +public+synthetic org/bukkit/entity/Damageable/getHealth()I +public+synthetic org/bukkit/entity/Damageable/getMaxHealth()I +public+synthetic org/bukkit/entity/Damageable/setHealth(I)V +public+synthetic org/bukkit/entity/Damageable/setMaxHealth(I)V + +public+synthetic org/bukkit/entity/LivingEntity/getLastDamage()I +public+synthetic org/bukkit/entity/LivingEntity/setLastDamage(I)V + +public+synthetic org/bukkit/entity/Minecart/getDamage()I +public+synthetic org/bukkit/entity/Minecart/setDamage(I)V + +public+synthetic org/bukkit/entity/Projectile/getShooter()Lorg/bukkit/entity/LivingEntity; +public+synthetic org/bukkit/entity/Projectile/setShooter(Lorg/bukkit/entity/LivingEntity;)V + +public+synthetic org/bukkit/event/entity/EntityDamageEvent/getDamage()I +public+synthetic org/bukkit/event/entity/EntityDamageEvent/setDamage(I)V + +public+synthetic org/bukkit/event/entity/EntityRegainHealthEvent/getAmount()I +public+synthetic org/bukkit/event/entity/EntityRegainHealthEvent/setAmount(I)V + +public+synthetic org/bukkit/event/vehicle/VehicleDamageEvent/getDamage()I +public+synthetic org/bukkit/event/vehicle/VehicleDamageEvent/setDamage(I)V + +# CraftBukkit +public+synthetic org/bukkit/craftbukkit/v1_7_R4/CraftServer/getOnlinePlayers()[Lorg/bukkit/entity/Player; + +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftArrow/getShooter()Lorg/bukkit/entity/LivingEntity; +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftArrow/setShooter(Lorg/bukkit/entity/LivingEntity;)V + +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftEnderDragonPart/damage(I)V +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftEnderDragonPart/damage(ILorg/bukkit/entity/Entity;)V +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftEnderDragonPart/getHealth()I +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftEnderDragonPart/getMaxHealth()I +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftEnderDragonPart/setHealth(I)V +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftEnderDragonPart/setMaxHealth(I)V + + +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftFireball/getShooter()Lorg/bukkit/entity/LivingEntity; +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftFireball/setShooter(Lorg/bukkit/entity/LivingEntity;)V + +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftFish/getShooter()Lorg/bukkit/entity/LivingEntity; +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftFish/setShooter(Lorg/bukkit/entity/LivingEntity;)V + +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/damage(I)V +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/damage(ILorg/bukkit/entity/Entity;)V +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/getHealth()I +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/getMaxHealth()I +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/setHealth(I)V +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/setMaxHealth(I)V +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/getLastDamage()I +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftLivingEntity/setLastDamage(I)V + +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftMinecart/getDamage()I +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftMinecart/setDamage(I)V + +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftProjectile/getShooter()Lorg/bukkit/entity/LivingEntity; +public+synthetic org/bukkit/craftbukkit/v1_7_R4/entity/CraftProjectile/setShooter(Lorg/bukkit/entity/LivingEntity;)V diff --git a/vspigot-server/deprecation-mappings.csrg b/vspigot-server/deprecation-mappings.csrg new file mode 100644 index 0000000..76354b5 --- /dev/null +++ b/vspigot-server/deprecation-mappings.csrg @@ -0,0 +1,28 @@ + +org/bukkit/Bukkit _INVALID_getOnlinePlayers ()[Lorg/bukkit/entity/Player; getOnlinePlayers +org/bukkit/Server _INVALID_getOnlinePlayers ()[Lorg/bukkit/entity/Player; getOnlinePlayers + +org/bukkit/entity/Damageable _INVALID_damage (I)V damage +org/bukkit/entity/Damageable _INVALID_damage (ILorg/bukkit/entity/Entity;)V damage +org/bukkit/entity/Damageable _INVALID_getHealth ()I getHealth +org/bukkit/entity/Damageable _INVALID_getMaxHealth ()I getMaxHealth +org/bukkit/entity/Damageable _INVALID_setHealth (I)V setHealth +org/bukkit/entity/Damageable _INVALID_setMaxHealth (I)V setMaxHealth + +org/bukkit/entity/LivingEntity _INVALID_getLastDamage ()I getLastDamage +org/bukkit/entity/LivingEntity _INVALID_setLastDamage (I)V setLastDamage + +org/bukkit/entity/Minecart _INVALID_getDamage ()I getDamage +org/bukkit/entity/Minecart _INVALID_setDamage (I)V setDamage + +org/bukkit/entity/Projectile _INVALID_getShooter ()Lorg/bukkit/entity/LivingEntity; getShooter +org/bukkit/entity/Projectile _INVALID_setShooter (Lorg/bukkit/entity/LivingEntity;)V setShooter + +org/bukkit/event/entity/EntityDamageEvent _INVALID_getDamage ()I getDamage +org/bukkit/event/entity/EntityDamageEvent _INVALID_setDamage (I)V setDamage + +org/bukkit/event/entity/EntityRegainHealthEvent _INVALID_getAmount ()I getAmount +org/bukkit/event/entity/EntityRegainHealthEvent _INVALID_setAmount (I)V setAmount + +org/bukkit/event/vehicle/VehicleDamageEvent _INVALID_getDamage ()I getDamage +org/bukkit/event/vehicle/VehicleDamageEvent _INVALID_setDamage (I)V setDamage diff --git a/vspigot-server/pom.xml b/vspigot-server/pom.xml new file mode 100644 index 0000000..16e384c --- /dev/null +++ b/vspigot-server/pom.xml @@ -0,0 +1,341 @@ + + 4.0.0 + net.valorhcf + vspigot-server + jar + 1.7.10-R0.1-SNAPSHOT + vSpigot + https://valorhcf.net + + + UTF-8 + ${project.name}-${project.version} + 4.11 + 1.7.10 + 1_7_R4 + git-Bukkit- + + 1.8 + 1.8 + + + + net.valorhcf + vspigot-parent + dev-SNAPSHOT + ../pom.xml + + + + + spigotmc-public + https://hub.spigotmc.org/nexus/content/groups/public/ + + + + + + net.valorhcf + vspigot-api + ${project.version} + jar + compile + + + org.bukkit + minecraft-server + ${minecraft.version} + jar + compile + + + net.sf.jopt-simple + jopt-simple + 3.2 + jar + compile + + + net.jafama + jafama + 2.1.0 + + + + jline + jline + 2.6 + jar + compile + + + org.fusesource.jansi + jansi + 1.8 + jar + compile + + + org.xerial + sqlite-jdbc + 3.7.2 + jar + compile + + + mysql + mysql-connector-java + 5.1.14 + jar + compile + + + com.google.code.gson + gson + 2.8.6 + + + org.projectlombok + lombok + 1.16.10 + + + + junit + junit + ${junit.version} + test + + + org.hamcrest + hamcrest-library + 1.3 + test + + + io.netty + netty-all + 4.0.23.Final + + + it.unimi.dsi + fastutil + 7.2.1 + + + + + + clean install + + + net.md-5 + scriptus + 0.2 + + + ex-vspigot + + git-vspigot-%s + ../ + vspigot.desc + + initialize + + describe + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.1 + + + + org.bukkit.craftbukkit.Main + CraftBukkit + 1.7.10-R0.1-SNAPSHOT + Bukkit Team + Bukkit + ${api.version} + Bukkit Team + true + ${maven.build.timestamp} + + + + net/bukkit/ + + true + + + + com/bukkit/ + + true + + + + org/bukkit/ + + true + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.0 + + + package + + shade + + + + + org.bouncycastle + net.minecraft.v${minecraft_version}.org.bouncycastle + + + joptsimple + org.bukkit.craftbukkit.libs.joptsimple + + + jline + org.bukkit.craftbukkit.libs.jline + + + org.ibex + org.bukkit.craftbukkit.libs.org.ibex + + + org.gjt + org.bukkit.craftbukkit.libs.org.gjt + + + com.google.gson + org.bukkit.craftbukkit.libs.com.google.gson + + + io.netty + net.minecraft.util.io.netty + + + + org.bukkit.craftbukkit + org.bukkit.craftbukkit.v${minecraft_version} + + org.bukkit.craftbukkit.Main* + + + + net.minecraft.server + net.minecraft.server.v${minecraft_version} + + + + + org.bukkit:minecraft-server + + net/minecraft/util/io/netty/** + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + eclipse + + true + + + + + org.codehaus.plexus + plexus-compiler-eclipse + 2.5.0-spigotmc + + + + + net.md-5 + specialsource-maven-plugin + 1.2.1 + + + package + + remap + + + ${project.basedir}/deprecation-mappings.csrg + ${project.basedir}/deprecation-mappings.at + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + com.lukegb.mojo + + gitdescribe-maven-plugin + + [1.3,) + + gitdescribe + + + + + + + + + + + + + + + diff --git a/vspigot-server/src/main/java/com/destroystokyo/paper/event/server/ServerTickEndEvent.java b/vspigot-server/src/main/java/com/destroystokyo/paper/event/server/ServerTickEndEvent.java new file mode 100644 index 0000000..3cd7401 --- /dev/null +++ b/vspigot-server/src/main/java/com/destroystokyo/paper/event/server/ServerTickEndEvent.java @@ -0,0 +1,56 @@ +package com.destroystokyo.paper.event.server; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * Called when the server has finished ticking the main loop + */ +public class ServerTickEndEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + private final int tickNumber; + private final double tickDuration; + private final long timeEnd; + + public ServerTickEndEvent(int tickNumber, double tickDuration, long timeRemaining) { + this.tickNumber = tickNumber; + this.tickDuration = tickDuration; + this.timeEnd = System.nanoTime() + timeRemaining; + } + + /** + * @return What tick this was since start (first tick = 1) + */ + public int getTickNumber() { + return tickNumber; + } + + /** + * @return Time in milliseconds of how long this tick took + */ + public double getTickDuration() { + return tickDuration; + } + + /** + * Amount of nanoseconds remaining before the next tick should start. + *

            + * If this value is negative, then that means the server has exceeded the tick time limit and TPS has been lost. + *

            + * Method will continously return the updated time remaining value. (return value is not static) + * + * @return Amount of nanoseconds remaining before the next tick should start + */ + public long getTimeRemaining() { + return this.timeEnd - System.nanoTime(); + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-server/src/main/java/com/destroystokyo/paper/event/server/ServerTickStartEvent.java b/vspigot-server/src/main/java/com/destroystokyo/paper/event/server/ServerTickStartEvent.java new file mode 100644 index 0000000..6aa0d1f --- /dev/null +++ b/vspigot-server/src/main/java/com/destroystokyo/paper/event/server/ServerTickStartEvent.java @@ -0,0 +1,29 @@ +package com.destroystokyo.paper.event.server; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class ServerTickStartEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + private final int tickNumber; + + public ServerTickStartEvent(int tickNumber) { + this.tickNumber = tickNumber; + } + + /** + * @return What tick this is going be since start (first tick = 1) + */ + public int getTickNumber() { + return tickNumber; + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/vspigot-server/src/main/java/io/kohi/kspigot/potion/PotionMatcher.java b/vspigot-server/src/main/java/io/kohi/kspigot/potion/PotionMatcher.java new file mode 100644 index 0000000..a8f9dca --- /dev/null +++ b/vspigot-server/src/main/java/io/kohi/kspigot/potion/PotionMatcher.java @@ -0,0 +1,60 @@ +package io.kohi.kspigot.potion; + +import org.bukkit.potion.PotionType; + +import java.util.Map; + +public class PotionMatcher +{ + private PotionType type; + private Integer level; + private Boolean extended; + private Boolean splash; + + public PotionMatcher(Map conf) + { + if (conf.containsKey("type")) + { + try + { + type = PotionType.valueOf((String) conf.get("type")); + } + catch (IllegalArgumentException ex) + { + } + } + if (conf.containsKey("level")) + { + level = (Integer) conf.get("level"); + } + if (conf.containsKey("extended")) + { + extended = (Boolean) conf.get("extended"); + } + if (conf.containsKey("splash")) + { + splash = (Boolean) conf.get("splash"); + } + } + + public boolean matches(int damage) + { + if (type != null && type.getDamageValue() != (damage & 15)) + { + return false; + } + if (level != null && level != ((damage >> 5) & 1) + 1) + { + return false; + } + if (extended != null && extended != (((damage >> 6) & 1) == 1)) + { + return false; + } + if (splash != null && splash != (((damage >> 14) & 1) == 1)) + { + return false; + } + return true; + } +} diff --git a/vspigot-server/src/main/java/io/kohi/kspigot/potion/PotionsConfig.java b/vspigot-server/src/main/java/io/kohi/kspigot/potion/PotionsConfig.java new file mode 100644 index 0000000..8d13123 --- /dev/null +++ b/vspigot-server/src/main/java/io/kohi/kspigot/potion/PotionsConfig.java @@ -0,0 +1,49 @@ +package io.kohi.kspigot.potion; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.bukkit.configuration.file.YamlConfiguration; + +public class PotionsConfig +{ + private static final YamlConfiguration conf = YamlConfiguration.loadConfiguration(new File("config/server", "potions.yml")); // MineHQ + private static final List disableBrewing = new ArrayList(); + private static final Map disableBrewingCache = new HashMap(); + + static + { + List disable = conf.getList("disable-brewing"); + if (disable != null) + { + for (Object obj : disable) + { + if (obj instanceof Map) + { + disableBrewing.add(new PotionMatcher((Map) obj)); + } + } + } + } + + public static boolean isBrewingDisabled(int damage) + { + Boolean cached = disableBrewingCache.get(damage); + if (cached != null) + { + return cached; + } + for (PotionMatcher potion : disableBrewing) + { + if (potion.matches(damage)) + { + disableBrewingCache.put(damage, true); + return true; + } + } + disableBrewingCache.put(damage, false); + return false; + } +} diff --git a/vspigot-server/src/main/java/jline/AnsiWindowsTerminal.java b/vspigot-server/src/main/java/jline/AnsiWindowsTerminal.java new file mode 100644 index 0000000..a638036 --- /dev/null +++ b/vspigot-server/src/main/java/jline/AnsiWindowsTerminal.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * MODIFICATIONS: methods to deal with wrapping the output stream. + */ + +package jline; + +import org.fusesource.jansi.AnsiConsole; +import org.fusesource.jansi.AnsiOutputStream; +import org.fusesource.jansi.WindowsAnsiOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; + +/** + * ANSI-supported {@link WindowsTerminal}. + * + * @since 2.0 + */ +public class AnsiWindowsTerminal + extends WindowsTerminal +{ + private final boolean ansiSupported = detectAnsiSupport(); + + @Override + public OutputStream wrapOutIfNeeded(OutputStream out) { + return wrapOutputStream(out); + } + + /** + * Returns an ansi output stream handler. We return whatever was + * passed if we determine we cannot handle ansi based on Kernel32 calls. + * + * @return an @{link AltWindowAnsiOutputStream} instance or the passed + * stream. + */ + private static OutputStream wrapOutputStream(final OutputStream stream) { + String os = System.getProperty("os.name"); + if( os.startsWith("Windows") ) { + // On windows we know the console does not interpret ANSI codes.. + try { + return new WindowsAnsiOutputStream(stream); + } catch (Throwable ignore) { + // this happens when JNA is not in the path.. or + // this happens when the stdout is being redirected to a file. + } + // Use the ANSIOutputStream to strip out the ANSI escape sequences. + return new AnsiOutputStream(stream); + } + return stream; + } + + private static boolean detectAnsiSupport() { + AnsiConsole.systemInstall(); // CraftBukkit - install Windows JNI library + OutputStream out = AnsiConsole.wrapOutputStream(new ByteArrayOutputStream()); + try { + out.close(); + } + catch (Exception e) { + // ignore; + } + return out instanceof WindowsAnsiOutputStream; + } + + public AnsiWindowsTerminal() throws Exception { + super(); + } + + @Override + public boolean isAnsiSupported() { + return ansiSupported; + } + + @Override + public boolean hasWeirdWrap() { + return false; + } +} diff --git a/vspigot-server/src/main/java/jline/internal/TerminalLineSettings.java b/vspigot-server/src/main/java/jline/internal/TerminalLineSettings.java new file mode 100644 index 0000000..b4b2409 --- /dev/null +++ b/vspigot-server/src/main/java/jline/internal/TerminalLineSettings.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ + +package jline.internal; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Provides access to terminal line settings via stty. + * + * @author Marc Prud'hommeaux + * @author Dale Kemp + * @author Jason Dillon + * @author Jean-Baptiste Onofré + * @since 2.0 + */ +public final class TerminalLineSettings +{ + public static final String JLINE_STTY = "jline.stty"; + + public static final String DEFAULT_STTY = "stty"; + + public static final String JLINE_SH = "jline.sh"; + + public static final String DEFAULT_SH = "sh"; + + private String sttyCommand; + + private String shCommand; + + private String config; + + private long configLastFetched; + + public TerminalLineSettings() throws IOException, InterruptedException { + sttyCommand = Configuration.getString(JLINE_STTY, DEFAULT_STTY); + shCommand = Configuration.getString(JLINE_SH, DEFAULT_SH); + config = get("-a"); + configLastFetched = System.currentTimeMillis(); + + Log.debug("Config: ", config); + + // sanity check + if (config.length() == 0) { + throw new IOException(MessageFormat.format("Unrecognized stty code: {0}", config)); + } + } + + public String getConfig() { + return config; + } + + public void restore() throws IOException, InterruptedException { + set("sane"); + } + + public String get(final String args) throws IOException, InterruptedException { + return stty(args); + } + + public void set(final String args) throws IOException, InterruptedException { + stty(args); + } + + /** + *

            + * Get the value of a stty property, including the management of a cache. + *

            + * + * @param name the stty property. + * @return the stty property value. + */ + public int getProperty(String name) { + assert name != null; + // CraftBukkit start + long currentTime = System.currentTimeMillis(); + + try { + // tty properties are cached so we don't have to worry too much about getting term widht/height + if (config == null || currentTime - configLastFetched > 1000) { + config = get("-a"); + } + } catch (Exception e) { + Log.debug("Failed to query stty ", name, "\n", e); + } + + // always update the last fetched time and try to parse the output + if (currentTime - configLastFetched > 1000) { + configLastFetched = currentTime; + } + + return this.getProperty(name, config); + // CraftBukkit end + } + + /** + *

            + * Parses a stty output (provided by stty -a) and return the value of a given property. + *

            + * + * @param name property name. + * @param stty string resulting of stty -a execution. + * @return value of the given property. + */ + protected static int getProperty(String name, String stty) { + // try the first kind of regex + Pattern pattern = Pattern.compile(name + "\\s+=\\s+([^;]*)[;\\n\\r]"); + Matcher matcher = pattern.matcher(stty); + if (!matcher.find()) { + // try a second kind of regex + pattern = Pattern.compile(name + "\\s+([^;]*)[;\\n\\r]"); + matcher = pattern.matcher(stty); + if (!matcher.find()) { + // try a second try of regex + pattern = Pattern.compile("(\\S*)\\s+" + name); + matcher = pattern.matcher(stty); + if (!matcher.find()) { + return -1; + } + } + } + return parseControlChar(matcher.group(1)); + } + + private static int parseControlChar(String str) { + // under + if ("".equals(str)) { + return -1; + } + // octal + if (str.charAt(0) == '0') { + return Integer.parseInt(str, 8); + } + // decimal + if (str.charAt(0) >= '1' && str.charAt(0) <= '9') { + return Integer.parseInt(str, 10); + } + // control char + if (str.charAt(0) == '^') { + if (str.charAt(1) == '?') { + return 127; + } else { + return str.charAt(1) - 64; + } + } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') { + if (str.charAt(2) == '^') { + if (str.charAt(3) == '?') { + return 127 + 128; + } else { + return str.charAt(3) - 64 + 128; + } + } else { + return str.charAt(2) + 128; + } + } else { + return str.charAt(0); + } + } + + private String stty(final String args) throws IOException, InterruptedException { + assert args != null; + return exec(String.format("%s %s < /dev/tty", sttyCommand, args)); + } + + private String exec(final String cmd) throws IOException, InterruptedException { + assert cmd != null; + return exec(shCommand, "-c", cmd); + } + + private String exec(final String... cmd) throws IOException, InterruptedException { + assert cmd != null; + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + Log.trace("Running: ", cmd); + + Process p = Runtime.getRuntime().exec(cmd); + + InputStream in = null; + InputStream err = null; + OutputStream out = null; + try { + int c; + in = p.getInputStream(); + while ((c = in.read()) != -1) { + bout.write(c); + } + err = p.getErrorStream(); + while ((c = err.read()) != -1) { + bout.write(c); + } + out = p.getOutputStream(); + p.waitFor(); + } + finally { + close(in, out, err); + } + + String result = bout.toString(); + + Log.trace("Result: ", result); + + return result; + } + + private static void close(final Closeable... closeables) { + for (Closeable c : closeables) { + try { + c.close(); + } + catch (Exception e) { + // Ignore + } + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/AttributeRanged.java b/vspigot-server/src/main/java/net/minecraft/server/AttributeRanged.java new file mode 100644 index 0000000..cd613d2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/AttributeRanged.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +public class AttributeRanged extends AttributeBase { + + private final double a; + public double b; // Spigot + private String c; + + public AttributeRanged(String s, double d0, double d1, double d2) { + super(s, d0); + this.a = d1; + this.b = d2; + if (d1 > d2) { + throw new IllegalArgumentException("Minimum value cannot be bigger than maximum value!"); + } else if (d0 < d1) { + throw new IllegalArgumentException("Default value cannot be lower than minimum value!"); + } else if (d0 > d2) { + throw new IllegalArgumentException("Default value cannot be bigger than maximum value!"); + } + } + + public AttributeRanged a(String s) { + this.c = s; + return this; + } + + public String f() { + return this.c; + } + + public double a(double d0) { + if (d0 < this.a) { + d0 = this.a; + } + + if (d0 > this.b) { + d0 = this.b; + } + + return d0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BanEntrySerializer.java b/vspigot-server/src/main/java/net/minecraft/server/BanEntrySerializer.java new file mode 100644 index 0000000..3b4b596 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BanEntrySerializer.java @@ -0,0 +1,89 @@ +package net.minecraft.server; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.util.Date; +import java.util.UUID; + +import net.minecraft.util.com.google.gson.JsonDeserializationContext; +import net.minecraft.util.com.google.gson.JsonDeserializer; +import net.minecraft.util.com.google.gson.JsonElement; +import net.minecraft.util.com.google.gson.JsonObject; +import net.minecraft.util.com.google.gson.JsonSerializationContext; +import net.minecraft.util.com.google.gson.JsonSerializer; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +class BanEntrySerializer implements JsonDeserializer, JsonSerializer { + + final UserCache a; + + private BanEntrySerializer(UserCache usercache) { + this.a = usercache; + } + + public JsonElement a(UserCacheEntry usercacheentry, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + jsonobject.addProperty("name", usercacheentry.a().getName()); + UUID uuid = usercacheentry.a().getId(); + + jsonobject.addProperty("uuid", uuid == null ? "" : uuid.toString()); + jsonobject.addProperty("expiresOn", UserCache.a.format(usercacheentry.b())); + return jsonobject; + } + + public UserCacheEntry a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) { + if (jsonelement.isJsonObject()) { + JsonObject jsonobject = jsonelement.getAsJsonObject(); + JsonElement jsonelement1 = jsonobject.get("name"); + JsonElement jsonelement2 = jsonobject.get("uuid"); + JsonElement jsonelement3 = jsonobject.get("expiresOn"); + + if (jsonelement1 != null && jsonelement2 != null) { + String s = jsonelement2.getAsString(); + String s1 = jsonelement1.getAsString(); + Date date = null; + + if (jsonelement3 != null) { + try { + date = UserCache.a.parse(jsonelement3.getAsString()); + } catch (ParseException parseexception) { + date = null; + } + } + + if (s1 != null && s != null) { + UUID uuid; + + try { + uuid = UUID.fromString(s); + } catch (Throwable throwable) { + return null; + } + + UserCacheEntry usercacheentry = new UserCacheEntry(this.a, new GameProfile(uuid, s1), date, (GameProfileLookup) null); + + return usercacheentry; + } else { + return null; + } + } else { + return null; + } + } else { + return null; + } + } + + public JsonElement serialize(Object object, Type type, JsonSerializationContext jsonserializationcontext) { + return this.a((UserCacheEntry) object, type, jsonserializationcontext); + } + + public Object deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) { + return this.a(jsonelement, type, jsondeserializationcontext); + } + + BanEntrySerializer(UserCache usercache, GameProfileLookup gameprofilelookup) { + this(usercache); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BiomeBase.java b/vspigot-server/src/main/java/net/minecraft/server/BiomeBase.java new file mode 100644 index 0000000..5e90488 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BiomeBase.java @@ -0,0 +1,391 @@ +package net.minecraft.server; + +import net.minecraft.util.com.google.common.collect.Sets; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Set; + +public abstract class BiomeBase { + + private static final Logger aC = LogManager.getLogger(); + protected static final BiomeTemperature a = new BiomeTemperature(0.1F, 0.2F); + protected static final BiomeTemperature b = new BiomeTemperature(-0.5F, 0.0F); + protected static final BiomeTemperature c = new BiomeTemperature(-1.0F, 0.1F); + protected static final BiomeTemperature d = new BiomeTemperature(-1.8F, 0.1F); + protected static final BiomeTemperature e = new BiomeTemperature(0.125F, 0.05F); + protected static final BiomeTemperature f = new BiomeTemperature(0.2F, 0.2F); + protected static final BiomeTemperature g = new BiomeTemperature(0.45F, 0.3F); + protected static final BiomeTemperature h = new BiomeTemperature(1.5F, 0.025F); + protected static final BiomeTemperature i = new BiomeTemperature(1.0F, 0.5F); + protected static final BiomeTemperature j = new BiomeTemperature(0.0F, 0.025F); + protected static final BiomeTemperature k = new BiomeTemperature(0.1F, 0.8F); + protected static final BiomeTemperature l = new BiomeTemperature(0.2F, 0.3F); + protected static final BiomeTemperature m = new BiomeTemperature(-0.2F, 0.1F); + private static final BiomeBase[] biomes = new BiomeBase[256]; + public static final Set n = Sets.newHashSet(); + public static final BiomeBase OCEAN = (new BiomeOcean(0)).b(112).a("Ocean").a(c); + public static final BiomeBase PLAINS = (new BiomePlains(1)).b(9286496).a("Plains"); + public static final BiomeBase DESERT = (new BiomeDesert(2)).b(16421912).a("Desert").b().a(2.0F, 0.0F).a(e); + public static final BiomeBase EXTREME_HILLS = (new BiomeBigHills(3, false)).b(6316128).a("Extreme Hills").a(i).a(0.2F, 0.3F); + public static final BiomeBase FOREST = (new BiomeForest(4, 0)).b(353825).a("Forest"); + public static final BiomeBase TAIGA = (new BiomeTaiga(5, 0)).b(747097).a("Taiga").a(5159473).a(0.25F, 0.8F).a(f); + public static final BiomeBase SWAMPLAND = (new BiomeSwamp(6)).b(522674).a("Swampland").a(9154376).a(m).a(0.8F, 0.9F); + public static final BiomeBase RIVER = (new BiomeRiver(7)).b(255).a("River").a(b); + public static final BiomeBase HELL = (new BiomeHell(8)).b(16711680).a("Hell").b().a(2.0F, 0.0F); + public static final BiomeBase SKY = (new BiomeTheEnd(9)).b(8421631).a("Sky").b(); + public static final BiomeBase FROZEN_OCEAN = (new BiomeOcean(10)).b(9474208).a("FrozenOcean").c().a(c).a(0.0F, 0.5F); + public static final BiomeBase FROZEN_RIVER = (new BiomeRiver(11)).b(10526975).a("FrozenRiver").c().a(b).a(0.0F, 0.5F); + public static final BiomeBase ICE_PLAINS = (new BiomeIcePlains(12, false)).b(16777215).a("Ice Plains").c().a(0.0F, 0.5F).a(e); + public static final BiomeBase ICE_MOUNTAINS = (new BiomeIcePlains(13, false)).b(10526880).a("Ice Mountains").c().a(g).a(0.0F, 0.5F); + public static final BiomeBase MUSHROOM_ISLAND = (new BiomeMushrooms(14)).b(16711935).a("MushroomIsland").a(0.9F, 1.0F).a(l); + public static final BiomeBase MUSHROOM_SHORE = (new BiomeMushrooms(15)).b(10486015).a("MushroomIslandShore").a(0.9F, 1.0F).a(j); + public static final BiomeBase BEACH = (new BiomeBeach(16)).b(16440917).a("Beach").a(0.8F, 0.4F).a(j); + public static final BiomeBase DESERT_HILLS = (new BiomeDesert(17)).b(13786898).a("DesertHills").b().a(2.0F, 0.0F).a(g); + public static final BiomeBase FOREST_HILLS = (new BiomeForest(18, 0)).b(2250012).a("ForestHills").a(g); + public static final BiomeBase TAIGA_HILLS = (new BiomeTaiga(19, 0)).b(1456435).a("TaigaHills").a(5159473).a(0.25F, 0.8F).a(g); + public static final BiomeBase SMALL_MOUNTAINS = (new BiomeBigHills(20, true)).b(7501978).a("Extreme Hills Edge").a(i.a()).a(0.2F, 0.3F); + public static final BiomeBase JUNGLE = (new BiomeJungle(21, false)).b(5470985).a("Jungle").a(5470985).a(0.95F, 0.9F); + public static final BiomeBase JUNGLE_HILLS = (new BiomeJungle(22, false)).b(2900485).a("JungleHills").a(5470985).a(0.95F, 0.9F).a(g); + public static final BiomeBase JUNGLE_EDGE = (new BiomeJungle(23, true)).b(6458135).a("JungleEdge").a(5470985).a(0.95F, 0.8F); + public static final BiomeBase DEEP_OCEAN = (new BiomeOcean(24)).b(48).a("Deep Ocean").a(d); + public static final BiomeBase STONE_BEACH = (new BiomeStoneBeach(25)).b(10658436).a("Stone Beach").a(0.2F, 0.3F).a(k); + public static final BiomeBase COLD_BEACH = (new BiomeBeach(26)).b(16445632).a("Cold Beach").a(0.05F, 0.3F).a(j).c(); + public static final BiomeBase BIRCH_FOREST = (new BiomeForest(27, 2)).a("Birch Forest").b(3175492); + public static final BiomeBase BIRCH_FOREST_HILLS = (new BiomeForest(28, 2)).a("Birch Forest Hills").b(2055986).a(g); + public static final BiomeBase ROOFED_FOREST = (new BiomeForest(29, 3)).b(4215066).a("Roofed Forest"); + public static final BiomeBase COLD_TAIGA = (new BiomeTaiga(30, 0)).b(3233098).a("Cold Taiga").a(5159473).c().a(-0.5F, 0.4F).a(f).c(16777215); + public static final BiomeBase COLD_TAIGA_HILLS = (new BiomeTaiga(31, 0)).b(2375478).a("Cold Taiga Hills").a(5159473).c().a(-0.5F, 0.4F).a(g).c(16777215); + public static final BiomeBase MEGA_TAIGA = (new BiomeTaiga(32, 1)).b(5858897).a("Mega Taiga").a(5159473).a(0.3F, 0.8F).a(f); + public static final BiomeBase MEGA_TAIGA_HILLS = (new BiomeTaiga(33, 1)).b(4542270).a("Mega Taiga Hills").a(5159473).a(0.3F, 0.8F).a(g); + public static final BiomeBase EXTREME_HILLS_PLUS = (new BiomeBigHills(34, true)).b(5271632).a("Extreme Hills+").a(i).a(0.2F, 0.3F); + public static final BiomeBase SAVANNA = (new BiomeSavanna(35)).b(12431967).a("Savanna").a(1.2F, 0.0F).b().a(e); + public static final BiomeBase SAVANNA_PLATEAU = (new BiomeSavanna(36)).b(10984804).a("Savanna Plateau").a(1.0F, 0.0F).b().a(h); + public static final BiomeBase MESA = (new BiomeMesa(37, false, false)).b(14238997).a("Mesa"); + public static final BiomeBase MESA_PLATEAU_F = (new BiomeMesa(38, false, true)).b(11573093).a("Mesa Plateau F").a(h); + public static final BiomeBase MESA_PLATEAU = (new BiomeMesa(39, false, false)).b(13274213).a("Mesa Plateau").a(h); + protected static final NoiseGenerator3 ac; + protected static final NoiseGenerator3 ad; + protected static final WorldGenTallPlant ae; + public String af; + public int ag; + public int ah; + public Block ai; + public int aj; + public Block ak; + public int al; + public float am; + public float an; + public float temperature; + public float humidity; + public int aq; + public BiomeDecorator ar; + protected List as; + protected List at; + protected List au; + protected List av; + protected boolean aw; + protected boolean ax; + public final int id; + protected WorldGenTrees az; + protected WorldGenBigTree aA; + protected WorldGenSwampTree aB; + + protected BiomeBase(int i) { + this.ai = Blocks.GRASS; + this.aj = 0; + this.ak = Blocks.DIRT; + this.al = 5169201; + this.am = a.a; + this.an = a.b; + this.temperature = 0.5F; + this.humidity = 0.5F; + this.aq = 16777215; + this.as = new ArrayList(); + this.at = new ArrayList(); + this.au = new ArrayList(); + this.av = new ArrayList(); + this.ax = true; + this.az = new WorldGenTrees(false); + this.aA = new WorldGenBigTree(false); + this.aB = new WorldGenSwampTree(); + this.id = i; + biomes[i] = this; + this.ar = this.a(); + this.at.add(new BiomeMeta(EntitySheep.class, 6, 4, 4)); // MineHQ - less sheep + this.at.add(new BiomeMeta(EntityPig.class, 10, 4, 4)); + this.at.add(new BiomeMeta(EntityChicken.class, 10, 4, 4)); + this.at.add(new BiomeMeta(EntityCow.class, 14, 4, 4)); // MineHQ - more cows + this.as.add(new BiomeMeta(EntitySpider.class, 100, 4, 4)); + this.as.add(new BiomeMeta(EntityZombie.class, 100, 4, 4)); + this.as.add(new BiomeMeta(EntitySkeleton.class, 100, 4, 4)); + this.as.add(new BiomeMeta(EntityCreeper.class, 100, 4, 4)); + this.as.add(new BiomeMeta(EntitySlime.class, 100, 4, 4)); + this.as.add(new BiomeMeta(EntityEnderman.class, 10, 1, 4)); + this.as.add(new BiomeMeta(EntityWitch.class, 5, 1, 1)); + this.au.add(new BiomeMeta(EntitySquid.class, 10, 4, 4)); + this.av.add(new BiomeMeta(EntityBat.class, 10, 8, 8)); + } + + protected BiomeDecorator a() { + return new BiomeDecorator(); + } + + protected BiomeBase a(float f, float f1) { + if (f > 0.1F && f < 0.2F) { + throw new IllegalArgumentException("Please avoid temperatures in the range 0.1 - 0.2 because of snow"); + } else { + this.temperature = f; + this.humidity = f1; + return this; + } + } + + protected final BiomeBase a(BiomeTemperature biometemperature) { + this.am = biometemperature.a; + this.an = biometemperature.b; + return this; + } + + protected BiomeBase b() { + this.ax = false; + return this; + } + + public WorldGenTreeAbstract a(Random random) { + return (WorldGenTreeAbstract) (random.nextInt(10) == 0 ? this.aA : this.az); + } + + public WorldGenerator b(Random random) { + return new WorldGenGrass(Blocks.LONG_GRASS, 1); + } + + public String a(Random random, int i, int j, int k) { + return random.nextInt(3) > 0 ? BlockFlowers.b[0] : BlockFlowers.a[0]; + } + + protected BiomeBase c() { + this.aw = true; + return this; + } + + protected BiomeBase a(String s) { + this.af = s; + return this; + } + + protected BiomeBase a(int i) { + this.al = i; + return this; + } + + protected BiomeBase b(int i) { + this.a(i, false); + return this; + } + + protected BiomeBase c(int i) { + this.ah = i; + return this; + } + + protected BiomeBase a(int i, boolean flag) { + this.ag = i; + if (flag) { + this.ah = (i & 16711422) >> 1; + } else { + this.ah = i; + } + + return this; + } + + public List getMobs(EnumCreatureType enumcreaturetype) { + return enumcreaturetype == EnumCreatureType.MONSTER ? this.as : (enumcreaturetype == EnumCreatureType.CREATURE ? this.at : (enumcreaturetype == EnumCreatureType.WATER_CREATURE ? this.au : (enumcreaturetype == EnumCreatureType.AMBIENT ? this.av : null))); + } + + public boolean d() { + return this.j(); + } + + public boolean e() { + return this.j() ? false : this.ax; + } + + public boolean f() { + return this.humidity > 0.85F; + } + + public float g() { + return 0.1F; + } + + public final int h() { + return (int) (this.humidity * 65536.0F); + } + + public final float a(int i, int j, int k) { + if (j > 64) { + float f = (float) ac.a((double) i * 1.0D / 8.0D, (double) k * 1.0D / 8.0D) * 4.0F; + + return this.temperature - (f + (float) j - 64.0F) * 0.05F / 30.0F; + } else { + return this.temperature; + } + } + + public void a(World world, Random random, int i, int j) { + this.ar.a(world, random, this, i, j); + } + + public boolean j() { + return this.aw; + } + + public void a(World world, Random random, Block[] ablock, byte[] abyte, int i, int j, double d0) { + this.b(world, random, ablock, abyte, i, j, d0); + } + + public final void b(World world, Random random, Block[] ablock, byte[] abyte, int i, int j, double d0) { + boolean flag = true; + Block block = this.ai; + byte b0 = (byte) (this.aj & 255); + Block block1 = this.ak; + int k = -1; + int l = (int) (d0 / 3.0D + 3.0D + random.nextDouble() * 0.25D); + int i1 = i & 15; + int j1 = j & 15; + int k1 = ablock.length / 256; + + for (int l1 = 255; l1 >= 0; --l1) { + int i2 = (j1 * 16 + i1) * k1 + l1; + + if (l1 <= (world.paperSpigotConfig.generateFlatBedrock ? 0 : random.nextInt(5))) { // PaperSpigot - Configurable flat bedrock worldgen + ablock[i2] = Blocks.BEDROCK; + } else { + Block block2 = ablock[i2]; + + if (block2 != null && block2.getMaterial() != Material.AIR) { + if (block2 == Blocks.STONE) { + if (k == -1) { + if (l <= 0) { + block = null; + b0 = 0; + block1 = Blocks.STONE; + } else if (l1 >= 59 && l1 <= 64) { + block = this.ai; + b0 = (byte) (this.aj & 255); + block1 = this.ak; + } + + if (l1 < 63 && (block == null || block.getMaterial() == Material.AIR)) { + if (this.a(i, l1, j) < 0.15F) { + block = Blocks.ICE; + b0 = 0; + } else { + block = Blocks.STATIONARY_WATER; + b0 = 0; + } + } + + k = l; + if (l1 >= 62) { + ablock[i2] = block; + abyte[i2] = b0; + } else if (l1 < 56 - l) { + block = null; + block1 = Blocks.STONE; + ablock[i2] = Blocks.GRAVEL; + } else { + ablock[i2] = block1; + } + } else if (k > 0) { + --k; + ablock[i2] = block1; + if (k == 0 && block1 == Blocks.SAND) { + k = random.nextInt(4) + Math.max(0, l1 - 63); + block1 = Blocks.SANDSTONE; + } + } + } + } else { + k = -1; + } + } + } + } + + protected BiomeBase k() { + return new BiomeBaseSub(this.id + 128, this); + } + + public Class l() { + return this.getClass(); + } + + public boolean a(BiomeBase biomebase) { + return biomebase == this ? true : (biomebase == null ? false : this.l() == biomebase.l()); + } + + public EnumTemperature m() { + return (double) this.temperature < 0.2D ? EnumTemperature.COLD : ((double) this.temperature < 1.0D ? EnumTemperature.MEDIUM : EnumTemperature.WARM); + } + + public static BiomeBase[] getBiomes() { + return biomes; + } + + public static BiomeBase getBiome(int i) { + if (i >= 0 && i <= biomes.length) { + return biomes[i]; + } else { + aC.warn("Biome ID is out of bounds: " + i + ", defaulting to 0 (Ocean)"); + return OCEAN; + } + } + + static { + PLAINS.k(); + DESERT.k(); + FOREST.k(); + TAIGA.k(); + SWAMPLAND.k(); + ICE_PLAINS.k(); + JUNGLE.k(); + JUNGLE_EDGE.k(); + COLD_TAIGA.k(); + SAVANNA.k(); + SAVANNA_PLATEAU.k(); + MESA.k(); + MESA_PLATEAU_F.k(); + MESA_PLATEAU.k(); + BIRCH_FOREST.k(); + BIRCH_FOREST_HILLS.k(); + ROOFED_FOREST.k(); + MEGA_TAIGA.k(); + EXTREME_HILLS.k(); + EXTREME_HILLS_PLUS.k(); + biomes[MEGA_TAIGA_HILLS.id + 128] = biomes[MEGA_TAIGA.id + 128]; + BiomeBase[] abiomebase = biomes; + int i = abiomebase.length; + + for (int j = 0; j < i; ++j) { + BiomeBase biomebase = abiomebase[j]; + + if (biomebase != null && biomebase.id < 128) { + n.add(biomebase); + } + } + + n.remove(HELL); + n.remove(SKY); + n.remove(FROZEN_OCEAN); + n.remove(SMALL_MOUNTAINS); + ac = new NoiseGenerator3(new Random(1234L), 1); + ad = new NoiseGenerator3(new Random(2345L), 1); + ae = new WorldGenTallPlant(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BiomeCache.java b/vspigot-server/src/main/java/net/minecraft/server/BiomeCache.java new file mode 100644 index 0000000..aad52cc --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BiomeCache.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.craftbukkit.util.LongHash; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + +public class BiomeCache { + + private final WorldChunkManager a; + private long b; + private Long2ObjectMap c = new Long2ObjectOpenHashMap(); // MineHQ + private List d = new ArrayList(); + + public BiomeCache(WorldChunkManager worldchunkmanager) { + this.a = worldchunkmanager; + } + + public BiomeCacheBlock a(int i, int j) { + i >>= 4; + j >>= 4; + // MineHQ start + long k = LongHash.toLong(i, j); + BiomeCacheBlock biomecacheblock = (BiomeCacheBlock) this.c.get(k); + // MineHQ end + + if (biomecacheblock == null) { + biomecacheblock = new BiomeCacheBlock(this, i, j); + this.c.put(k, biomecacheblock); + this.d.add(biomecacheblock); + } + + biomecacheblock.e = MinecraftServer.ar(); + return biomecacheblock; + } + + public BiomeBase b(int i, int j) { + return this.a(i, j).a(i, j); + } + + public void a() { + long i = MinecraftServer.ar(); + long j = i - this.b; + + if (j > 7500L || j < 0L) { + this.b = i; + + for (int k = 0; k < this.d.size(); ++k) { + BiomeCacheBlock biomecacheblock = (BiomeCacheBlock) this.d.get(k); + long l = i - biomecacheblock.e; + + if (l > 30000L || l < 0L) { + this.d.remove(k--); + long i1 = LongHash.toLong(biomecacheblock.c, biomecacheblock.d); // MineHQ + + this.c.remove(i1); + } + } + } + } + + public BiomeBase[] d(int i, int j) { + return this.a(i, j).b; + } + + static WorldChunkManager a(BiomeCache biomecache) { + return biomecache.a; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/BiomeDecorator.java b/vspigot-server/src/main/java/net/minecraft/server/BiomeDecorator.java new file mode 100644 index 0000000..861c989 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BiomeDecorator.java @@ -0,0 +1,298 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BiomeDecorator { + + protected World a; + protected Random b; + protected int c; + protected int d; + protected WorldGenerator e = new WorldGenClay(4); + protected WorldGenerator f; + protected WorldGenerator g; + protected WorldGenerator h; + protected WorldGenerator i; + protected WorldGenerator j; + protected WorldGenerator k; + protected WorldGenerator l; + protected WorldGenerator m; + protected WorldGenerator n; + protected WorldGenerator o; + protected WorldGenFlowers p; + protected WorldGenerator q; + protected WorldGenerator r; + protected WorldGenerator s; + protected WorldGenerator t; + protected WorldGenerator u; + protected WorldGenerator v; + protected int w; + protected int x; + protected int y; + protected int z; + protected int A; + protected int B; + protected int C; + protected int D; + protected int E; + protected int F; + protected int G; + protected int H; + public boolean I; + + public BiomeDecorator() { + this.f = new WorldGenSand(Blocks.SAND, 7); + this.g = new WorldGenSand(Blocks.GRAVEL, 6); + this.h = new WorldGenMinable(Blocks.DIRT, 32); + this.i = new WorldGenMinable(Blocks.GRAVEL, 32); + // MineHQ - move down so they can get world config + /* + this.j = new WorldGenMinable(Blocks.COAL_ORE, 16); + this.k = new WorldGenMinable(Blocks.IRON_ORE, 8); + this.l = new WorldGenMinable(Blocks.GOLD_ORE, 8); + this.m = new WorldGenMinable(Blocks.REDSTONE_ORE, 7); + this.n = new WorldGenMinable(Blocks.DIAMOND_ORE, 7); + this.o = new WorldGenMinable(Blocks.LAPIS_ORE, 6); + */ + this.p = new WorldGenFlowers(Blocks.YELLOW_FLOWER); + this.q = new WorldGenFlowers(Blocks.BROWN_MUSHROOM); + this.r = new WorldGenFlowers(Blocks.RED_MUSHROOM); + this.s = new WorldGenHugeMushroom(); + this.t = new WorldGenReed(); + this.u = new WorldGenCactus(); + this.v = new WorldGenWaterLily(); + this.y = 2; + this.z = 1; + this.E = 1; + this.F = 3; + this.G = 1; + this.I = true; + } + + public void a(World world, Random random, BiomeBase biomebase, int i, int j) { + if (this.a != null) { + throw new RuntimeException("Already decorating!!"); + } else { + this.a = world; + this.b = random; + this.c = i; + this.d = j; + // MineHQ Start + this.j = new WorldGenMinable(Blocks.COAL_ORE, world.generatorConfig.coalSize, world.generatorConfig.coalMustTouchAir); + this.k = new WorldGenMinable(Blocks.IRON_ORE, world.generatorConfig.ironSize, world.generatorConfig.ironMustTouchAir); + this.l = new WorldGenMinable(Blocks.GOLD_ORE, world.generatorConfig.goldSize, world.generatorConfig.goldMustTouchAir); + this.m = new WorldGenMinable(Blocks.REDSTONE_ORE, world.generatorConfig.redstoneSize, world.generatorConfig.redstoneMustTouchAir); + this.n = new WorldGenMinable(Blocks.DIAMOND_ORE, world.generatorConfig.diamondSize, world.generatorConfig.diamondMustTouchAir); + this.o = new WorldGenMinable(Blocks.LAPIS_ORE, world.generatorConfig.lapisSize, world.generatorConfig.lapisMustTouchAir); + // MineHQ end + this.a(biomebase); + this.a = null; + this.b = null; + } + } + + protected void a(BiomeBase biomebase) { + this.a(); + + int i; + int j; + int k; + + for (i = 0; i < this.F; ++i) { + j = this.c + this.b.nextInt(16) + 8; + k = this.d + this.b.nextInt(16) + 8; + this.f.generate(this.a, this.b, j, this.a.i(j, k), k); + } + + for (i = 0; i < this.G; ++i) { + j = this.c + this.b.nextInt(16) + 8; + k = this.d + this.b.nextInt(16) + 8; + this.e.generate(this.a, this.b, j, this.a.i(j, k), k); + } + + for (i = 0; i < this.E; ++i) { + j = this.c + this.b.nextInt(16) + 8; + k = this.d + this.b.nextInt(16) + 8; + this.g.generate(this.a, this.b, j, this.a.i(j, k), k); + } + + i = this.x; + if (this.b.nextInt(10) == 0) { + ++i; + } + + int l; + int i1; + + for (j = 0; j < i; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.a.getHighestBlockYAt(k, l); + WorldGenTreeAbstract worldgentreeabstract = biomebase.a(this.b); + + worldgentreeabstract.a(1.0D, 1.0D, 1.0D); + if (worldgentreeabstract.generate(this.a, this.b, k, i1, l)) { + worldgentreeabstract.b(this.a, this.b, k, i1, l); + } + } + + for (j = 0; j < this.H; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + this.s.generate(this.a, this.b, k, this.a.getHighestBlockYAt(k, l), l); + } + + for (j = 0; j < this.y; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.b.nextInt(this.a.getHighestBlockYAt(k, l) + 32); + String s = biomebase.a(this.b, k, i1, l); + BlockFlowers blockflowers = BlockFlowers.e(s); + + if (blockflowers.getMaterial() != Material.AIR) { + this.p.a(blockflowers, BlockFlowers.f(s)); + this.p.generate(this.a, this.b, k, i1, l); + } + } + + for (j = 0; j < this.z; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.b.nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + WorldGenerator worldgenerator = biomebase.b(this.b); + + worldgenerator.generate(this.a, this.b, k, i1, l); + } + + for (j = 0; j < this.A; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.b.nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + (new WorldGenDeadBush(Blocks.DEAD_BUSH)).generate(this.a, this.b, k, i1, l); + } + + for (j = 0; j < this.w; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + + for (i1 = this.b.nextInt(this.getHighestBlockYAt(k, l) * 2); i1 > 0 && this.a.isEmpty(k, i1 - 1, l); --i1) { // Spigot + ; + } + + this.v.generate(this.a, this.b, k, i1, l); + } + + for (j = 0; j < this.B; ++j) { + if (this.b.nextInt(4) == 0) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.a.getHighestBlockYAt(k, l); + this.q.generate(this.a, this.b, k, i1, l); + } + + if (this.b.nextInt(8) == 0) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.b.nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.r.generate(this.a, this.b, k, i1, l); + } + } + + if (this.b.nextInt(4) == 0) { + j = this.c + this.b.nextInt(16) + 8; + k = this.d + this.b.nextInt(16) + 8; + l = this.b.nextInt(this.getHighestBlockYAt(j, k) * 2); // Spigot + this.q.generate(this.a, this.b, j, l, k); + } + + if (this.b.nextInt(8) == 0) { + j = this.c + this.b.nextInt(16) + 8; + k = this.d + this.b.nextInt(16) + 8; + l = this.b.nextInt(this.getHighestBlockYAt(j, k) * 2); // Spigot + this.r.generate(this.a, this.b, j, l, k); + } + + for (j = 0; j < this.C * this.a.generatorConfig.sugarCaneMultiplier; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.b.nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.t.generate(this.a, this.b, k, i1, l); + } + + for (j = 0; j < 10 * this.a.generatorConfig.sugarCaneMultiplier; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.b.nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.t.generate(this.a, this.b, k, i1, l); + } + + if (this.b.nextInt(32) == 0) { + j = this.c + this.b.nextInt(16) + 8; + k = this.d + this.b.nextInt(16) + 8; + l = this.b.nextInt(this.getHighestBlockYAt(j, k) * 2); // Spigot + (new WorldGenPumpkin()).generate(this.a, this.b, j, l, k); + } + + for (j = 0; j < this.D; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.d + this.b.nextInt(16) + 8; + i1 = this.b.nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.u.generate(this.a, this.b, k, i1, l); + } + + if (this.I) { + for (j = 0; j < 50; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.b.nextInt(this.b.nextInt(248) + 8); + i1 = this.d + this.b.nextInt(16) + 8; + (new WorldGenLiquids(Blocks.WATER)).generate(this.a, this.b, k, l, i1); + } + + for (j = 0; j < 20; ++j) { + k = this.c + this.b.nextInt(16) + 8; + l = this.b.nextInt(this.b.nextInt(this.b.nextInt(240) + 8) + 8); + i1 = this.d + this.b.nextInt(16) + 8; + (new WorldGenLiquids(Blocks.LAVA)).generate(this.a, this.b, k, l, i1); + } + } + } + + protected void a(int i, WorldGenerator worldgenerator, int j, int k) { + for (int l = 0; l < i; ++l) { + int i1 = this.c + this.b.nextInt(16); + int j1 = this.b.nextInt(k - j) + j; + int k1 = this.d + this.b.nextInt(16); + + worldgenerator.generate(this.a, this.b, i1, j1, k1); + } + } + + protected void b(int i, WorldGenerator worldgenerator, int j, int k) { + for (int l = 0; l < i; ++l) { + int i1 = this.c + this.b.nextInt(16); + int j1 = this.b.nextInt(k) + this.b.nextInt(k) + (j - k); + int k1 = this.d + this.b.nextInt(16); + + worldgenerator.generate(this.a, this.b, i1, j1, k1); + } + } + + protected void a() { + this.a(20, this.h, 0, 256); + this.a(10, this.i, 0, 256); + net.valorhcf.generator.GeneratorConfig conf = this.a.generatorConfig; + if (conf.coalMultiplier > 0) this.a((int) (20 * conf.coalMultiplier + this.b.nextFloat()), this.j, 0, 128); + if (conf.ironMultiplier > 0) this.a((int) (20 * conf.ironMultiplier + this.b.nextFloat()), this.k, 0, 64); + if (conf.goldMultiplier > 0) this.a((int) (2 * conf.goldMultiplier + this.b.nextFloat()), this.l, 0, 32); + if (conf.redstoneMultiplier > 0) this.a((int) (8 * conf.redstoneMultiplier + this.b.nextFloat()), this.m, 0, 16); + if (conf.diamondMultiplier > 0) this.a((int) (1 * conf.diamondMultiplier + this.b.nextFloat()), this.n, 0, 16); + if (conf.lapisMultiplier > 0) this.b((int) (1 * conf.lapisMultiplier + this.b.nextFloat()), this.o, 16, 16); + } + + // Spigot Start + private int getHighestBlockYAt(int x, int z) + { + return Math.max( 1, this.a.getHighestBlockYAt( x, z ) ); + } + // Spigot End +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BiomeMesa.java b/vspigot-server/src/main/java/net/minecraft/server/BiomeMesa.java new file mode 100644 index 0000000..8b1a69d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BiomeMesa.java @@ -0,0 +1,279 @@ +package net.minecraft.server; + +import java.util.Arrays; +import java.util.Random; + +public class BiomeMesa extends BiomeBase { + + private byte[] aC; + private long aD; + private NoiseGenerator3 aE; + private NoiseGenerator3 aF; + private NoiseGenerator3 aG; + private boolean aH; + private boolean aI; + + public BiomeMesa(int i, boolean flag, boolean flag1) { + super(i); + this.aH = flag; + this.aI = flag1; + this.b(); + this.a(2.0F, 0.0F); + this.at.clear(); + this.ai = Blocks.SAND; + this.aj = 1; + this.ak = Blocks.STAINED_HARDENED_CLAY; + this.ar.x = -999; + this.ar.A = 20; + this.ar.C = 3; + this.ar.D = 5; + this.ar.y = 0; + this.at.clear(); + if (flag1) { + this.ar.x = 5; + } + } + + public WorldGenTreeAbstract a(Random random) { + return this.az; + } + + public void a(World world, Random random, int i, int j) { + super.a(world, random, i, j); + } + + public void a(World world, Random random, Block[] ablock, byte[] abyte, int i, int j, double d0) { + if (this.aC == null || this.aD != world.getSeed()) { + this.a(world.getSeed()); + } + + if (this.aE == null || this.aF == null || this.aD != world.getSeed()) { + Random random1 = new Random(this.aD); + + this.aE = new NoiseGenerator3(random1, 4); + this.aF = new NoiseGenerator3(random1, 1); + } + + this.aD = world.getSeed(); + double d1 = 0.0D; + int k; + int l; + + if (this.aH) { + k = (i & -16) + (j & 15); + l = (j & -16) + (i & 15); + double d2 = Math.min(Math.abs(d0), this.aE.a((double) k * 0.25D, (double) l * 0.25D)); + + if (d2 > 0.0D) { + double d3 = 0.001953125D; + double d4 = Math.abs(this.aF.a((double) k * d3, (double) l * d3)); + + d1 = d2 * d2 * 2.5D; + double d5 = Math.ceil(d4 * 50.0D) + 14.0D; + + if (d1 > d5) { + d1 = d5; + } + + d1 += 64.0D; + } + } + + k = i & 15; + l = j & 15; + boolean flag = true; + Block block = Blocks.STAINED_HARDENED_CLAY; + Block block1 = this.ak; + int i1 = (int) (d0 / 3.0D + 3.0D + random.nextDouble() * 0.25D); + boolean flag1 = Math.cos(d0 / 3.0D * 3.141592653589793D) > 0.0D; + int j1 = -1; + boolean flag2 = false; + int k1 = ablock.length / 256; + + for (int l1 = 255; l1 >= 0; --l1) { + int i2 = (l * 16 + k) * k1 + l1; + + if ((ablock[i2] == null || ablock[i2].getMaterial() == Material.AIR) && l1 < (int) d1) { + ablock[i2] = Blocks.STONE; + } + + if (l1 <= (world.paperSpigotConfig.generateFlatBedrock ? 0 : random.nextInt(5))) { // PaperSpigot - Configurable flat bedrock worldgen + ablock[i2] = Blocks.BEDROCK; + } else { + Block block2 = ablock[i2]; + + if (block2 != null && block2.getMaterial() != Material.AIR) { + if (block2 == Blocks.STONE) { + byte b0; + + if (j1 == -1) { + flag2 = false; + if (i1 <= 0) { + block = null; + block1 = Blocks.STONE; + } else if (l1 >= 59 && l1 <= 64) { + block = Blocks.STAINED_HARDENED_CLAY; + block1 = this.ak; + } + + if (l1 < 63 && (block == null || block.getMaterial() == Material.AIR)) { + block = Blocks.STATIONARY_WATER; + } + + j1 = i1 + Math.max(0, l1 - 63); + if (l1 >= 62) { + if (this.aI && l1 > 86 + i1 * 2) { + if (flag1) { + ablock[i2] = Blocks.DIRT; + abyte[i2] = 1; + } else { + ablock[i2] = Blocks.GRASS; + } + } else if (l1 > 66 + i1) { + b0 = 16; + if (l1 >= 64 && l1 <= 127) { + if (!flag1) { + b0 = this.d(i, l1, j); + } + } else { + b0 = 1; + } + + if (b0 < 16) { + ablock[i2] = Blocks.STAINED_HARDENED_CLAY; + abyte[i2] = (byte) b0; + } else { + ablock[i2] = Blocks.HARDENED_CLAY; + } + } else { + ablock[i2] = this.ai; + abyte[i2] = (byte) this.aj; + flag2 = true; + } + } else { + ablock[i2] = block1; + if (block1 == Blocks.STAINED_HARDENED_CLAY) { + abyte[i2] = 1; + } + } + } else if (j1 > 0) { + --j1; + if (flag2) { + ablock[i2] = Blocks.STAINED_HARDENED_CLAY; + abyte[i2] = 1; + } else { + b0 = this.d(i, l1, j); + if (b0 < 16) { + ablock[i2] = Blocks.STAINED_HARDENED_CLAY; + abyte[i2] = b0; + } else { + ablock[i2] = Blocks.HARDENED_CLAY; + } + } + } + } + } else { + j1 = -1; + } + } + } + } + + private void a(long i) { + this.aC = new byte[64]; + Arrays.fill(this.aC, (byte) 16); + Random random = new Random(i); + + this.aG = new NoiseGenerator3(random, 1); + + int j; + + for (j = 0; j < 64; ++j) { + j += random.nextInt(5) + 1; + if (j < 64) { + this.aC[j] = 1; + } + } + + j = random.nextInt(4) + 2; + + int k; + int l; + int i1; + int j1; + + for (k = 0; k < j; ++k) { + l = random.nextInt(3) + 1; + i1 = random.nextInt(64); + + for (j1 = 0; i1 + j1 < 64 && j1 < l; ++j1) { + this.aC[i1 + j1] = 4; + } + } + + k = random.nextInt(4) + 2; + + int k1; + + for (l = 0; l < k; ++l) { + i1 = random.nextInt(3) + 2; + j1 = random.nextInt(64); + + for (k1 = 0; j1 + k1 < 64 && k1 < i1; ++k1) { + this.aC[j1 + k1] = 12; + } + } + + l = random.nextInt(4) + 2; + + for (i1 = 0; i1 < l; ++i1) { + j1 = random.nextInt(3) + 1; + k1 = random.nextInt(64); + + for (int l1 = 0; k1 + l1 < 64 && l1 < j1; ++l1) { + this.aC[k1 + l1] = 14; + } + } + + i1 = random.nextInt(3) + 3; + j1 = 0; + + for (k1 = 0; k1 < i1; ++k1) { + byte b0 = 1; + + j1 += random.nextInt(16) + 4; + + for (int i2 = 0; j1 + i2 < 64 && i2 < b0; ++i2) { + this.aC[j1 + i2] = 0; + if (j1 + i2 > 1 && random.nextBoolean()) { + this.aC[j1 + i2 - 1] = 8; + } + + if (j1 + i2 < 63 && random.nextBoolean()) { + this.aC[j1 + i2 + 1] = 8; + } + } + } + } + + private byte d(int i, int j, int k) { + int l = (int) Math.round(this.aG.a((double) i * 1.0D / 512.0D, (double) i * 1.0D / 512.0D) * 2.0D); + + return this.aC[(j + l + 64) % 64]; + } + + protected BiomeBase k() { + boolean flag = this.id == BiomeBase.MESA.id; + BiomeMesa biomemesa = new BiomeMesa(this.id + 128, flag, this.aI); + + if (!flag) { + biomemesa.a(g); + biomemesa.a(this.af + " M"); + } else { + biomemesa.a(this.af + " (Bryce)"); + } + + biomemesa.a(this.ag, true); + return biomemesa; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BiomeTheEndDecorator.java b/vspigot-server/src/main/java/net/minecraft/server/BiomeTheEndDecorator.java new file mode 100644 index 0000000..2f4e749 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BiomeTheEndDecorator.java @@ -0,0 +1,28 @@ +package net.minecraft.server; + +public class BiomeTheEndDecorator extends BiomeDecorator { + + protected WorldGenerator J; + + public BiomeTheEndDecorator() { + this.J = new WorldGenEnder(Blocks.WHITESTONE); + } + + protected void a(BiomeBase biomebase) { + this.a(); + if (this.b.nextInt(5) == 0) { + int i = this.c + this.b.nextInt(16) + 8; + int j = this.d + this.b.nextInt(16) + 8; + int k = this.a.i(i, j); + + this.J.generate(this.a, this.b, i, k, j); + } + + if (this.c == 0 && this.d == 0) { + EntityEnderDragon entityenderdragon = new EntityEnderDragon(this.a); + + entityenderdragon.setPositionRotation(0.0D, 128.0D, 0.0D, this.b.nextFloat() * 360.0F, 0.0F); + this.a.addEntity(entityenderdragon, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Block.java b/vspigot-server/src/main/java/net/minecraft/server/Block.java new file mode 100644 index 0000000..106270c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Block.java @@ -0,0 +1,861 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftItem; +import org.bukkit.event.block.BlockDropItemsEvent; + +import net.valorhcf.blocks.BlockAccessCache; // Poweruser + +public class Block { + + // Poweruser start + public static final RegistryBlocks REGISTRY_BLOCKS = new RegistryBlocks("air"); + public static final RegistryMaterials REGISTRY = REGISTRY_BLOCKS; + private static final BlockAccessCache blockCache = new BlockAccessCache(); + // Poweruser end + + private CreativeModeTab creativeTab; + protected String d; + public static final StepSound e = new StepSound("stone", 1.0F, 1.0F); + public static final StepSound f = new StepSound("wood", 1.0F, 1.0F); + public static final StepSound g = new StepSound("gravel", 1.0F, 1.0F); + public static final StepSound h = new StepSound("grass", 1.0F, 1.0F); + public static final StepSound i = new StepSound("stone", 1.0F, 1.0F); + public static final StepSound j = new StepSound("stone", 1.0F, 1.5F); + public static final StepSound k = new StepSoundStone("stone", 1.0F, 1.0F); + public static final StepSound l = new StepSound("cloth", 1.0F, 1.0F); + public static final StepSound m = new StepSound("sand", 1.0F, 1.0F); + public static final StepSound n = new StepSound("snow", 1.0F, 1.0F); + public static final StepSound o = new StepSoundLadder("ladder", 1.0F, 1.0F); + public static final StepSound p = new StepSoundAnvil("anvil", 0.3F, 1.0F); + protected boolean q; + protected int r; + protected boolean s; + protected int t; + protected boolean u; + protected float strength; + protected float durability; + protected boolean x = true; + protected boolean y = true; + protected boolean z; + protected boolean isTileEntity; + protected double minX; + protected double minY; + protected double minZ; + protected double maxX; + protected double maxY; + protected double maxZ; + public StepSound stepSound; + public float I; + protected final Material material; + public float frictionFactor; + private String name; + + // MineHQ start + private int blockId = -1; + private static final Block[] blocksArray = new Block[4096]; + public List droppedItemsCatcher; + // MineHQ end + + public static int getId(Block block) { + return block == null ? 0 : block.blockId; // MineHQ + } + + public static Block getById(int i) { + // MineHQ start + if (0 <= i && i < 4096) { + return blocksArray[i]; + } + + return null; + // MineHQ end + } + + public static Block a(Item item) { + return getById(Item.getId(item)); + } + + public static Block b(String s) { + if (REGISTRY.b(s)) { + return (Block) REGISTRY.get(s); + } else { + try { + return (Block) REGISTRY.a(Integer.parseInt(s)); + } catch (NumberFormatException numberformatexception) { + return null; + } + } + } + + public boolean j() { + return this.q; + } + + public int k() { + return this.r; + } + + public int m() { + return this.t; + } + + public boolean n() { + return this.u; + } + + public Material getMaterial() { + return this.material; + } + + public MaterialMapColor f(int i) { + return this.getMaterial().r(); + } + + public static void p() { + REGISTRY.a(0, "air", (new BlockAir()).c("air")); + REGISTRY.a(1, "stone", (new BlockStone()).c(1.5F).b(10.0F).a(i).c("stone").d("stone")); + REGISTRY.a(2, "grass", (new BlockGrass()).c(0.6F).a(h).c("grass").d("grass")); + REGISTRY.a(3, "dirt", (new BlockDirt()).c(0.5F).a(g).c("dirt").d("dirt")); + Block block = (new Block(Material.STONE)).c(2.0F).b(10.0F).a(i).c("stonebrick").a(CreativeModeTab.b).d("cobblestone"); + + REGISTRY.a(4, "cobblestone", block); + Block block1 = (new BlockWood()).c(2.0F).b(5.0F).a(f).c("wood").d("planks"); + + REGISTRY.a(5, "planks", block1); + REGISTRY.a(6, "sapling", (new BlockSapling()).c(0.0F).a(h).c("sapling").d("sapling")); + REGISTRY.a(7, "bedrock", (new Block(Material.STONE)).s().b(6000000.0F).a(i).c("bedrock").H().a(CreativeModeTab.b).d("bedrock")); + REGISTRY.a(8, "flowing_water", (new BlockFlowing(Material.WATER)).c(100.0F).g(3).c("water").H().d("water_flow")); + REGISTRY.a(9, "water", (new BlockStationary(Material.WATER)).c(100.0F).g(3).c("water").H().d("water_still")); + REGISTRY.a(10, "flowing_lava", (new BlockFlowing(Material.LAVA)).c(100.0F).a(1.0F).c("lava").H().d("lava_flow")); + REGISTRY.a(11, "lava", (new BlockStationary(Material.LAVA)).c(100.0F).a(1.0F).c("lava").H().d("lava_still")); + REGISTRY.a(12, "sand", (new BlockSand()).c(0.5F).a(m).c("sand").d("sand")); + REGISTRY.a(13, "gravel", (new BlockGravel()).c(0.6F).a(g).c("gravel").d("gravel")); + REGISTRY.a(14, "gold_ore", (new BlockOre()).c(3.0F).b(5.0F).a(i).c("oreGold").d("gold_ore")); + REGISTRY.a(15, "iron_ore", (new BlockOre()).c(3.0F).b(5.0F).a(i).c("oreIron").d("iron_ore")); + REGISTRY.a(16, "coal_ore", (new BlockOre()).c(3.0F).b(5.0F).a(i).c("oreCoal").d("coal_ore")); + REGISTRY.a(17, "log", (new BlockLog1()).c("log").d("log")); + REGISTRY.a(18, "leaves", (new BlockLeaves1()).c("leaves").d("leaves")); + REGISTRY.a(19, "sponge", (new BlockSponge()).c(0.6F).a(h).c("sponge").d("sponge")); + REGISTRY.a(20, "glass", (new BlockGlass(Material.SHATTERABLE, false)).c(0.3F).a(k).c("glass").d("glass")); + REGISTRY.a(21, "lapis_ore", (new BlockOre()).c(3.0F).b(5.0F).a(i).c("oreLapis").d("lapis_ore")); + REGISTRY.a(22, "lapis_block", (new BlockOreBlock(MaterialMapColor.H)).c(3.0F).b(5.0F).a(i).c("blockLapis").a(CreativeModeTab.b).d("lapis_block")); + REGISTRY.a(23, "dispenser", (new BlockDispenser()).c(3.5F).a(i).c("dispenser").d("dispenser")); + Block block2 = (new BlockSandStone()).a(i).c(0.8F).c("sandStone").d("sandstone"); + + REGISTRY.a(24, "sandstone", block2); + REGISTRY.a(25, "noteblock", (new BlockNote()).c(0.8F).c("musicBlock").d("noteblock")); + REGISTRY.a(26, "bed", (new BlockBed()).c(0.2F).c("bed").H().d("bed")); + REGISTRY.a(27, "golden_rail", (new BlockPoweredRail()).c(0.7F).a(j).c("goldenRail").d("rail_golden")); + REGISTRY.a(28, "detector_rail", (new BlockMinecartDetector()).c(0.7F).a(j).c("detectorRail").d("rail_detector")); + REGISTRY.a(29, "sticky_piston", (new BlockPiston(true)).c("pistonStickyBase")); + REGISTRY.a(30, "web", (new BlockWeb()).g(1).c(4.0F).c("web").d("web")); + REGISTRY.a(31, "tallgrass", (new BlockLongGrass()).c(0.0F).a(h).c("tallgrass")); + REGISTRY.a(32, "deadbush", (new BlockDeadBush()).c(0.0F).a(h).c("deadbush").d("deadbush")); + REGISTRY.a(33, "piston", (new BlockPiston(false)).c("pistonBase")); + REGISTRY.a(34, "piston_head", new BlockPistonExtension()); + REGISTRY.a(35, "wool", (new BlockCloth(Material.CLOTH)).c(0.8F).a(l).c("cloth").d("wool_colored")); + REGISTRY.a(36, "piston_extension", new BlockPistonMoving()); + REGISTRY.a(37, "yellow_flower", (new BlockFlowers(0)).c(0.0F).a(h).c("flower1").d("flower_dandelion")); + REGISTRY.a(38, "red_flower", (new BlockFlowers(1)).c(0.0F).a(h).c("flower2").d("flower_rose")); + REGISTRY.a(39, "brown_mushroom", (new BlockMushroom()).c(0.0F).a(h).a(0.125F).c("mushroom").d("mushroom_brown")); + REGISTRY.a(40, "red_mushroom", (new BlockMushroom()).c(0.0F).a(h).c("mushroom").d("mushroom_red")); + REGISTRY.a(41, "gold_block", (new BlockOreBlock(MaterialMapColor.F)).c(3.0F).b(10.0F).a(j).c("blockGold").d("gold_block")); + REGISTRY.a(42, "iron_block", (new BlockOreBlock(MaterialMapColor.h)).c(5.0F).b(10.0F).a(j).c("blockIron").d("iron_block")); + REGISTRY.a(43, "double_stone_slab", (new BlockStep(true)).c(2.0F).b(10.0F).a(i).c("stoneSlab")); + REGISTRY.a(44, "stone_slab", (new BlockStep(false)).c(2.0F).b(10.0F).a(i).c("stoneSlab")); + Block block3 = (new Block(Material.STONE)).c(2.0F).b(10.0F).a(i).c("brick").a(CreativeModeTab.b).d("brick"); + + REGISTRY.a(45, "brick_block", block3); + REGISTRY.a(46, "tnt", (new BlockTNT()).c(0.0F).a(h).c("tnt").d("tnt")); + REGISTRY.a(47, "bookshelf", (new BlockBookshelf()).c(1.5F).a(f).c("bookshelf").d("bookshelf")); + REGISTRY.a(48, "mossy_cobblestone", (new Block(Material.STONE)).c(2.0F).b(10.0F).a(i).c("stoneMoss").a(CreativeModeTab.b).d("cobblestone_mossy")); + REGISTRY.a(49, "obsidian", (new BlockObsidian()).c(50.0F).b(2000.0F).a(i).c("obsidian").d("obsidian")); + REGISTRY.a(50, "torch", (new BlockTorch()).c(0.0F).a(0.9375F).a(f).c("torch").d("torch_on")); + REGISTRY.a(51, "fire", (new BlockFire()).c(0.0F).a(1.0F).a(f).c("fire").H().d("fire")); + REGISTRY.a(52, "mob_spawner", (new BlockMobSpawner()).c(5.0F).a(j).c("mobSpawner").H().d("mob_spawner")); + REGISTRY.a(53, "oak_stairs", (new BlockStairs(block1, 0)).c("stairsWood")); + REGISTRY.a(54, "chest", (new BlockChest(0)).c(2.5F).a(f).c("chest")); + REGISTRY.a(55, "redstone_wire", (new BlockRedstoneWire()).c(0.0F).a(e).c("redstoneDust").H().d("redstone_dust")); + REGISTRY.a(56, "diamond_ore", (new BlockOre()).c(3.0F).b(5.0F).a(i).c("oreDiamond").d("diamond_ore")); + REGISTRY.a(57, "diamond_block", (new BlockOreBlock(MaterialMapColor.G)).c(5.0F).b(10.0F).a(j).c("blockDiamond").d("diamond_block")); + REGISTRY.a(58, "crafting_table", (new BlockWorkbench()).c(2.5F).a(f).c("workbench").d("crafting_table")); + REGISTRY.a(59, "wheat", (new BlockCrops()).c("crops").d("wheat")); + Block block4 = (new BlockSoil()).c(0.6F).a(g).c("farmland").d("farmland"); + + REGISTRY.a(60, "farmland", block4); + REGISTRY.a(61, "furnace", (new BlockFurnace(false)).c(3.5F).a(i).c("furnace").a(CreativeModeTab.c)); + REGISTRY.a(62, "lit_furnace", (new BlockFurnace(true)).c(3.5F).a(i).a(0.875F).c("furnace")); + REGISTRY.a(63, "standing_sign", (new BlockSign(TileEntitySign.class, true)).c(1.0F).a(f).c("sign").H()); + REGISTRY.a(64, "wooden_door", (new BlockDoor(Material.WOOD)).c(3.0F).a(f).c("doorWood").H().d("door_wood")); + REGISTRY.a(65, "ladder", (new BlockLadder()).c(0.4F).a(o).c("ladder").d("ladder")); + REGISTRY.a(66, "rail", (new BlockMinecartTrack()).c(0.7F).a(j).c("rail").d("rail_normal")); + REGISTRY.a(67, "stone_stairs", (new BlockStairs(block, 0)).c("stairsStone")); + REGISTRY.a(68, "wall_sign", (new BlockSign(TileEntitySign.class, false)).c(1.0F).a(f).c("sign").H()); + REGISTRY.a(69, "lever", (new BlockLever()).c(0.5F).a(f).c("lever").d("lever")); + REGISTRY.a(70, "stone_pressure_plate", (new BlockPressurePlateBinary("stone", Material.STONE, EnumMobType.MOBS)).c(0.5F).a(i).c("pressurePlate")); + REGISTRY.a(71, "iron_door", (new BlockDoor(Material.ORE)).c(5.0F).a(j).c("doorIron").H().d("door_iron")); + REGISTRY.a(72, "wooden_pressure_plate", (new BlockPressurePlateBinary("planks_oak", Material.WOOD, EnumMobType.EVERYTHING)).c(0.5F).a(f).c("pressurePlate")); + REGISTRY.a(73, "redstone_ore", (new BlockRedstoneOre(false)).c(3.0F).b(5.0F).a(i).c("oreRedstone").a(CreativeModeTab.b).d("redstone_ore")); + REGISTRY.a(74, "lit_redstone_ore", (new BlockRedstoneOre(true)).a(0.625F).c(3.0F).b(5.0F).a(i).c("oreRedstone").d("redstone_ore")); + REGISTRY.a(75, "unlit_redstone_torch", (new BlockRedstoneTorch(false)).c(0.0F).a(f).c("notGate").d("redstone_torch_off")); + REGISTRY.a(76, "redstone_torch", (new BlockRedstoneTorch(true)).c(0.0F).a(0.5F).a(f).c("notGate").a(CreativeModeTab.d).d("redstone_torch_on")); + REGISTRY.a(77, "stone_button", (new BlockStoneButton()).c(0.5F).a(i).c("button")); + REGISTRY.a(78, "snow_layer", (new BlockSnow()).c(0.1F).a(n).c("snow").g(0).d("snow")); + REGISTRY.a(79, "ice", (new BlockIce()).c(0.5F).g(3).a(k).c("ice").d("ice")); + REGISTRY.a(80, "snow", (new BlockSnowBlock()).c(0.2F).a(n).c("snow").d("snow")); + REGISTRY.a(81, "cactus", (new BlockCactus()).c(0.4F).a(l).c("cactus").d("cactus")); + REGISTRY.a(82, "clay", (new BlockClay()).c(0.6F).a(g).c("clay").d("clay")); + REGISTRY.a(83, "reeds", (new BlockReed()).c(0.0F).a(h).c("reeds").H().d("reeds")); + REGISTRY.a(84, "jukebox", (new BlockJukeBox()).c(2.0F).b(10.0F).a(i).c("jukebox").d("jukebox")); + REGISTRY.a(85, "fence", (new BlockFence("planks_oak", Material.WOOD)).c(2.0F).b(5.0F).a(f).c("fence")); + Block block5 = (new BlockPumpkin(false)).c(1.0F).a(f).c("pumpkin").d("pumpkin"); + + REGISTRY.a(86, "pumpkin", block5); + REGISTRY.a(87, "netherrack", (new BlockBloodStone()).c(0.4F).a(i).c("hellrock").d("netherrack")); + REGISTRY.a(88, "soul_sand", (new BlockSlowSand()).c(0.5F).a(m).c("hellsand").d("soul_sand")); + REGISTRY.a(89, "glowstone", (new BlockLightStone(Material.SHATTERABLE)).c(0.3F).a(k).a(1.0F).c("lightgem").d("glowstone")); + REGISTRY.a(90, "portal", (new BlockPortal()).c(-1.0F).a(k).a(0.75F).c("portal").d("portal")); + REGISTRY.a(91, "lit_pumpkin", (new BlockPumpkin(true)).c(1.0F).a(f).a(1.0F).c("litpumpkin").d("pumpkin")); + REGISTRY.a(92, "cake", (new BlockCake()).c(0.5F).a(l).c("cake").H().d("cake")); + REGISTRY.a(93, "unpowered_repeater", (new BlockRepeater(false)).c(0.0F).a(f).c("diode").H().d("repeater_off")); + REGISTRY.a(94, "powered_repeater", (new BlockRepeater(true)).c(0.0F).a(0.625F).a(f).c("diode").H().d("repeater_on")); + REGISTRY.a(95, "stained_glass", (new BlockStainedGlass(Material.SHATTERABLE)).c(0.3F).a(k).c("stainedGlass").d("glass")); + REGISTRY.a(96, "trapdoor", (new BlockTrapdoor(Material.WOOD)).c(3.0F).a(f).c("trapdoor").H().d("trapdoor")); + REGISTRY.a(97, "monster_egg", (new BlockMonsterEggs()).c(0.75F).c("monsterStoneEgg")); + Block block6 = (new BlockSmoothBrick()).c(1.5F).b(10.0F).a(i).c("stonebricksmooth").d("stonebrick"); + + REGISTRY.a(98, "stonebrick", block6); + REGISTRY.a(99, "brown_mushroom_block", (new BlockHugeMushroom(Material.WOOD, 0)).c(0.2F).a(f).c("mushroom").d("mushroom_block")); + REGISTRY.a(100, "red_mushroom_block", (new BlockHugeMushroom(Material.WOOD, 1)).c(0.2F).a(f).c("mushroom").d("mushroom_block")); + REGISTRY.a(101, "iron_bars", (new BlockThin("iron_bars", "iron_bars", Material.ORE, true)).c(5.0F).b(10.0F).a(j).c("fenceIron")); + REGISTRY.a(102, "glass_pane", (new BlockThin("glass", "glass_pane_top", Material.SHATTERABLE, false)).c(0.3F).a(k).c("thinGlass")); + Block block7 = (new BlockMelon()).c(1.0F).a(f).c("melon").d("melon"); + + REGISTRY.a(103, "melon_block", block7); + REGISTRY.a(104, "pumpkin_stem", (new BlockStem(block5)).c(0.0F).a(f).c("pumpkinStem").d("pumpkin_stem")); + REGISTRY.a(105, "melon_stem", (new BlockStem(block7)).c(0.0F).a(f).c("pumpkinStem").d("melon_stem")); + REGISTRY.a(106, "vine", (new BlockVine()).c(0.2F).a(h).c("vine").d("vine")); + REGISTRY.a(107, "fence_gate", (new BlockFenceGate()).c(2.0F).b(5.0F).a(f).c("fenceGate")); + REGISTRY.a(108, "brick_stairs", (new BlockStairs(block3, 0)).c("stairsBrick")); + REGISTRY.a(109, "stone_brick_stairs", (new BlockStairs(block6, 0)).c("stairsStoneBrickSmooth")); + REGISTRY.a(110, "mycelium", (new BlockMycel()).c(0.6F).a(h).c("mycel").d("mycelium")); + REGISTRY.a(111, "waterlily", (new BlockWaterLily()).c(0.0F).a(h).c("waterlily").d("waterlily")); + Block block8 = (new Block(Material.STONE)).c(2.0F).b(10.0F).a(i).c("netherBrick").a(CreativeModeTab.b).d("nether_brick"); + + REGISTRY.a(112, "nether_brick", block8); + REGISTRY.a(113, "nether_brick_fence", (new BlockFence("nether_brick", Material.STONE)).c(2.0F).b(10.0F).a(i).c("netherFence")); + REGISTRY.a(114, "nether_brick_stairs", (new BlockStairs(block8, 0)).c("stairsNetherBrick")); + REGISTRY.a(115, "nether_wart", (new BlockNetherWart()).c("netherStalk").d("nether_wart")); + REGISTRY.a(116, "enchanting_table", (new BlockEnchantmentTable()).c(5.0F).b(2000.0F).c("enchantmentTable").d("enchanting_table")); + REGISTRY.a(117, "brewing_stand", (new BlockBrewingStand()).c(0.5F).a(0.125F).c("brewingStand").d("brewing_stand")); + REGISTRY.a(118, "cauldron", (new BlockCauldron()).c(2.0F).c("cauldron").d("cauldron")); + REGISTRY.a(119, "end_portal", (new BlockEnderPortal(Material.PORTAL)).c(-1.0F).b(6000000.0F)); + REGISTRY.a(120, "end_portal_frame", (new BlockEnderPortalFrame()).a(k).a(0.125F).c(-1.0F).c("endPortalFrame").b(6000000.0F).a(CreativeModeTab.c).d("endframe")); + REGISTRY.a(121, "end_stone", (new Block(Material.STONE)).c(3.0F).b(15.0F).a(i).c("whiteStone").a(CreativeModeTab.b).d("end_stone")); + REGISTRY.a(122, "dragon_egg", (new BlockDragonEgg()).c(3.0F).b(15.0F).a(i).a(0.125F).c("dragonEgg").d("dragon_egg")); + REGISTRY.a(123, "redstone_lamp", (new BlockRedstoneLamp(false)).c(0.3F).a(k).c("redstoneLight").a(CreativeModeTab.d).d("redstone_lamp_off")); + REGISTRY.a(124, "lit_redstone_lamp", (new BlockRedstoneLamp(true)).c(0.3F).a(k).c("redstoneLight").d("redstone_lamp_on")); + REGISTRY.a(125, "double_wooden_slab", (new BlockWoodStep(true)).c(2.0F).b(5.0F).a(f).c("woodSlab")); + REGISTRY.a(126, "wooden_slab", (new BlockWoodStep(false)).c(2.0F).b(5.0F).a(f).c("woodSlab")); + REGISTRY.a(127, "cocoa", (new BlockCocoa()).c(0.2F).b(5.0F).a(f).c("cocoa").d("cocoa")); + REGISTRY.a(128, "sandstone_stairs", (new BlockStairs(block2, 0)).c("stairsSandStone")); + REGISTRY.a(129, "emerald_ore", (new BlockOre()).c(3.0F).b(5.0F).a(i).c("oreEmerald").d("emerald_ore")); + REGISTRY.a(130, "ender_chest", (new BlockEnderChest()).c(22.5F).b(1000.0F).a(i).c("enderChest").a(0.5F)); + REGISTRY.a(131, "tripwire_hook", (new BlockTripwireHook()).c("tripWireSource").d("trip_wire_source")); + REGISTRY.a(132, "tripwire", (new BlockTripwire()).c("tripWire").d("trip_wire")); + REGISTRY.a(133, "emerald_block", (new BlockOreBlock(MaterialMapColor.I)).c(5.0F).b(10.0F).a(j).c("blockEmerald").d("emerald_block")); + REGISTRY.a(134, "spruce_stairs", (new BlockStairs(block1, 1)).c("stairsWoodSpruce")); + REGISTRY.a(135, "birch_stairs", (new BlockStairs(block1, 2)).c("stairsWoodBirch")); + REGISTRY.a(136, "jungle_stairs", (new BlockStairs(block1, 3)).c("stairsWoodJungle")); + REGISTRY.a(137, "command_block", (new BlockCommand()).s().b(6000000.0F).c("commandBlock").d("command_block")); + REGISTRY.a(138, "beacon", (new BlockBeacon()).c("beacon").a(1.0F).d("beacon")); + REGISTRY.a(139, "cobblestone_wall", (new BlockCobbleWall(block)).c("cobbleWall")); + REGISTRY.a(140, "flower_pot", (new BlockFlowerPot()).c(0.0F).a(e).c("flowerPot").d("flower_pot")); + REGISTRY.a(141, "carrots", (new BlockCarrots()).c("carrots").d("carrots")); + REGISTRY.a(142, "potatoes", (new BlockPotatoes()).c("potatoes").d("potatoes")); + REGISTRY.a(143, "wooden_button", (new BlockWoodButton()).c(0.5F).a(f).c("button")); + REGISTRY.a(144, "skull", (new BlockSkull()).c(1.0F).a(i).c("skull").d("skull")); + REGISTRY.a(145, "anvil", (new BlockAnvil()).c(5.0F).a(p).b(2000.0F).c("anvil")); + REGISTRY.a(146, "trapped_chest", (new BlockChest(1)).c(2.5F).a(f).c("chestTrap")); + REGISTRY.a(147, "light_weighted_pressure_plate", (new BlockPressurePlateWeighted("gold_block", Material.ORE, 15)).c(0.5F).a(f).c("weightedPlate_light")); + REGISTRY.a(148, "heavy_weighted_pressure_plate", (new BlockPressurePlateWeighted("iron_block", Material.ORE, 150)).c(0.5F).a(f).c("weightedPlate_heavy")); + REGISTRY.a(149, "unpowered_comparator", (new BlockRedstoneComparator(false)).c(0.0F).a(f).c("comparator").H().d("comparator_off")); + REGISTRY.a(150, "powered_comparator", (new BlockRedstoneComparator(true)).c(0.0F).a(0.625F).a(f).c("comparator").H().d("comparator_on")); + REGISTRY.a(151, "daylight_detector", (new BlockDaylightDetector()).c(0.2F).a(f).c("daylightDetector").d("daylight_detector")); + REGISTRY.a(152, "redstone_block", (new BlockRedstone(MaterialMapColor.f)).c(5.0F).b(10.0F).a(j).c("blockRedstone").d("redstone_block")); + REGISTRY.a(153, "quartz_ore", (new BlockOre()).c(3.0F).b(5.0F).a(i).c("netherquartz").d("quartz_ore")); + REGISTRY.a(154, "hopper", (new BlockHopper()).c(3.0F).b(8.0F).a(f).c("hopper").d("hopper")); + Block block9 = (new BlockQuartz()).a(i).c(0.8F).c("quartzBlock").d("quartz_block"); + + REGISTRY.a(155, "quartz_block", block9); + REGISTRY.a(156, "quartz_stairs", (new BlockStairs(block9, 0)).c("stairsQuartz")); + REGISTRY.a(157, "activator_rail", (new BlockPoweredRail()).c(0.7F).a(j).c("activatorRail").d("rail_activator")); + REGISTRY.a(158, "dropper", (new BlockDropper()).c(3.5F).a(i).c("dropper").d("dropper")); + REGISTRY.a(159, "stained_hardened_clay", (new BlockCloth(Material.STONE)).c(1.25F).b(7.0F).a(i).c("clayHardenedStained").d("hardened_clay_stained")); + REGISTRY.a(160, "stained_glass_pane", (new BlockStainedGlassPane()).c(0.3F).a(k).c("thinStainedGlass").d("glass")); + REGISTRY.a(161, "leaves2", (new BlockLeaves2()).c("leaves").d("leaves")); + REGISTRY.a(162, "log2", (new BlockLog2()).c("log").d("log")); + REGISTRY.a(163, "acacia_stairs", (new BlockStairs(block1, 4)).c("stairsWoodAcacia")); + REGISTRY.a(164, "dark_oak_stairs", (new BlockStairs(block1, 5)).c("stairsWoodDarkOak")); + REGISTRY.a(170, "hay_block", (new BlockHay()).c(0.5F).a(h).c("hayBlock").a(CreativeModeTab.b).d("hay_block")); + REGISTRY.a(171, "carpet", (new BlockCarpet()).c(0.1F).a(l).c("woolCarpet").g(0)); + REGISTRY.a(172, "hardened_clay", (new BlockHardenedClay()).c(1.25F).b(7.0F).a(i).c("clayHardened").d("hardened_clay")); + REGISTRY.a(173, "coal_block", (new Block(Material.STONE)).c(5.0F).b(10.0F).a(i).c("blockCoal").a(CreativeModeTab.b).d("coal_block")); + REGISTRY.a(174, "packed_ice", (new BlockPackedIce()).c(0.5F).a(k).c("icePacked").d("ice_packed")); + REGISTRY.a(175, "double_plant", new BlockTallPlant()); + + // MineHQ start - blocks array + for (int prefillIndex = 0; prefillIndex < 4096; prefillIndex++) { + Block prefillBlock = (Block) REGISTRY.a(prefillIndex); + if (prefillBlock != null) { + if (prefillBlock.blockId == -1) prefillBlock.blockId = prefillIndex; + blocksArray[prefillIndex] = prefillBlock; + if (block.material == Material.AIR) { + block.u = false; + } else { + block.u = block == block4 || block.s || block.r == 0 || block.b() == 10 || block instanceof BlockStepAbstract; + } + } + } + // MineHQ end + } + + protected Block(Material material) { + this.stepSound = e; + this.I = 1.0F; + this.frictionFactor = 0.6F; + this.material = material; + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + this.q = this.c(); + this.r = this.c() ? 255 : 0; + this.s = !material.blocksLight(); + } + + protected Block a(StepSound stepsound) { + this.stepSound = stepsound; + return this; + } + + protected Block g(int i) { + this.r = i; + return this; + } + + protected Block a(float f) { + this.t = (int) (15.0F * f); + return this; + } + + protected Block b(float f) { + this.durability = f * 3.0F; + return this; + } + + public boolean r() { + return this.material.k() && this.d() && !this.isPowerSource(); + } + + public boolean d() { + return true; + } + + public boolean b(IBlockAccess iblockaccess, int i, int j, int k) { + return !this.material.isSolid(); + } + + public int b() { + return 0; + } + + protected Block c(float f) { + this.strength = f; + if (this.durability < f * 5.0F) { + this.durability = f * 5.0F; + } + + return this; + } + + protected Block s() { + this.c(-1.0F); + return this; + } + + public float f(World world, int i, int j, int k) { + return this.strength; + } + + protected Block a(boolean flag) { + this.z = flag; + return this; + } + + public boolean isTicking() { + return this.z; + } + + public boolean isTileEntity() { + return this.isTileEntity; + } + + protected final void a(float f, float f1, float f2, float f3, float f4, float f5) { + this.minX = (double) f; + this.minY = (double) f1; + this.minZ = (double) f2; + this.maxX = (double) f3; + this.maxY = (double) f4; + this.maxZ = (double) f5; + } + + public boolean d(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return iblockaccess.getType(i, j, k).getMaterial().isBuildable(); + } + + public void a(World world, int i, int j, int k, AxisAlignedBB axisalignedbb, List list, Entity entity) { + AxisAlignedBB axisalignedbb1 = this.a(world, i, j, k); + + if (axisalignedbb1 != null && axisalignedbb.b(axisalignedbb1)) { + list.add(axisalignedbb1); + } + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return AxisAlignedBB.a((double) i + this.minX, (double) j + this.minY, (double) k + this.minZ, (double) i + this.maxX, (double) j + this.maxY, (double) k + this.maxZ); + } + + public boolean c() { + return true; + } + + public boolean a(int i, boolean flag) { + return this.v(); + } + + public boolean v() { + return true; + } + + public void a(World world, int i, int j, int k, Random random) {} + + public void postBreak(World world, int i, int j, int k, int l) {} + + public void doPhysics(World world, int i, int j, int k, Block block) {} + + public int a(World world) { + return 10; + } + + public void onPlace(World world, int i, int j, int k) { + org.spigotmc.AsyncCatcher.catchOp( "block onPlace"); // Spigot + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + org.spigotmc.AsyncCatcher.catchOp( "block remove"); // Spigot + } + + public int a(Random random) { + return 1; + } + + public Item getDropType(int i, Random random, int j) { + return Item.getItemOf(this); + } + + public float getDamage(EntityHuman entityhuman, World world, int i, int j, int k) { + float f = this.f(world, i, j, k); + + return f < 0.0F ? 0.0F : (!entityhuman.a(this) ? entityhuman.a(this, false) / f / 100.0F : entityhuman.a(this, true) / f / 30.0F); + } + + public final void b(World world, int i, int j, int k, int l, int i1) { + // MineHQ start + if (this == Blocks.AIR) return; + if (this.droppedItemsCatcher == null) { + this.droppedItemsCatcher = new ArrayList(1); + this.dropNaturally(world, i, j, k, l, 1.0f, i1); + BlockDropItemsEvent dropItemsEvent = new BlockDropItemsEvent(world.getWorld().getBlockAt(i, j, k), null, this.droppedItemsCatcher); + Bukkit.getPluginManager().callEvent(dropItemsEvent); + if (!dropItemsEvent.isCancelled() && dropItemsEvent.getToDrop() != null) { + for (final org.bukkit.entity.Item item : dropItemsEvent.getToDrop()) { + world.addEntity(((CraftItem) item).getHandle()); + } + } + this.droppedItemsCatcher = null; + } else { + this.dropNaturally(world, i, j, k, l, 1.0F, i1); + } + // MineHQ end + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (!world.isStatic) { + int j1 = this.getDropCount(i1, world.random); + + for (int k1 = 0; k1 < j1; ++k1) { + // CraftBukkit - <= to < to allow for plugins to completely disable block drops from explosions + if (world.random.nextFloat() < f) { + Item item = this.getDropType(l, world.random, i1); + + if (item != null) { + this.a(world, i, j, k, new ItemStack(item, 1, this.getDropData(l))); + } + } + } + } + } + + protected void a(World world, int i, int j, int k, ItemStack itemstack) { + if (!world.isStatic && world.getGameRules().getBoolean("doTileDrops")) { + float f = 0.7F; + double d0 = (double) (world.random.nextFloat() * f) + (double) (1.0F - f) * 0.5D; + double d1 = (double) (world.random.nextFloat() * f) + (double) (1.0F - f) * 0.5D; + double d2 = (double) (world.random.nextFloat() * f) + (double) (1.0F - f) * 0.5D; + EntityItem entityitem = new EntityItem(world, (double) i + d0, (double) j + d1, (double) k + d2, itemstack); + + entityitem.pickupDelay = 10; + // MineHQ start + if (this.droppedItemsCatcher == null) { + world.addEntity(entityitem); + } else { + this.droppedItemsCatcher.add(new CraftItem(world.getServer(), entityitem)); + } + // MineHQ end + } + } + + protected void dropExperience(World world, int i, int j, int k, int l) { + if (!world.isStatic) { + while (l > 0) { + int i1 = EntityExperienceOrb.getOrbValue(l); + + l -= i1; + world.addEntity(new EntityExperienceOrb(world, (double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, i1)); + } + } + } + + public int getDropData(int i) { + return 0; + } + + public float a(Entity entity) { + return this.durability / 5.0F; + } + + public MovingObjectPosition a(World world, int i, int j, int k, Vec3D vec3d, Vec3D vec3d1) { + this.updateShape(world, i, j, k); + vec3d = vec3d.add((double) (-i), (double) (-j), (double) (-k)); + vec3d1 = vec3d1.add((double) (-i), (double) (-j), (double) (-k)); + Vec3D vec3d2 = vec3d.b(vec3d1, this.minX); + Vec3D vec3d3 = vec3d.b(vec3d1, this.maxX); + Vec3D vec3d4 = vec3d.c(vec3d1, this.minY); + Vec3D vec3d5 = vec3d.c(vec3d1, this.maxY); + Vec3D vec3d6 = vec3d.d(vec3d1, this.minZ); + Vec3D vec3d7 = vec3d.d(vec3d1, this.maxZ); + + if (!this.a(vec3d2)) { + vec3d2 = null; + } + + if (!this.a(vec3d3)) { + vec3d3 = null; + } + + if (!this.b(vec3d4)) { + vec3d4 = null; + } + + if (!this.b(vec3d5)) { + vec3d5 = null; + } + + if (!this.c(vec3d6)) { + vec3d6 = null; + } + + if (!this.c(vec3d7)) { + vec3d7 = null; + } + + Vec3D vec3d8 = null; + + if (vec3d2 != null && (vec3d8 == null || vec3d.distanceSquared(vec3d2) < vec3d.distanceSquared(vec3d8))) { + vec3d8 = vec3d2; + } + + if (vec3d3 != null && (vec3d8 == null || vec3d.distanceSquared(vec3d3) < vec3d.distanceSquared(vec3d8))) { + vec3d8 = vec3d3; + } + + if (vec3d4 != null && (vec3d8 == null || vec3d.distanceSquared(vec3d4) < vec3d.distanceSquared(vec3d8))) { + vec3d8 = vec3d4; + } + + if (vec3d5 != null && (vec3d8 == null || vec3d.distanceSquared(vec3d5) < vec3d.distanceSquared(vec3d8))) { + vec3d8 = vec3d5; + } + + if (vec3d6 != null && (vec3d8 == null || vec3d.distanceSquared(vec3d6) < vec3d.distanceSquared(vec3d8))) { + vec3d8 = vec3d6; + } + + if (vec3d7 != null && (vec3d8 == null || vec3d.distanceSquared(vec3d7) < vec3d.distanceSquared(vec3d8))) { + vec3d8 = vec3d7; + } + + if (vec3d8 == null) { + return null; + } else { + byte b0 = -1; + + if (vec3d8 == vec3d2) { + b0 = 4; + } + + if (vec3d8 == vec3d3) { + b0 = 5; + } + + if (vec3d8 == vec3d4) { + b0 = 0; + } + + if (vec3d8 == vec3d5) { + b0 = 1; + } + + if (vec3d8 == vec3d6) { + b0 = 2; + } + + if (vec3d8 == vec3d7) { + b0 = 3; + } + + return new MovingObjectPosition(i, j, k, b0, vec3d8.add((double) i, (double) j, (double) k)); + } + } + + private boolean a(Vec3D vec3d) { + return vec3d == null ? false : vec3d.b >= this.minY && vec3d.b <= this.maxY && vec3d.c >= this.minZ && vec3d.c <= this.maxZ; + } + + private boolean b(Vec3D vec3d) { + return vec3d == null ? false : vec3d.a >= this.minX && vec3d.a <= this.maxX && vec3d.c >= this.minZ && vec3d.c <= this.maxZ; + } + + private boolean c(Vec3D vec3d) { + return vec3d == null ? false : vec3d.a >= this.minX && vec3d.a <= this.maxX && vec3d.b >= this.minY && vec3d.b <= this.maxY; + } + + public void wasExploded(World world, int i, int j, int k, Explosion explosion) {} + + public boolean canPlace(World world, int i, int j, int k, int l, ItemStack itemstack) { + return this.canPlace(world, i, j, k, l); + } + + public boolean canPlace(World world, int i, int j, int k, int l) { + return this.canPlace(world, i, j, k); + } + + public boolean canPlace(World world, int i, int j, int k) { + return world.getType(i, j, k).material.isReplaceable(); + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + return false; + } + + public void b(World world, int i, int j, int k, Entity entity) {} + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + return i1; + } + + public void attack(World world, int i, int j, int k, EntityHuman entityhuman) {} + + public void a(World world, int i, int j, int k, Entity entity, Vec3D vec3d) {} + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) {} + + public final double x() { + return this.minX; + } + + public final double y() { + return this.maxX; + } + + public final double z() { + return this.minY; + } + + public final double A() { + return this.maxY; + } + + public final double B() { + return this.minZ; + } + + public final double C() { + return this.maxZ; + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return 0; + } + + public boolean isPowerSource() { + return false; + } + + public void a(World world, int i, int j, int k, Entity entity) {} + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return 0; + } + + public void g() {} + + public void a(World world, EntityHuman entityhuman, int i, int j, int k, int l) { + entityhuman.a(StatisticList.MINE_BLOCK_COUNT[getId(this)], 1); + entityhuman.applyExhaustion(world.paperSpigotConfig.blockBreakExhaustion); // PaperSpigot - Configurable block break exhaustion + if (this.E() && EnchantmentManager.hasSilkTouchEnchantment(entityhuman)) { + ItemStack itemstack = this.j(l); + + if (itemstack != null) { + this.a(world, i, j, k, itemstack); + } + } else { + int i1 = EnchantmentManager.getBonusBlockLootEnchantmentLevel(entityhuman); + + this.b(world, i, j, k, l, i1); + } + } + + protected boolean E() { + return this.d() && !this.isTileEntity; + } + + protected ItemStack j(int i) { + int j = 0; + Item item = Item.getItemOf(this); + + if (item != null && item.n()) { + j = i; + } + + return new ItemStack(item, 1, j); + } + + public int getDropCount(int i, Random random) { + return this.a(random); + } + + public boolean j(World world, int i, int j, int k) { + return true; + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) {} + + public void postPlace(World world, int i, int j, int k, int l) {} + + public Block c(String s) { + this.name = s; + return this; + } + + public String getName() { + return LocaleI18n.get(this.a() + ".name"); + } + + public String a() { + return "tile." + this.name; + } + + public boolean a(World world, int i, int j, int k, int l, int i1) { + return false; + } + + public boolean G() { + return this.y; + } + + protected Block H() { + this.y = false; + return this; + } + + public int h() { + return this.material.getPushReaction(); + } + + public void a(World world, int i, int j, int k, Entity entity, float f) {} + + public int getDropData(World world, int i, int j, int k) { + return this.getDropData(world.getData(i, j, k)); + } + + public Block a(CreativeModeTab creativemodetab) { + this.creativeTab = creativemodetab; + return this; + } + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) {} + + public void f(World world, int i, int j, int k, int l) {} + + public void l(World world, int i, int j, int k) {} + + public boolean L() { + return true; + } + + public boolean a(Explosion explosion) { + return true; + } + + public boolean c(Block block) { + return this == block; + } + + public static boolean a(Block block, Block block1) { + return block != null && block1 != null ? (block == block1 ? true : block.c(block1)) : false; + } + + public boolean isComplexRedstone() { + return false; + } + + public int g(World world, int i, int j, int k, int l) { + return 0; + } + + protected Block d(String s) { + this.d = s; + return this; + } + + // CraftBukkit start + public int getExpDrop(World world, int data, int enchantmentLevel) { + return 0; + } + // CraftBukkit end + + // Spigot start + public static float range(float min, float value, float max) { + if (value < min) { + return min; + } + if (value > max) { + return max; + } + return value; + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockActionDataList.java b/vspigot-server/src/main/java/net/minecraft/server/BlockActionDataList.java new file mode 100644 index 0000000..30109ec --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockActionDataList.java @@ -0,0 +1,13 @@ +package net.minecraft.server; + +import java.util.ArrayList; + +// CraftBukkit - imported class because the constructor is package private +class BlockActionDataList extends ArrayList { + + private BlockActionDataList() {} + + BlockActionDataList(BananaAPI bananaapi) { + this(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockAnvil.java b/vspigot-server/src/main/java/net/minecraft/server/BlockAnvil.java new file mode 100644 index 0000000..1fa14c5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockAnvil.java @@ -0,0 +1,88 @@ +package net.minecraft.server; + +public class BlockAnvil extends BlockFalling { + + public static final String[] a = new String[] { "intact", "slightlyDamaged", "veryDamaged"}; + private static final String[] N = new String[] { "anvil_top_damaged_0", "anvil_top_damaged_1", "anvil_top_damaged_2"}; + + protected BlockAnvil() { + super(Material.HEAVY); + this.g(0); + this.a(CreativeModeTab.c); + } + + // Spigot start + @Override + public AxisAlignedBB a( World world, int i, int j, int k ) + { + updateShape( world, i, j, k ); + return super.a( world, i, j, k ); + } + // Spigot end + + public boolean d() { + return false; + } + + public boolean c() { + return false; + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3; + int i1 = world.getData(i, j, k) >> 2; + + ++l; + l %= 4; + if (l == 0) { + world.setData(i, j, k, 2 | i1 << 2, 2); + } + + if (l == 1) { + world.setData(i, j, k, 3 | i1 << 2, 2); + } + + if (l == 2) { + world.setData(i, j, k, 0 | i1 << 2, 2); + } + + if (l == 3) { + world.setData(i, j, k, 1 | i1 << 2, 2); + } + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (world.isStatic) { + return true; + } else { + entityhuman.openAnvil(i, j, k); + return true; + } + } + + public int b() { + return 35; + } + + public int getDropData(int i) { + return i >> 2; + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k) & 3; + + if (l != 3 && l != 1) { + this.a(0.125F, 0.0F, 0.0F, 0.875F, 1.0F, 1.0F); + } else { + this.a(0.0F, 0.0F, 0.125F, 1.0F, 1.0F, 0.875F); + } + } + + protected void a(EntityFallingBlock entityfallingblock) { + entityfallingblock.a(true); + } + + public void a(World world, int i, int j, int k, int l) { + world.triggerEffect(1022, i, j, k, 0); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockBloodStone.java b/vspigot-server/src/main/java/net/minecraft/server/BlockBloodStone.java new file mode 100644 index 0000000..ca46ea6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockBloodStone.java @@ -0,0 +1,27 @@ +package net.minecraft.server; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockBloodStone extends Block { + + public BlockBloodStone() { + super(Material.STONE); + this.a(CreativeModeTab.b); + } + + public MaterialMapColor f(int i) { + return MaterialMapColor.K; + } + + // CraftBukkit start + public void doPhysics(World world, int i, int j, int k, int l) { + if (net.minecraft.server.Block.getById(l) != null && net.minecraft.server.Block.getById(l).isPowerSource()) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + int power = block.getBlockPower(); + + BlockRedstoneEvent event = new BlockRedstoneEvent(block, power, power); + world.getServer().getPluginManager().callEvent(event); + } + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockBrewingStand.java b/vspigot-server/src/main/java/net/minecraft/server/BlockBrewingStand.java new file mode 100644 index 0000000..9f49f43 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockBrewingStand.java @@ -0,0 +1,115 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +public class BlockBrewingStand extends BlockContainer { + + private Random a = new Random(); + + public BlockBrewingStand() { + super(Material.ORE); + } + + public boolean c() { + return false; + } + + public int b() { + return 25; + } + + public TileEntity a(World world, int i) { + return new TileEntityBrewingStand(); + } + + public boolean d() { + return false; + } + + public void a(World world, int i, int j, int k, AxisAlignedBB axisalignedbb, List list, Entity entity) { + this.a(0.4375F, 0.0F, 0.4375F, 0.5625F, 0.875F, 0.5625F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.g(); + super.a(world, i, j, k, axisalignedbb, list, entity); + } + + public void g() { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (world.isStatic) { + return true; + } else { + TileEntityBrewingStand tileentitybrewingstand = (TileEntityBrewingStand) world.getTileEntity(i, j, k); + + if (tileentitybrewingstand != null) { + entityhuman.openBrewingStand(tileentitybrewingstand); + } + + return true; + } + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + if (itemstack.hasName()) { + ((TileEntityBrewingStand) world.getTileEntity(i, j, k)).a(itemstack.getName()); + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + TileEntity tileentity = world.getTileEntity(i, j, k); + + if (tileentity instanceof TileEntityBrewingStand) { + TileEntityBrewingStand tileentitybrewingstand = (TileEntityBrewingStand) tileentity; + + for (int i1 = 0; i1 < tileentitybrewingstand.getSize(); ++i1) { + ItemStack itemstack = tileentitybrewingstand.getItem(i1); + + if (itemstack != null) { + float f = this.a.nextFloat() * 0.8F + 0.1F; + float f1 = this.a.nextFloat() * 0.8F + 0.1F; + float f2 = this.a.nextFloat() * 0.8F + 0.1F; + + while (itemstack.count > 0) { + int j1 = this.a.nextInt(21) + 10; + + if (j1 > itemstack.count) { + j1 = itemstack.count; + } + + itemstack.count -= j1; + EntityItem entityitem = new EntityItem(world, (double) ((float) i + f), (double) ((float) j + f1), (double) ((float) k + f2), new ItemStack(itemstack.getItem(), j1, itemstack.getData())); + float f3 = 0.05F; + + entityitem.motX = (double) ((float) this.a.nextGaussian() * f3); + entityitem.motY = (double) ((float) this.a.nextGaussian() * f3 + 0.2F); + entityitem.motZ = (double) ((float) this.a.nextGaussian() * f3); + // Spigot Start + if ( itemstack.hasTag() ) + { + entityitem.getItemStack().setTag( (NBTTagCompound) itemstack.getTag().clone() ); + } + // Spigot End + world.addEntity( entityitem ); + } + } + } + } + + super.remove(world, i, j, k, block, l); + } + + public Item getDropType(int i, Random random, int j) { + return Items.BREWING_STAND; + } + + public boolean isComplexRedstone() { + return true; + } + + public int g(World world, int i, int j, int k, int l) { + return Container.b((IInventory) world.getTileEntity(i, j, k)); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockButtonAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/BlockButtonAbstract.java new file mode 100644 index 0000000..6e54205 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockButtonAbstract.java @@ -0,0 +1,332 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +// CraftBukkit start +import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.event.entity.EntityInteractEvent; +// CraftBukkit end + +public abstract class BlockButtonAbstract extends Block { + + private final boolean a; + + protected BlockButtonAbstract(boolean flag) { + super(Material.ORIENTABLE); + this.a(true); + this.a(CreativeModeTab.d); + this.a = flag; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public int a(World world) { + return this.a ? 30 : 20; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public boolean canPlace(World world, int i, int j, int k, int l) { + return l == 2 && world.getType(i, j, k + 1).r() ? true : (l == 3 && world.getType(i, j, k - 1).r() ? true : (l == 4 && world.getType(i + 1, j, k).r() ? true : l == 5 && world.getType(i - 1, j, k).r())); + } + + public boolean canPlace(World world, int i, int j, int k) { + return world.getType(i - 1, j, k).r() ? true : (world.getType(i + 1, j, k).r() ? true : (world.getType(i, j, k - 1).r() ? true : world.getType(i, j, k + 1).r())); + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + int j1 = world.getData(i, j, k); + int k1 = j1 & 8; + + j1 &= 7; + if (l == 2 && world.getType(i, j, k + 1).r()) { + j1 = 4; + } else if (l == 3 && world.getType(i, j, k - 1).r()) { + j1 = 3; + } else if (l == 4 && world.getType(i + 1, j, k).r()) { + j1 = 2; + } else if (l == 5 && world.getType(i - 1, j, k).r()) { + j1 = 1; + } else { + j1 = this.e(world, i, j, k); + } + + return j1 + k1; + } + + private int e(World world, int i, int j, int k) { + return world.getType(i - 1, j, k).r() ? 1 : (world.getType(i + 1, j, k).r() ? 2 : (world.getType(i, j, k - 1).r() ? 3 : (world.getType(i, j, k + 1).r() ? 4 : 1))); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (this.m(world, i, j, k)) { + int l = world.getData(i, j, k) & 7; + boolean flag = false; + + if (!world.getType(i - 1, j, k).r() && l == 1) { + flag = true; + } + + if (!world.getType(i + 1, j, k).r() && l == 2) { + flag = true; + } + + if (!world.getType(i, j, k - 1).r() && l == 3) { + flag = true; + } + + if (!world.getType(i, j, k + 1).r() && l == 4) { + flag = true; + } + + if (flag) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + } + } + + private boolean m(World world, int i, int j, int k) { + if (!this.canPlace(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + return false; + } else { + return true; + } + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + + this.b(l); + } + + private void b(int i) { + int j = i & 7; + boolean flag = (i & 8) > 0; + float f = 0.375F; + float f1 = 0.625F; + float f2 = 0.1875F; + float f3 = 0.125F; + + if (flag) { + f3 = 0.0625F; + } + + if (j == 1) { + this.a(0.0F, f, 0.5F - f2, f3, f1, 0.5F + f2); + } else if (j == 2) { + this.a(1.0F - f3, f, 0.5F - f2, 1.0F, f1, 0.5F + f2); + } else if (j == 3) { + this.a(0.5F - f2, f, 0.0F, 0.5F + f2, f1, f3); + } else if (j == 4) { + this.a(0.5F - f2, f, 1.0F - f3, 0.5F + f2, f1, 1.0F); + } + } + + public void attack(World world, int i, int j, int k, EntityHuman entityhuman) {} + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + int i1 = world.getData(i, j, k); + int j1 = i1 & 7; + int k1 = 8 - (i1 & 8); + + if (k1 == 0) { + return true; + } else { + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + int old = (k1 != 8) ? 15 : 0; + int current = (k1 == 8) ? 15 : 0; + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if ((eventRedstone.getNewCurrent() > 0) != (k1 == 8)) { + return true; + } + // CraftBukkit end + + world.setData(i, j, k, j1 + k1, 3); + world.c(i, j, k, i, j, k); + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "random.click", 0.3F, 0.6F); + this.a(world, i, j, k, j1); + world.a(i, j, k, this, this.a(world)); + return true; + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + if ((l & 8) > 0) { + int i1 = l & 7; + + this.a(world, i, j, k, i1); + } + + super.remove(world, i, j, k, block, l); + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return (iblockaccess.getData(i, j, k) & 8) > 0 ? 15 : 0; + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + int i1 = iblockaccess.getData(i, j, k); + + if ((i1 & 8) == 0) { + return 0; + } else { + int j1 = i1 & 7; + + return j1 == 5 && l == 1 ? 15 : (j1 == 4 && l == 2 ? 15 : (j1 == 3 && l == 3 ? 15 : (j1 == 2 && l == 4 ? 15 : (j1 == 1 && l == 5 ? 15 : 0)))); + } + } + + public boolean isPowerSource() { + return true; + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + int l = world.getData(i, j, k); + + if ((l & 8) != 0) { + if (this.a) { + this.n(world, i, j, k); + } else { + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if (eventRedstone.getNewCurrent() > 0) { + return; + } + // CraftBukkit end + world.setData(i, j, k, l & 7, 3); + int i1 = l & 7; + + this.a(world, i, j, k, i1); + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "random.click", 0.3F, 0.5F); + world.c(i, j, k, i, j, k); + } + } + } + } + + public void g() { + float f = 0.1875F; + float f1 = 0.125F; + float f2 = 0.125F; + + this.a(0.5F - f, 0.5F - f1, 0.5F - f2, 0.5F + f, 0.5F + f1, 0.5F + f2); + } + + public void a(World world, int i, int j, int k, Entity entity) { + if (!world.isStatic) { + if (this.a) { + if ((world.getData(i, j, k) & 8) == 0) { + this.n(world, i, j, k); + } + } + } + } + + private void n(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + int i1 = l & 7; + boolean flag = (l & 8) != 0; + + this.b(l); + List list = world.a(EntityArrow.class, AxisAlignedBB.a((double) i + this.minX, (double) j + this.minY, (double) k + this.minZ, (double) i + this.maxX, (double) j + this.maxY, (double) k + this.maxZ)); + boolean flag1 = !list.isEmpty(); + + // CraftBukkit start - Call interact event when arrows turn on wooden buttons + if (flag != flag1 && flag1) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + boolean allowed = false; + + // If all of the events are cancelled block the button press, else allow + for (Object object : list) { + if (object != null) { + EntityInteractEvent event = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + allowed = true; + break; + } + } + } + + if (!allowed) { + return; + } + } + // CraftBukkit end + + if (flag1 && !flag) { + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 0, 15); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if (eventRedstone.getNewCurrent() <= 0) { + return; + } + // CraftBukkit end + world.setData(i, j, k, i1 | 8, 3); + this.a(world, i, j, k, i1); + world.c(i, j, k, i, j, k); + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "random.click", 0.3F, 0.6F); + } + + if (!flag1 && flag) { + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if (eventRedstone.getNewCurrent() > 0) { + return; + } + // CraftBukkit end + world.setData(i, j, k, i1, 3); + this.a(world, i, j, k, i1); + world.c(i, j, k, i, j, k); + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "random.click", 0.3F, 0.5F); + } + + if (flag1) { + world.a(i, j, k, this, this.a(world)); + } + } + + private void a(World world, int i, int j, int k, int l) { + world.applyPhysics(i, j, k, this); + if (l == 1) { + world.applyPhysics(i - 1, j, k, this); + } else if (l == 2) { + world.applyPhysics(i + 1, j, k, this); + } else if (l == 3) { + world.applyPhysics(i, j, k - 1, this); + } else if (l == 4) { + world.applyPhysics(i, j, k + 1, this); + } else { + world.applyPhysics(i, j - 1, k, this); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockCactus.java b/vspigot-server/src/main/java/net/minecraft/server/BlockCactus.java new file mode 100644 index 0000000..e2e41b9 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockCactus.java @@ -0,0 +1,86 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockCactus extends Block { + + protected BlockCactus() { + super(Material.CACTUS); + this.a(true); + this.a(CreativeModeTab.c); + } + + public void a(World world, int i, int j, int k, Random random) { + if (world.isEmpty(i, j + 1, k)) { + int l; + + for (l = 1; world.getType(i, j - l, k) == this; ++l) { + ; + } + + if (l < world.paperSpigotConfig.cactusMaxHeight) { // PaperSpigot - Configurable max growth height for cactus blocks + int i1 = world.getData(i, j, k); + + if (i1 >= (byte) range(3, (world.growthOdds / world.spigotConfig.cactusModifier * 15) + 0.5F, 15)) { // Spigot + CraftEventFactory.handleBlockGrowEvent(world, i, j + 1, k, this, 0); // CraftBukkit + world.setData(i, j, k, 0, 4); + this.doPhysics(world, i, j + 1, k, this); + } else { + world.setData(i, j, k, i1 + 1, 4); + } + } + } + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + float f = 0.0625F; + + return AxisAlignedBB.a((double) ((float) i + f), (double) j, (double) ((float) k + f), (double) ((float) (i + 1) - f), (double) ((float) (j + 1) - f), (double) ((float) (k + 1) - f)); + } + + public boolean d() { + return false; + } + + public boolean c() { + return false; + } + + public int b() { + return 13; + } + + public boolean canPlace(World world, int i, int j, int k) { + return !super.canPlace(world, i, j, k) ? false : this.j(world, i, j, k); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!this.j(world, i, j, k)) { + world.setAir(i, j, k, true); + } + } + + public boolean j(World world, int i, int j, int k) { + if (world.getType(i - 1, j, k).getMaterial().isBuildable()) { + return false; + } else if (world.getType(i + 1, j, k).getMaterial().isBuildable()) { + return false; + } else if (world.getType(i, j, k - 1).getMaterial().isBuildable()) { + return false; + } else if (world.getType(i, j, k + 1).getMaterial().isBuildable()) { + return false; + } else { + Block block = world.getType(i, j - 1, k); + + return block == Blocks.CACTUS || block == Blocks.SAND; + } + } + + public void a(World world, int i, int j, int k, Entity entity) { + CraftEventFactory.blockDamage = world.getWorld().getBlockAt(i, j, k); // CraftBukkit + entity.damageEntity(DamageSource.CACTUS, 1.0F); + CraftEventFactory.blockDamage = null; // CraftBukkit + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockCake.java b/vspigot-server/src/main/java/net/minecraft/server/BlockCake.java new file mode 100644 index 0000000..e65a270 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockCake.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockCake extends Block { + + protected BlockCake() { + super(Material.CAKE); + this.a(true); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + float f = 0.0625F; + float f1 = (float) (1 + l * 2) / 16.0F; + float f2 = 0.5F; + + this.a(f1, 0.0F, f, 1.0F - f, f2, 1.0F - f); + } + + public void g() { + float f = 0.0625F; + float f1 = 0.5F; + + this.a(f, 0.0F, f, 1.0F - f, f1, 1.0F - f); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + float f = 0.0625F; + float f1 = (float) (1 + l * 2) / 16.0F; + float f2 = 0.5F; + + return AxisAlignedBB.a((double) ((float) i + f1), (double) j, (double) ((float) k + f), (double) ((float) (i + 1) - f), (double) ((float) j + f2 - f), (double) ((float) (k + 1) - f)); + } + + public boolean d() { + return false; + } + + public boolean c() { + return false; + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + this.b(world, i, j, k, entityhuman); + return true; + } + + public void attack(World world, int i, int j, int k, EntityHuman entityhuman) { + this.b(world, i, j, k, entityhuman); + } + + private void b(World world, int i, int j, int k, EntityHuman entityhuman) { + if (entityhuman.g(false)) { + // CraftBukkit start + int oldFoodLevel = entityhuman.getFoodData().foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, 2 + oldFoodLevel); + + if (!event.isCancelled()) { + entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F); + } + + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel)); + // CraftBukkit end + int l = world.getData(i, j, k) + 1; + + if (l >= 6) { + world.setAir(i, j, k); + } else { + world.setData(i, j, k, l, 2); + } + } + } + + public boolean canPlace(World world, int i, int j, int k) { + return !super.canPlace(world, i, j, k) ? false : this.j(world, i, j, k); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!this.j(world, i, j, k)) { + world.setAir(i, j, k); + } + } + + public boolean j(World world, int i, int j, int k) { + return world.getType(i, j - 1, k).getMaterial().isBuildable(); + } + + public int a(Random random) { + return 0; + } + + public Item getDropType(int i, Random random, int j) { + return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockCocoa.java b/vspigot-server/src/main/java/net/minecraft/server/BlockCocoa.java new file mode 100644 index 0000000..5584fbd --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockCocoa.java @@ -0,0 +1,144 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockCocoa extends BlockDirectional implements IBlockFragilePlantElement { + + public BlockCocoa() { + super(Material.PLANT); + this.a(true); + } + + public void a(World world, int i, int j, int k, Random random) { + if (!this.j(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setTypeAndData(i, j, k, getById(0), 0, 2); + } else if (world.random.nextInt(5) == 0) { + int l = world.getData(i, j, k); + int i1 = c(l); + + if (i1 < 2) { + ++i1; + // CraftBukkit + CraftEventFactory.handleBlockGrowEvent(world, i, j, k, this, i1 << 2 | l(l)); + } + } + } + + public boolean j(World world, int i, int j, int k) { + int l = l(world.getData(i, j, k)); + + i += Direction.a[l]; + k += Direction.b[l]; + Block block = world.getType(i, j, k); + + return block == Blocks.LOG && BlockLogAbstract.c(world.getData(i, j, k)) == 3; + } + + public int b() { + return 28; + } + + public boolean d() { + return false; + } + + public boolean c() { + return false; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + int i1 = l(l); + int j1 = c(l); + int k1 = 4 + j1 * 2; + int l1 = 5 + j1 * 2; + float f = (float) k1 / 2.0F; + + switch (i1) { + case 0: + this.a((8.0F - f) / 16.0F, (12.0F - (float) l1) / 16.0F, (15.0F - (float) k1) / 16.0F, (8.0F + f) / 16.0F, 0.75F, 0.9375F); + break; + + case 1: + this.a(0.0625F, (12.0F - (float) l1) / 16.0F, (8.0F - f) / 16.0F, (1.0F + (float) k1) / 16.0F, 0.75F, (8.0F + f) / 16.0F); + break; + + case 2: + this.a((8.0F - f) / 16.0F, (12.0F - (float) l1) / 16.0F, 0.0625F, (8.0F + f) / 16.0F, 0.75F, (1.0F + (float) k1) / 16.0F); + break; + + case 3: + this.a((15.0F - (float) k1) / 16.0F, (12.0F - (float) l1) / 16.0F, (8.0F - f) / 16.0F, 0.9375F, 0.75F, (8.0F + f) / 16.0F); + } + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = ((MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3) + 0) % 4; + + world.setData(i, j, k, l, 2); + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + if (l == 1 || l == 0) { + l = 2; + } + + return Direction.f[Direction.e[l]]; + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!this.j(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setTypeAndData(i, j, k, getById(0), 0, 2); + } + } + + public static int c(int i) { + return (i & 12) >> 2; + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + int j1 = c(l); + byte b0 = 1; + + if (j1 >= 2) { + b0 = 3; + } + + for (int k1 = 0; k1 < b0; ++k1) { + this.a(world, i, j, k, new ItemStack(Items.INK_SACK, 1, 3)); + } + } + + public int getDropData(World world, int i, int j, int k) { + return 3; + } + + public boolean a(World world, int i, int j, int k, boolean flag) { + int l = world.getData(i, j, k); + int i1 = c(l); + + return i1 < 2; + } + + public boolean a(World world, Random random, int i, int j, int k) { + return true; + } + + public void b(World world, Random random, int i, int j, int k) { + int l = world.getData(i, j, k); + int i1 = BlockDirectional.l(l); + int j1 = c(l); + + ++j1; + CraftEventFactory.handleBlockGrowEvent(world, i, j, k, this, j1 << 2 | i1); // CraftBukkit + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockCommand.java b/vspigot-server/src/main/java/net/minecraft/server/BlockCommand.java new file mode 100644 index 0000000..c60f0a9 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockCommand.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockCommand extends BlockContainer { + + public BlockCommand() { + super(Material.ORE); + } + + public TileEntity a(World world, int i) { + return new TileEntityCommand(); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!world.isStatic) { + boolean flag = world.isBlockIndirectlyPowered(i, j, k); + int l = world.getData(i, j, k); + boolean flag1 = (l & 1) != 0; + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(i, j, k); + int old = flag1 ? 15 : 0; + int current = flag ? 15 : 0; + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, old, current); + world.getServer().getPluginManager().callEvent(eventRedstone); + // CraftBukkit end + + if (eventRedstone.getNewCurrent() > 0 && !(eventRedstone.getOldCurrent() > 0)) { // CraftBukkit + world.setData(i, j, k, l | 1, 4); + world.a(i, j, k, this, this.a(world)); + } else if (!(eventRedstone.getNewCurrent() > 0) && eventRedstone.getOldCurrent() > 0) { // CraftBukkit + world.setData(i, j, k, l & -2, 4); + } + } + } + + public void a(World world, int i, int j, int k, Random random) { + TileEntity tileentity = world.getTileEntity(i, j, k); + + if (tileentity != null && tileentity instanceof TileEntityCommand) { + CommandBlockListenerAbstract commandblocklistenerabstract = ((TileEntityCommand) tileentity).getCommandBlock(); + + commandblocklistenerabstract.a(world); + world.updateAdjacentComparators(i, j, k, this); + } + } + + public int a(World world) { + return 1; + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + TileEntityCommand tileentitycommand = (TileEntityCommand) world.getTileEntity(i, j, k); + + if (tileentitycommand != null) { + entityhuman.a((TileEntity) tileentitycommand); + } + + return true; + } + + public boolean isComplexRedstone() { + return true; + } + + public int g(World world, int i, int j, int k, int l) { + TileEntity tileentity = world.getTileEntity(i, j, k); + + return tileentity != null && tileentity instanceof TileEntityCommand ? ((TileEntityCommand) tileentity).getCommandBlock().g() : 0; + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + TileEntityCommand tileentitycommand = (TileEntityCommand) world.getTileEntity(i, j, k); + + if (itemstack.hasName()) { + tileentitycommand.getCommandBlock().setName(itemstack.getName()); + } + } + + public int a(Random random) { + return 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockCrops.java b/vspigot-server/src/main/java/net/minecraft/server/BlockCrops.java new file mode 100644 index 0000000..e377557 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockCrops.java @@ -0,0 +1,136 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockCrops extends BlockPlant implements IBlockFragilePlantElement { + + protected BlockCrops() { + this.a(true); + float f = 0.5F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.25F, 0.5F + f); + this.a((CreativeModeTab) null); + this.c(0.0F); + this.a(h); + this.H(); + } + + protected boolean a(Block block) { + return block == Blocks.SOIL; + } + + public void a(World world, int i, int j, int k, Random random) { + super.a(world, i, j, k, random); + if (world.isLightLevel(i, j + 1, k, 9)) { // MineHQ + int l = world.getData(i, j, k); + + if (l < 7) { + float f = this.n(world, i, j, k); + + if (random.nextInt((int) (world.growthOdds / world.spigotConfig.wheatModifier * (25.0F / f)) + 1) == 0) { // Spigot + ++l; + CraftEventFactory.handleBlockGrowEvent(world, i, j, k, this, l); // CraftBukkit + } + } + } + } + + public void m(World world, int i, int j, int k) { + int l = world.getData(i, j, k) + MathHelper.nextInt(world.random, 2, 5); + + if (l > 7) { + l = 7; + } + + CraftEventFactory.handleBlockGrowEvent(world, i, j, k, this, l); // CraftBukkit + } + + private float n(World world, int i, int j, int k) { + float f = 1.0F; + Block block = world.getType(i, j, k - 1); + Block block1 = world.getType(i, j, k + 1); + Block block2 = world.getType(i - 1, j, k); + Block block3 = world.getType(i + 1, j, k); + Block block4 = world.getType(i - 1, j, k - 1); + Block block5 = world.getType(i + 1, j, k - 1); + Block block6 = world.getType(i + 1, j, k + 1); + Block block7 = world.getType(i - 1, j, k + 1); + boolean flag = block2 == this || block3 == this; + boolean flag1 = block == this || block1 == this; + boolean flag2 = block4 == this || block5 == this || block6 == this || block7 == this; + + for (int l = i - 1; l <= i + 1; ++l) { + for (int i1 = k - 1; i1 <= k + 1; ++i1) { + float f1 = 0.0F; + + if (world.getType(l, j - 1, i1) == Blocks.SOIL) { + f1 = 1.0F; + if (world.getData(l, j - 1, i1) > 0) { + f1 = 3.0F; + } + } + + if (l != i || i1 != k) { + f1 /= 4.0F; + } + + f += f1; + } + } + + if (flag2 || flag && flag1) { + f /= 2.0F; + } + + return f; + } + + public int b() { + return 6; + } + + protected Item i() { + return Items.SEEDS; + } + + protected Item P() { + return Items.WHEAT; + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + super.dropNaturally(world, i, j, k, l, f, 0); + if (!world.isStatic) { + if (l >= 7) { + int j1 = 3 + i1; + + for (int k1 = 0; k1 < j1; ++k1) { + if (world.random.nextInt(15) <= l) { + this.a(world, i, j, k, new ItemStack(this.i(), 1, 0)); + } + } + } + } + } + + public Item getDropType(int i, Random random, int j) { + return i == 7 ? this.P() : this.i(); + } + + public int a(Random random) { + return 1; + } + + public boolean a(World world, int i, int j, int k, boolean flag) { + return world.getData(i, j, k) != 7; + } + + public boolean a(World world, Random random, int i, int j, int k) { + return true; + } + + public void b(World world, Random random, int i, int j, int k) { + this.m(world, i, j, k); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockDaylightDetector.java b/vspigot-server/src/main/java/net/minecraft/server/BlockDaylightDetector.java new file mode 100644 index 0000000..1298610 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockDaylightDetector.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockDaylightDetector extends BlockContainer { + + private IIcon[] a = new IIcon[2]; + + public BlockDaylightDetector() { + super(Material.WOOD); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.375F, 1.0F); + this.a(CreativeModeTab.d); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.375F, 1.0F); + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return iblockaccess.getData(i, j, k); + } + + public void a(World world, int i, int j, int k, Random random) {} + + public void doPhysics(World world, int i, int j, int k, Block block) {} + + public void onPlace(World world, int i, int j, int k) {} + + public void e(World world, int i, int j, int k) { + if (!world.worldProvider.g) { + int l = world.getData(i, j, k); + int i1 = world.b(EnumSkyBlock.SKY, i, j, k) - world.j; + float f = world.d(1.0F); + + if (f < 3.1415927F) { + f += (0.0F - f) * 0.2F; + } else { + f += (6.2831855F - f) * 0.2F; + } + + // PaperSpigot start - Configurable "inversion" for daylight detectors + if (world.paperSpigotConfig.invertedDaylightDetectors) { + i1 = Math.round((float) i1 * MathHelper.cos(f) * -1 + 15); + if (i1 < 10) { + i1 = 0; + } + + if (i1 > 9) { + i1 = 15; + } + } else { + i1 = Math.round((float) i1 * MathHelper.cos(f)); + if (i1 < 0) { + i1 = 0; + } + + if (i1 > 15) { + i1 = 15; + } + } + // PaperSpigot end + + if (l != i1) { + i1 = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, i, j, k, l, i1).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent + world.setData(i, j, k, i1, 3); + } + } + } + + public boolean d() { + return false; + } + + public boolean c() { + return false; + } + + public boolean isPowerSource() { + return true; + } + + public TileEntity a(World world, int i) { + return new TileEntityLightDetector(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockDiodeAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/BlockDiodeAbstract.java new file mode 100644 index 0000000..ec581e0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockDiodeAbstract.java @@ -0,0 +1,259 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public abstract class BlockDiodeAbstract extends BlockDirectional { + + protected final boolean a; + + protected BlockDiodeAbstract(boolean flag) { + super(Material.ORIENTABLE); + this.a = flag; + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); + } + + public boolean d() { + return false; + } + + public boolean canPlace(World world, int i, int j, int k) { + return !World.a((IBlockAccess) world, i, j - 1, k) ? false : super.canPlace(world, i, j, k); + } + + public boolean j(World world, int i, int j, int k) { + return !World.a((IBlockAccess) world, i, j - 1, k) ? false : super.j(world, i, j, k); + } + + public void a(World world, int i, int j, int k, Random random) { + int l = world.getData(i, j, k); + + if (!this.g((IBlockAccess) world, i, j, k, l)) { // CraftBukkit - Cast world to IBlockAccess to call the right method. + boolean flag = this.a(world, i, j, k, l); + + if (this.a && !flag) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, i, j, k, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end + + world.setTypeAndData(i, j, k, this.i(), l, 2); + } else if (!this.a) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, i, j, k, 0, 15).getNewCurrent() != 15) { + return; + } + // CraftBukkit end + + world.setTypeAndData(i, j, k, this.e(), l, 2); + if (!flag) { + world.a(i, j, k, this.e(), this.k(l), -1); + } + } + } + } + + public int b() { + return 36; + } + + protected boolean c(int i) { + return this.a; + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return this.b(iblockaccess, i, j, k, l); + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + int i1 = iblockaccess.getData(i, j, k); + + if (!this.c(i1)) { + return 0; + } else { + int j1 = l(i1); + + return j1 == 0 && l == 3 ? this.f(iblockaccess, i, j, k, i1) : (j1 == 1 && l == 4 ? this.f(iblockaccess, i, j, k, i1) : (j1 == 2 && l == 2 ? this.f(iblockaccess, i, j, k, i1) : (j1 == 3 && l == 5 ? this.f(iblockaccess, i, j, k, i1) : 0))); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!this.j(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + world.applyPhysics(i + 1, j, k, this); + world.applyPhysics(i - 1, j, k, this); + world.applyPhysics(i, j, k + 1, this); + world.applyPhysics(i, j, k - 1, this); + world.applyPhysics(i, j - 1, k, this); + world.applyPhysics(i, j + 1, k, this); + } else { + this.b(world, i, j, k, block); + } + } + + protected void b(World world, int i, int j, int k, Block block) { + int l = world.getData(i, j, k); + + if (!this.g((IBlockAccess) world, i, j, k, l)) { // CraftBukkit - Cast world to IBlockAccess to call the right method. + boolean flag = this.a(world, i, j, k, l); + + if ((this.a && !flag || !this.a && flag) && !world.a(i, j, k, (Block) this)) { + byte b0 = -1; + + if (this.i(world, i, j, k, l)) { + b0 = -3; + } else if (this.a) { + b0 = -2; + } + + world.a(i, j, k, this, this.b(l), b0); + } + } + } + + public boolean g(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return false; + } + + protected boolean a(World world, int i, int j, int k, int l) { + return this.h(world, i, j, k, l) > 0; + } + + protected int h(World world, int i, int j, int k, int l) { + int i1 = l(l); + int j1 = i + Direction.a[i1]; + int k1 = k + Direction.b[i1]; + int l1 = world.getBlockFacePower(j1, j, k1, Direction.d[i1]); + + return l1 >= 15 ? l1 : Math.max(l1, world.getType(j1, j, k1) == Blocks.REDSTONE_WIRE ? world.getData(j1, j, k1) : 0); + } + + protected int h(IBlockAccess iblockaccess, int i, int j, int k, int l) { + int i1 = l(l); + + switch (i1) { + case 0: + case 2: + return Math.max(this.i(iblockaccess, i - 1, j, k, 4), this.i(iblockaccess, i + 1, j, k, 5)); + + case 1: + case 3: + return Math.max(this.i(iblockaccess, i, j, k + 1, 3), this.i(iblockaccess, i, j, k - 1, 2)); + + default: + return 0; + } + } + + protected int i(IBlockAccess iblockaccess, int i, int j, int k, int l) { + Block block = iblockaccess.getType(i, j, k); + + return this.a(block) ? (block == Blocks.REDSTONE_WIRE ? iblockaccess.getData(i, j, k) : iblockaccess.getBlockPower(i, j, k, l)) : 0; + } + + public boolean isPowerSource() { + return true; + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = ((MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4; + + world.setData(i, j, k, l, 3); + boolean flag = this.a(world, i, j, k, l); + + if (flag) { + world.a(i, j, k, this, 1); + } + } + + public void onPlace(World world, int i, int j, int k) { + this.e(world, i, j, k); + } + + protected void e(World world, int i, int j, int k) { + int l = l(world.getData(i, j, k)); + + if (l == 1) { + world.e(i + 1, j, k, this); + world.b(i + 1, j, k, this, 4); + } + + if (l == 3) { + world.e(i - 1, j, k, this); + world.b(i - 1, j, k, this, 5); + } + + if (l == 2) { + world.e(i, j, k + 1, this); + world.b(i, j, k + 1, this, 2); + } + + if (l == 0) { + world.e(i, j, k - 1, this); + world.b(i, j, k - 1, this, 3); + } + } + + public void postBreak(World world, int i, int j, int k, int l) { + if (this.a) { + world.applyPhysics(i + 1, j, k, this); + world.applyPhysics(i - 1, j, k, this); + world.applyPhysics(i, j, k + 1, this); + world.applyPhysics(i, j, k - 1, this); + world.applyPhysics(i, j - 1, k, this); + world.applyPhysics(i, j + 1, k, this); + } + + super.postBreak(world, i, j, k, l); + } + + public boolean c() { + return false; + } + + protected boolean a(Block block) { + return block.isPowerSource(); + } + + protected int f(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return 15; + } + + public static boolean d(Block block) { + return Blocks.DIODE_OFF.e(block) || Blocks.REDSTONE_COMPARATOR_OFF.e(block); + } + + public boolean e(Block block) { + return block == this.e() || block == this.i(); + } + + public boolean i(World world, int i, int j, int k, int l) { + int i1 = l(l); + + if (d(world.getType(i - Direction.a[i1], j, k - Direction.b[i1]))) { + int j1 = world.getData(i - Direction.a[i1], j, k - Direction.b[i1]); + int k1 = l(j1); + + return k1 != i1; + } else { + return false; + } + } + + protected int k(int i) { + return this.b(i); + } + + protected abstract int b(int i); + + protected abstract BlockDiodeAbstract e(); + + protected abstract BlockDiodeAbstract i(); + + public boolean c(Block block) { + return this.e(block); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockDispenser.java b/vspigot-server/src/main/java/net/minecraft/server/BlockDispenser.java new file mode 100644 index 0000000..9450df9 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockDispenser.java @@ -0,0 +1,189 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockDispenser extends BlockContainer { + + public static final IRegistry a = new RegistryDefault(new DispenseBehaviorItem()); + protected Random b = new Random(); + public static boolean eventFired = false; // CraftBukkit + + protected BlockDispenser() { + super(Material.STONE); + this.a(CreativeModeTab.d); + } + + public int a(World world) { + return 4; + } + + public void onPlace(World world, int i, int j, int k) { + super.onPlace(world, i, j, k); + this.m(world, i, j, k); + } + + private void m(World world, int i, int j, int k) { + if (!world.isStatic) { + Block block = world.getType(i, j, k - 1); + Block block1 = world.getType(i, j, k + 1); + Block block2 = world.getType(i - 1, j, k); + Block block3 = world.getType(i + 1, j, k); + byte b0 = 3; + + if (block.j() && !block1.j()) { + b0 = 3; + } + + if (block1.j() && !block.j()) { + b0 = 2; + } + + if (block2.j() && !block3.j()) { + b0 = 5; + } + + if (block3.j() && !block2.j()) { + b0 = 4; + } + + world.setData(i, j, k, b0, 2); + } + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (world.isStatic) { + return true; + } else { + TileEntityDispenser tileentitydispenser = (TileEntityDispenser) world.getTileEntity(i, j, k); + + if (tileentitydispenser != null) { + entityhuman.openDispenser(tileentitydispenser); + } + + return true; + } + } + + // CraftBukkit - protected -> public + public void dispense(World world, int i, int j, int k) { + SourceBlock sourceblock = new SourceBlock(world, i, j, k); + TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity(); + + if (tileentitydispenser != null) { + int l = tileentitydispenser.i(); + + if (l < 0) { + world.triggerEffect(1001, i, j, k, 0); + } else { + ItemStack itemstack = tileentitydispenser.getItem(l); + IDispenseBehavior idispensebehavior = this.a(itemstack); + + if (idispensebehavior != IDispenseBehavior.a) { + ItemStack itemstack1 = idispensebehavior.a(sourceblock, itemstack); + eventFired = false; // CraftBukkit - reset event status + + tileentitydispenser.setItem(l, itemstack1.count == 0 ? null : itemstack1); + } + } + } + } + + protected IDispenseBehavior a(ItemStack itemstack) { + return (IDispenseBehavior) a.get(itemstack.getItem()); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + boolean flag = world.isBlockIndirectlyPowered(i, j, k) || world.isBlockIndirectlyPowered(i, j + 1, k); + int l = world.getData(i, j, k); + boolean flag1 = (l & 8) != 0; + + if (flag && !flag1) { + world.a(i, j, k, this, this.a(world)); + world.setData(i, j, k, l | 8, 4); + } else if (!flag && flag1) { + world.setData(i, j, k, l & -9, 4); + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + this.dispense(world, i, j, k); + } + } + + public TileEntity a(World world, int i) { + return new TileEntityDispenser(); + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = BlockPiston.a(world, i, j, k, entityliving); + + world.setData(i, j, k, l, 2); + if (itemstack.hasName()) { + ((TileEntityDispenser) world.getTileEntity(i, j, k)).a(itemstack.getName()); + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + TileEntityDispenser tileentitydispenser = (TileEntityDispenser) world.getTileEntity(i, j, k); + + if (tileentitydispenser != null) { + for (int i1 = 0; i1 < tileentitydispenser.getSize(); ++i1) { + ItemStack itemstack = tileentitydispenser.getItem(i1); + + if (itemstack != null) { + float f = this.b.nextFloat() * 0.8F + 0.1F; + float f1 = this.b.nextFloat() * 0.8F + 0.1F; + float f2 = this.b.nextFloat() * 0.8F + 0.1F; + + while (itemstack.count > 0) { + int j1 = this.b.nextInt(21) + 10; + + if (j1 > itemstack.count) { + j1 = itemstack.count; + } + + itemstack.count -= j1; + EntityItem entityitem = new EntityItem(world, (double) ((float) i + f), (double) ((float) j + f1), (double) ((float) k + f2), new ItemStack(itemstack.getItem(), j1, itemstack.getData())); + + if (itemstack.hasTag()) { + entityitem.getItemStack().setTag((NBTTagCompound) itemstack.getTag().clone()); + } + + float f3 = 0.05F; + + entityitem.motX = (double) ((float) this.b.nextGaussian() * f3); + entityitem.motY = (double) ((float) this.b.nextGaussian() * f3 + 0.2F); + entityitem.motZ = (double) ((float) this.b.nextGaussian() * f3); + world.addEntity(entityitem); + } + } + } + + world.updateAdjacentComparators(i, j, k, block); + } + + super.remove(world, i, j, k, block, l); + } + + public static IPosition a(ISourceBlock isourceblock) { + EnumFacing enumfacing = b(isourceblock.h()); + double d0 = isourceblock.getX() + 0.7D * (double) enumfacing.getAdjacentX(); + double d1 = isourceblock.getY() + 0.7D * (double) enumfacing.getAdjacentY(); + double d2 = isourceblock.getZ() + 0.7D * (double) enumfacing.getAdjacentZ(); + + return new Position(d0, d1, d2); + } + + public static EnumFacing b(int i) { + return EnumFacing.a(i & 7); + } + + public boolean isComplexRedstone() { + return true; + } + + public int g(World world, int i, int j, int k, int l) { + return Container.b((IInventory) world.getTileEntity(i, j, k)); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockDoor.java b/vspigot-server/src/main/java/net/minecraft/server/BlockDoor.java new file mode 100644 index 0000000..9c3ae2e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockDoor.java @@ -0,0 +1,240 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockDoor extends Block { + + protected BlockDoor(Material material) { + super(material); + float f = 0.5F; + float f1 = 1.0F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, f1, 0.5F + f); + } + + public boolean c() { + return false; + } + + public boolean b(IBlockAccess iblockaccess, int i, int j, int k) { + int l = this.g(iblockaccess, i, j, k); + + return (l & 4) != 0; + } + + public boolean d() { + return false; + } + + public int b() { + return 7; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.b(this.g(iblockaccess, i, j, k)); + } + + public int e(IBlockAccess iblockaccess, int i, int j, int k) { + return this.g(iblockaccess, i, j, k) & 3; + } + + public boolean f(IBlockAccess iblockaccess, int i, int j, int k) { + return (this.g(iblockaccess, i, j, k) & 4) != 0; + } + + private void b(int i) { + float f = 0.1875F; + + this.a(0.0F, 0.0F, 0.0F, 1.0F, 2.0F, 1.0F); + int j = i & 3; + boolean flag = (i & 4) != 0; + boolean flag1 = (i & 16) != 0; + + if (j == 0) { + if (flag) { + if (!flag1) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f); + } else { + this.a(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F); + } + } else { + this.a(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F); + } + } else if (j == 1) { + if (flag) { + if (!flag1) { + this.a(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } else { + this.a(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F); + } + } else { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f); + } + } else if (j == 2) { + if (flag) { + if (!flag1) { + this.a(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F); + } else { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f); + } + } else { + this.a(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + } else if (j == 3) { + if (flag) { + if (!flag1) { + this.a(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F); + } else { + this.a(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + } else { + this.a(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F); + } + } + } + + public void attack(World world, int i, int j, int k, EntityHuman entityhuman) {} + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (this.material == Material.ORE) { + return true; + } else { + int i1 = this.g(world, i, j, k); + int j1 = i1 & 7; + + j1 ^= 4; + if ((i1 & 8) == 0) { + world.setData(i, j, k, j1, 2); + world.c(i, j, k, i, j, k); + } else { + world.setData(i, j - 1, k, j1, 2); + world.c(i, j - 1, k, i, j, k); + } + + world.a(entityhuman, 1003, i, j, k, 0); + return true; + } + } + + public void setDoor(World world, int i, int j, int k, boolean flag) { + int l = this.g(world, i, j, k); + boolean flag1 = (l & 4) != 0; + + if (flag1 != flag) { + int i1 = l & 7; + + i1 ^= 4; + if ((l & 8) == 0) { + world.setData(i, j, k, i1, 2); + world.c(i, j, k, i, j, k); + } else { + world.setData(i, j - 1, k, i1, 2); + world.c(i, j - 1, k, i, j, k); + } + + world.a((EntityHuman) null, 1003, i, j, k, 0); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + int l = world.getData(i, j, k); + + if ((l & 8) == 0) { + boolean flag = false; + + if (world.getType(i, j + 1, k) != this) { + world.setAir(i, j, k); + flag = true; + } + + if (!World.a((IBlockAccess) world, i, j - 1, k)) { + world.setAir(i, j, k); + flag = true; + if (world.getType(i, j + 1, k) == this) { + world.setAir(i, j + 1, k); + } + } + + if (flag) { + if (!world.isStatic) { + this.b(world, i, j, k, l, 0); + } + // CraftBukkit start + } else if (block.isPowerSource()) { + org.bukkit.World bworld = world.getWorld(); + org.bukkit.block.Block bukkitBlock = bworld.getBlockAt(i, j, k); + org.bukkit.block.Block blockTop = bworld.getBlockAt(i, j + 1, k); + + int power = bukkitBlock.getBlockPower(); + int powerTop = blockTop.getBlockPower(); + if (powerTop > power) power = powerTop; + int oldPower = (world.getData(i, j, k) & 4) > 0 ? 15 : 0; + + if (oldPower == 0 ^ power == 0) { + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, oldPower, power); + world.getServer().getPluginManager().callEvent(eventRedstone); + + this.setDoor(world, i, j, k, eventRedstone.getNewCurrent() > 0); + } + // CraftBukkit end + } + } else { + if (world.getType(i, j - 1, k) != this) { + world.setAir(i, j, k); + } + + if (block != this) { + this.doPhysics(world, i, j - 1, k, block); + } + } + } + + public Item getDropType(int i, Random random, int j) { + return (i & 8) != 0 ? null : (this.material == Material.ORE ? Items.IRON_DOOR : Items.WOOD_DOOR); + } + + public MovingObjectPosition a(World world, int i, int j, int k, Vec3D vec3d, Vec3D vec3d1) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k, vec3d, vec3d1); + } + + public boolean canPlace(World world, int i, int j, int k) { + return j >= 255 ? false : World.a((IBlockAccess) world, i, j - 1, k) && super.canPlace(world, i, j, k) && super.canPlace(world, i, j + 1, k); + } + + public int h() { + return 1; + } + + public int g(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + boolean flag = (l & 8) != 0; + int i1; + int j1; + + if (flag) { + i1 = iblockaccess.getData(i, j - 1, k); + j1 = l; + } else { + i1 = l; + j1 = iblockaccess.getData(i, j + 1, k); + } + + boolean flag1 = (j1 & 1) != 0; + + return i1 & 7 | (flag ? 8 : 0) | (flag1 ? 16 : 0); + } + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild && (l & 8) != 0 && world.getType(i, j - 1, k) == this) { + world.setAir(i, j - 1, k); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockDragonEgg.java b/vspigot-server/src/main/java/net/minecraft/server/BlockDragonEgg.java new file mode 100644 index 0000000..385225a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockDragonEgg.java @@ -0,0 +1,124 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.BlockFromToEvent; // CraftBukkit + +public class BlockDragonEgg extends Block { + + public BlockDragonEgg() { + super(Material.DRAGON_EGG); + this.a(0.0625F, 0.0F, 0.0625F, 0.9375F, 1.0F, 0.9375F); + } + + public void onPlace(World world, int i, int j, int k) { + world.a(i, j, k, this, this.a(world)); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + world.a(i, j, k, this, this.a(world)); + } + + public void a(World world, int i, int j, int k, Random random) { + this.e(world, i, j, k); + } + + private void e(World world, int i, int j, int k) { + if (BlockFalling.canFall(world, i, j - 1, k) && j >= 0) { + byte b0 = 32; + + if (!BlockFalling.instaFall && world.b(i - b0, j - b0, k - b0, i + b0, j + b0, k + b0)) { + // CraftBukkit - added data + // PaperSpigot start - Add FallingBlock and TNT source location API + org.bukkit.Location loc = new org.bukkit.Location(world.getWorld(), (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)); + EntityFallingBlock entityfallingblock = new EntityFallingBlock(loc, world, (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), this, world.getData(i, j, k)); + // PaperSpigot end + + world.addEntity(entityfallingblock); + } else { + world.setAir(i, j, k); + + while (BlockFalling.canFall(world, i, j - 1, k) && j > 0) { + --j; + } + + if (j > 0) { + world.setTypeAndData(i, j, k, this, 0, 2); + } + } + } + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + this.m(world, i, j, k); + return true; + } + + public void attack(World world, int i, int j, int k, EntityHuman entityhuman) { + this.m(world, i, j, k); + } + + private void m(World world, int i, int j, int k) { + if (world.getType(i, j, k) == this) { + for (int l = 0; l < 1000; ++l) { + int i1 = i + world.random.nextInt(16) - world.random.nextInt(16); + int j1 = j + world.random.nextInt(8) - world.random.nextInt(8); + int k1 = k + world.random.nextInt(16) - world.random.nextInt(16); + + if (world.getType(i1, j1, k1).material == Material.AIR) { + // CraftBukkit start + org.bukkit.block.Block from = world.getWorld().getBlockAt(i, j, k); + org.bukkit.block.Block to = world.getWorld().getBlockAt(i1, j1, k1); + BlockFromToEvent event = new BlockFromToEvent(from, to); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + i1 = event.getToBlock().getX(); + j1 = event.getToBlock().getY(); + k1 = event.getToBlock().getZ(); + // CraftBukkit end + + if (!world.isStatic) { + world.setTypeAndData(i1, j1, k1, this, world.getData(i, j, k), 2); + world.setAir(i, j, k); + } else { + short short1 = 128; + + for (int l1 = 0; l1 < short1; ++l1) { + double d0 = world.random.nextDouble(); + float f = (world.random.nextFloat() - 0.5F) * 0.2F; + float f1 = (world.random.nextFloat() - 0.5F) * 0.2F; + float f2 = (world.random.nextFloat() - 0.5F) * 0.2F; + double d1 = (double) i1 + (double) (i - i1) * d0 + (world.random.nextDouble() - 0.5D) * 1.0D + 0.5D; + double d2 = (double) j1 + (double) (j - j1) * d0 + world.random.nextDouble() * 1.0D - 0.5D; + double d3 = (double) k1 + (double) (k - k1) * d0 + (world.random.nextDouble() - 0.5D) * 1.0D + 0.5D; + + world.addParticle("portal", d1, d2, d3, (double) f, (double) f1, (double) f2); + } + } + + return; + } + } + } + } + + public int a(World world) { + return 5; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 27; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockDropper.java b/vspigot-server/src/main/java/net/minecraft/server/BlockDropper.java new file mode 100644 index 0000000..039afd3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockDropper.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +// CraftBukkit end + +public class BlockDropper extends BlockDispenser { + + private final IDispenseBehavior P = new DispenseBehaviorItem(); + + public BlockDropper() {} + + protected IDispenseBehavior a(ItemStack itemstack) { + return this.P; + } + + public TileEntity a(World world, int i) { + return new TileEntityDropper(); + } + + public void dispense(World world, int i, int j, int k) { // CraftBukkit - protected -> public + SourceBlock sourceblock = new SourceBlock(world, i, j, k); + TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity(); + + if (tileentitydispenser != null) { + int l = tileentitydispenser.i(); + + if (l < 0) { + world.triggerEffect(1001, i, j, k, 0); + } else { + ItemStack itemstack = tileentitydispenser.getItem(l); + int i1 = world.getData(i, j, k) & 7; + IInventory iinventory = TileEntityHopper.getInventoryAt(world, (double) (i + Facing.b[i1]), (double) (j + Facing.c[i1]), (double) (k + Facing.d[i1])); + ItemStack itemstack1; + + if (iinventory != null) { + // CraftBukkit start - Fire event when pushing items into other inventories + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack.cloneItemStack().a(1)); + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + destinationInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(tileentitydispenser.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + itemstack1 = TileEntityHopper.addItem(iinventory, CraftItemStack.asNMSCopy(event.getItem()), Facing.OPPOSITE_FACING[i1]); + if (event.getItem().equals(oitemstack) && itemstack1 == null) { + // CraftBukkit end + itemstack1 = itemstack.cloneItemStack(); + if (--itemstack1.count == 0) { + itemstack1 = null; + } + } else { + itemstack1 = itemstack.cloneItemStack(); + } + } else { + itemstack1 = this.P.a(sourceblock, itemstack); + if (itemstack1 != null && itemstack1.count == 0) { + itemstack1 = null; + } + } + + tileentitydispenser.setItem(l, itemstack1); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockEnderPortal.java b/vspigot-server/src/main/java/net/minecraft/server/BlockEnderPortal.java new file mode 100644 index 0000000..cf22c4d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockEnderPortal.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit + +public class BlockEnderPortal extends BlockContainer { + + public static boolean a; + + protected BlockEnderPortal(Material material) { + super(material); + this.a(1.0F); + } + + public TileEntity a(World world, int i) { + return new TileEntityEnderPortal(); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + float f = 0.0625F; + + this.a(0.0F, 0.0F, 0.0F, 1.0F, f, 1.0F); + } + + public void a(World world, int i, int j, int k, AxisAlignedBB axisalignedbb, List list, Entity entity) {} + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int a(Random random) { + return 0; + } + + public void a(World world, int i, int j, int k, Entity entity) { + if (entity.vehicle == null && entity.passenger == null && !world.isStatic) { + // CraftBukkit start - Entity in portal + EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), i, j, k)); + world.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + entity.b(1); + } + } + + public int b() { + return -1; + } + + public MaterialMapColor f(int i) { + return MaterialMapColor.J; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockFalling.java b/vspigot-server/src/main/java/net/minecraft/server/BlockFalling.java new file mode 100644 index 0000000..07096e8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockFalling.java @@ -0,0 +1,81 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockFalling extends Block { + + public static boolean instaFall; + + public BlockFalling() { + super(Material.SAND); + this.a(CreativeModeTab.b); + } + + public BlockFalling(Material material) { + super(material); + } + + public void onPlace(World world, int i, int j, int k) { + world.a(i, j, k, this, this.a(world)); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + world.a(i, j, k, this, this.a(world)); + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + this.m(world, i, j, k); + } + } + + private void m(World world, int i, int j, int k) { + if (canFall(world, i, j - 1, k) && j >= 0) { + byte b0 = 32; + + if (!instaFall && world.b(i - b0, j - b0, k - b0, i + b0, j + b0, k + b0)) { + if (!world.isStatic) { + // PaperSpigot start - Add FallingBlock and TNT source location API + org.bukkit.Location loc = new org.bukkit.Location(world.getWorld(), (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)); + EntityFallingBlock entityfallingblock = new EntityFallingBlock(loc, world, (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), this, world.getData(i, j, k)); + // PaperSpigot end + + this.a(entityfallingblock); + world.addEntity(entityfallingblock); + } + } else { + world.setAir(i, j, k); + + while (canFall(world, i, j - 1, k) && j > 0) { + --j; + } + + if (j > 0) { + world.setTypeUpdate(i, j, k, this); + } + } + } + } + + protected void a(EntityFallingBlock entityfallingblock) {} + + public int a(World world) { + return 2; + } + + public static boolean canFall(World world, int i, int j, int k) { + Block block = world.getType(i, j, k); + + if (block.material == Material.AIR) { + return true; + } else if (block == Blocks.FIRE) { + return true; + } else { + Material material = block.material; + + return material == Material.WATER ? true : material == Material.LAVA; + } + } + + public void a(World world, int i, int j, int k, int l) {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockFire.java b/vspigot-server/src/main/java/net/minecraft/server/BlockFire.java new file mode 100644 index 0000000..91c6d68 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockFire.java @@ -0,0 +1,282 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockSpreadEvent; +// CraftBukkit end + +public class BlockFire extends Block { + + private int[] a = new int[256]; + private int[] b = new int[256]; + + protected BlockFire() { + super(Material.FIRE); + this.a(true); + } + + public static void e() { + Blocks.FIRE.a(getId(Blocks.WOOD), 5, 20); + Blocks.FIRE.a(getId(Blocks.WOOD_DOUBLE_STEP), 5, 20); + Blocks.FIRE.a(getId(Blocks.WOOD_STEP), 5, 20); + Blocks.FIRE.a(getId(Blocks.FENCE), 5, 20); + Blocks.FIRE.a(getId(Blocks.WOOD_STAIRS), 5, 20); + Blocks.FIRE.a(getId(Blocks.BIRCH_WOOD_STAIRS), 5, 20); + Blocks.FIRE.a(getId(Blocks.SPRUCE_WOOD_STAIRS), 5, 20); + Blocks.FIRE.a(getId(Blocks.JUNGLE_WOOD_STAIRS), 5, 20); + Blocks.FIRE.a(getId(Blocks.LOG), 5, 5); + Blocks.FIRE.a(getId(Blocks.LOG2), 5, 5); + Blocks.FIRE.a(getId(Blocks.LEAVES), 30, 60); + Blocks.FIRE.a(getId(Blocks.LEAVES2), 30, 60); + Blocks.FIRE.a(getId(Blocks.BOOKSHELF), 30, 20); + Blocks.FIRE.a(getId(Blocks.TNT), 15, 100); + Blocks.FIRE.a(getId(Blocks.LONG_GRASS), 60, 100); + Blocks.FIRE.a(getId(Blocks.DOUBLE_PLANT), 60, 100); + Blocks.FIRE.a(getId(Blocks.YELLOW_FLOWER), 60, 100); + Blocks.FIRE.a(getId(Blocks.RED_ROSE), 60, 100); + Blocks.FIRE.a(getId(Blocks.WOOL), 30, 60); + Blocks.FIRE.a(getId(Blocks.VINE), 15, 100); + Blocks.FIRE.a(getId(Blocks.COAL_BLOCK), 5, 5); + Blocks.FIRE.a(getId(Blocks.HAY_BLOCK), 60, 20); + Blocks.FIRE.a(getId(Blocks.WOOL_CARPET), 60, 20); + } + + public void a(int i, int j, int k) { + this.a[i] = j; + this.b[i] = k; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 3; + } + + public int a(Random random) { + return 0; + } + + public int a(World world) { + return 30; + } + + public void a(World world, int i, int j, int k, Random random) { + if (world.getGameRules().getBoolean("doFireTick")) { + // Poweruser start + Block blockBeneath = world.getType(i, j - 1, k); + boolean flag = blockBeneath == Blocks.NETHERRACK || (world.worldProvider instanceof WorldProviderTheEnd && blockBeneath == Blocks.BEDROCK); + // Poweruser end + + if (!this.canPlace(world, i, j, k)) { + fireExtinguished(world, i, j, k); // CraftBukkit - invalid place location + } + + if (!flag && world.Q() && (world.isRainingAt(i, j, k) || world.isRainingAt(i - 1, j, k) || world.isRainingAt(i + 1, j, k) || world.isRainingAt(i, j, k - 1) || world.isRainingAt(i, j, k + 1))) { + fireExtinguished(world, i, j, k); // CraftBukkit - extinguished by rain + } else { + int l = world.getData(i, j, k); + + if (l < 15) { + world.setData(i, j, k, l + random.nextInt(3) / 2, 4); + } + + world.a(i, j, k, this, this.a(world) + random.nextInt(10)); + if (!flag && !this.e(world, i, j, k)) { + if (!World.a((IBlockAccess) world, i, j - 1, k) || l > 3) { + fireExtinguished(world, i, j, k); // CraftBukkit - burn out of inflammable block + } + } else if (!flag && !this.e((IBlockAccess) world, i, j - 1, k) && l == 15 && random.nextInt(4) == 0) { + fireExtinguished(world, i, j, k); // CraftBukkit - burn out + } else { + boolean flag1 = world.z(i, j, k); + byte b0 = 0; + + if (flag1) { + b0 = -50; + } + + this.a(world, i + 1, j, k, 300 + b0, random, l); + this.a(world, i - 1, j, k, 300 + b0, random, l); + this.a(world, i, j - 1, k, 250 + b0, random, l); + this.a(world, i, j + 1, k, 250 + b0, random, l); + this.a(world, i, j, k - 1, 300 + b0, random, l); + this.a(world, i, j, k + 1, 300 + b0, random, l); + + for (int i1 = i - 1; i1 <= i + 1; ++i1) { + for (int j1 = k - 1; j1 <= k + 1; ++j1) { + for (int k1 = j - 1; k1 <= j + 4; ++k1) { + if (i1 != i || k1 != j || j1 != k) { + int l1 = 100; + + if (k1 > j + 1) { + l1 += (k1 - (j + 1)) * 100; + } + + int i2 = this.m(world, i1, k1, j1); + + if (i2 > 0) { + int j2 = (i2 + 40 + world.difficulty.a() * 7) / (l + 30); + + if (flag1) { + j2 /= 2; + } + + if (j2 > 0 && random.nextInt(l1) <= j2 && (!world.Q() || !world.isRainingAt(i1, k1, j1)) && !world.isRainingAt(i1 - 1, k1, k) && !world.isRainingAt(i1 + 1, k1, j1) && !world.isRainingAt(i1, k1, j1 - 1) && !world.isRainingAt(i1, k1, j1 + 1)) { + int k2 = l + random.nextInt(5) / 4; + + if (k2 > 15) { + k2 = 15; + } + + // CraftBukkit start - Call to stop spread of fire + if (world.getType(i1, k1, j1) != Blocks.FIRE) { + if (CraftEventFactory.callBlockIgniteEvent(world, i1, k1, j1, i, j, k).isCancelled()) { + continue; + } + + org.bukkit.Server server = world.getServer(); + org.bukkit.World bworld = world.getWorld(); + org.bukkit.block.BlockState blockState = bworld.getBlockAt(i1, k1, j1).getState(); + blockState.setTypeId(Block.getId(this)); + blockState.setData(new org.bukkit.material.MaterialData(Block.getId(this), (byte) k2)); + + BlockSpreadEvent spreadEvent = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(i, j, k), blockState); + server.getPluginManager().callEvent(spreadEvent); + + if (!spreadEvent.isCancelled()) { + blockState.update(true); + } + } + // CraftBukkit end + } + } + } + } + } + } + } + } + } + } + + public boolean L() { + return false; + } + + private void a(World world, int i, int j, int k, int l, Random random, int i1) { + // Poweruser start + Block blockAtPos = world.getType(i, j, k); + int j1 = this.b[Block.getId(blockAtPos)]; + + if (random.nextInt(l) < j1) { + boolean flag = blockAtPos == Blocks.TNT; + // Poweruser end + // CraftBukkit start + org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(i, j, k); + + BlockBurnEvent event = new BlockBurnEvent(theBlock); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + + if (random.nextInt(i1 + 10) < 5 && !world.isRainingAt(i, j, k)) { + int k1 = i1 + random.nextInt(5) / 4; + + if (k1 > 15) { + k1 = 15; + } + + world.setTypeAndData(i, j, k, this, k1, 3); + } else { + world.setAir(i, j, k); + } + + if (flag) { + Blocks.TNT.postBreak(world, i, j, k, 1); + } + } + } + + private boolean e(World world, int i, int j, int k) { + return this.e((IBlockAccess) world, i + 1, j, k) ? true : (this.e((IBlockAccess) world, i - 1, j, k) ? true : (this.e((IBlockAccess) world, i, j - 1, k) ? true : (this.e((IBlockAccess) world, i, j + 1, k) ? true : (this.e((IBlockAccess) world, i, j, k - 1) ? true : this.e((IBlockAccess) world, i, j, k + 1))))); + } + + private int m(World world, int i, int j, int k) { + byte b0 = 0; + + if (!world.isEmpty(i, j, k)) { + return 0; + } else { + int l = this.a(world, i + 1, j, k, b0); + + l = this.a(world, i - 1, j, k, l); + l = this.a(world, i, j - 1, k, l); + l = this.a(world, i, j + 1, k, l); + l = this.a(world, i, j, k - 1, l); + l = this.a(world, i, j, k + 1, l); + return l; + } + } + + public boolean v() { + return false; + } + + public boolean e(IBlockAccess iblockaccess, int i, int j, int k) { + return this.a[Block.getId(iblockaccess.getType(i, j, k))] > 0; + } + + public int a(World world, int i, int j, int k, int l) { + int i1 = this.a[Block.getId(world.getType(i, j, k))]; + + return i1 > l ? i1 : l; + } + + public boolean canPlace(World world, int i, int j, int k) { + return World.a((IBlockAccess) world, i, j - 1, k) || this.e(world, i, j, k); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!World.a((IBlockAccess) world, i, j - 1, k) && !this.e(world, i, j, k)) { + fireExtinguished(world, i, j, k); // CraftBukkit - fuel block gone + } + } + + public void onPlace(World world, int i, int j, int k) { + if (world.worldProvider.dimension > 0 || !Blocks.PORTAL.e(world, i, j, k)) { + if (!World.a((IBlockAccess) world, i, j - 1, k) && !this.e(world, i, j, k)) { + fireExtinguished(world, i, j, k); // CraftBukkit - fuel block broke + } else { + world.a(i, j, k, this, this.a(world) + world.random.nextInt(10)); + } + } + } + + public MaterialMapColor f(int i) { + return MaterialMapColor.f; + } + + // CraftBukkit start + private void fireExtinguished(World world, int x, int y, int z) { + if (!CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(x, y, z), Blocks.AIR).isCancelled()) { + world.setAir(x, y, z); + } + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockFlowerPot.java b/vspigot-server/src/main/java/net/minecraft/server/BlockFlowerPot.java new file mode 100644 index 0000000..e468eb8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockFlowerPot.java @@ -0,0 +1,187 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockFlowerPot extends BlockContainer { + + public BlockFlowerPot() { + super(Material.ORIENTABLE); + this.g(); + } + + public void g() { + float f = 0.375F; + float f1 = f / 2.0F; + + this.a(0.5F - f1, 0.0F, 0.5F - f1, 0.5F + f1, f, 0.5F + f1); + } + + public boolean c() { + return false; + } + + public int b() { + return 33; + } + + public boolean d() { + return false; + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() instanceof ItemBlock) { + TileEntityFlowerPot tileentityflowerpot = this.e(world, i, j, k); + + if (tileentityflowerpot != null) { + if (tileentityflowerpot.a() != null) { + return false; + } else { + Block block = Block.a(itemstack.getItem()); + + if (!this.a(block, itemstack.getData())) { + return false; + } else { + tileentityflowerpot.a(itemstack.getItem(), itemstack.getData()); + tileentityflowerpot.update(); + if (!world.setData(i, j, k, itemstack.getData(), 2)) { + world.notify(i, j, k); + } + + if (!entityhuman.abilities.canInstantlyBuild && --itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + + return true; + } + } + } else { + return false; + } + } else { + return false; + } + } + + private boolean a(Block block, int i) { + return block != Blocks.YELLOW_FLOWER && block != Blocks.RED_ROSE && block != Blocks.CACTUS && block != Blocks.BROWN_MUSHROOM && block != Blocks.RED_MUSHROOM && block != Blocks.SAPLING && block != Blocks.DEAD_BUSH ? block == Blocks.LONG_GRASS && i == 2 : true; + } + + public int getDropData(World world, int i, int j, int k) { + TileEntityFlowerPot tileentityflowerpot = this.e(world, i, j, k); + + return tileentityflowerpot != null && tileentityflowerpot.a() != null ? tileentityflowerpot.b() : 0; + } + + public boolean canPlace(World world, int i, int j, int k) { + return super.canPlace(world, i, j, k) && World.a((IBlockAccess) world, i, j - 1, k); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!World.a((IBlockAccess) world, i, j - 1, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + TileEntityFlowerPot tileentityflowerpot = this.e(world, i, j, k); + + if (tileentityflowerpot != null && tileentityflowerpot.a() != null) { + this.a(world, i, j, k, new ItemStack(tileentityflowerpot.a(), 1, tileentityflowerpot.b())); + tileentityflowerpot.a( null, 0 ); // Spigot + } + + super.remove(world, i, j, k, block, l); + } + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + super.a(world, i, j, k, l, entityhuman); + if (entityhuman.abilities.canInstantlyBuild) { + TileEntityFlowerPot tileentityflowerpot = this.e(world, i, j, k); + + if (tileentityflowerpot != null) { + tileentityflowerpot.a(Item.getById(0), 0); + } + } + } + + public Item getDropType(int i, Random random, int j) { + return Items.FLOWER_POT; + } + + private TileEntityFlowerPot e(World world, int i, int j, int k) { + TileEntity tileentity = world.getTileEntity(i, j, k); + + return tileentity != null && tileentity instanceof TileEntityFlowerPot ? (TileEntityFlowerPot) tileentity : null; + } + + public TileEntity a(World world, int i) { + Object object = null; + byte b0 = 0; + + switch (i) { + case 1: + object = Blocks.RED_ROSE; + b0 = 0; + break; + + case 2: + object = Blocks.YELLOW_FLOWER; + break; + + case 3: + object = Blocks.SAPLING; + b0 = 0; + break; + + case 4: + object = Blocks.SAPLING; + b0 = 1; + break; + + case 5: + object = Blocks.SAPLING; + b0 = 2; + break; + + case 6: + object = Blocks.SAPLING; + b0 = 3; + break; + + case 7: + object = Blocks.RED_MUSHROOM; + break; + + case 8: + object = Blocks.BROWN_MUSHROOM; + break; + + case 9: + object = Blocks.CACTUS; + break; + + case 10: + object = Blocks.DEAD_BUSH; + break; + + case 11: + object = Blocks.LONG_GRASS; + b0 = 2; + break; + + case 12: + object = Blocks.SAPLING; + b0 = 4; + break; + + case 13: + object = Blocks.SAPLING; + b0 = 5; + } + + return new TileEntityFlowerPot(Item.getItemOf((Block) object), b0); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockFlowing.java b/vspigot-server/src/main/java/net/minecraft/server/BlockFlowing.java new file mode 100644 index 0000000..41bdebd --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockFlowing.java @@ -0,0 +1,373 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.block.BlockFace; +import org.bukkit.event.block.BlockFromToEvent; +// CraftBukkit end + +public class BlockFlowing extends BlockFluids { + + int a; + boolean[] b = new boolean[4]; + int[] M = new int[4]; + + protected BlockFlowing(Material material) { + super(material); + } + + private void n(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + + world.setTypeAndData(i, j, k, Block.getById(Block.getId(this) + 1), l, 2); + } + + public void a(World world, int i, int j, int k, Random random) { + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + org.bukkit.Server server = world.getServer(); + org.bukkit.block.Block source = bworld == null ? null : bworld.getBlockAt(i, j, k); + // CraftBukkit end + + int l = this.e(world, i, j, k); + byte b0 = 1; + + if (this.material == Material.LAVA && !world.worldProvider.f) { + b0 = 2; + } + + boolean flag = true; + int i1 = this.getFlowSpeed(world, i, j, k); // PaperSpigot + int j1; + + if (l > 0) { + byte b1 = -100; + + this.a = 0; + int k1 = this.a(world, i - 1, j, k, b1); + + k1 = this.a(world, i + 1, j, k, k1); + k1 = this.a(world, i, j, k - 1, k1); + k1 = this.a(world, i, j, k + 1, k1); + j1 = k1 + b0; + if (j1 >= 8 || k1 < 0) { + j1 = -1; + } + + if (this.e(world, i, j + 1, k) >= 0) { + int l1 = this.e(world, i, j + 1, k); + + if (l1 >= 8) { + j1 = l1; + } else { + j1 = l1 + 8; + } + } + + if (this.a >= 2 && this.material == Material.WATER) { + if (world.getType(i, j - 1, k).getMaterial().isBuildable()) { + j1 = 0; + } else if (world.getType(i, j - 1, k).getMaterial() == this.material && world.getData(i, j - 1, k) == 0) { + j1 = 0; + } + } + + if (!world.paperSpigotConfig.fastDrainLava && this.material == Material.LAVA && l < 8 && j1 < 8 && j1 > l && random.nextInt(4) != 0) { // PaperSpigot + i1 *= 4; + } + + if (j1 == l) { + if (flag) { + this.n(world, i, j, k); + } + } else { + l = j1; + if (j1 < 0 || canFastDrain(world, i, j, k)) { // PaperSpigot - Fast draining + world.setAir(i, j, k); + } else { + world.setData(i, j, k, j1, 2); + world.a(i, j, k, this, i1); + // PaperSpigot start - Optimize draining + world.e(i - 1, j, k, this); + world.e(i + 1, j, k, this); + world.e(i, j + 1, k, this); + world.e(i, j, k - 1, this); + world.e(i, j, k + 1, this); + world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, i, j, k); // Spigot + // PaperSpigot end + } + } + } else { + this.n(world, i, j, k); + } + + if (world.getType(i, j, k).getMaterial() != material) return; // PaperSpigot - Stop updating flowing block if material has changed + + if (this.q(world, i, j - 1, k)) { + // CraftBukkit start - Send "down" to the server + BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN); + if (server != null) { + server.getPluginManager().callEvent(event); + } + + if (!event.isCancelled()) { + if (this.material == Material.LAVA && world.getType(i, j - 1, k).getMaterial() == Material.WATER) { + world.setTypeUpdate(i, j - 1, k, Blocks.STONE); + this.fizz(world, i, j - 1, k); + return; + } + + if (l >= 8) { + this.flow(world, i, j - 1, k, l); + } else { + this.flow(world, i, j - 1, k, l + 8); + } + } + // CraftBukkit end + } else if (l >= 0 && (l == 0 || this.p(world, i, j - 1, k))) { + boolean[] aboolean = this.o(world, i, j, k); + + j1 = l + b0; + if (l >= 8) { + j1 = 1; + } + + if (j1 >= 8) { + return; + } + + // CraftBukkit start - All four cardinal directions. Do not change the order! + BlockFace[] faces = new BlockFace[] { BlockFace.WEST, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH }; + int index = 0; + + for (BlockFace currentFace : faces) { + if (aboolean[index]) { + BlockFromToEvent event = new BlockFromToEvent(source, currentFace); + + if (server != null) { + server.getPluginManager().callEvent(event); + } + + if (!event.isCancelled()) { + this.flow(world, i + currentFace.getModX(), j, k + currentFace.getModZ(), j1); + } + } + index++; + } + // CraftBukkit end + } + } + + private void flow(World world, int i, int j, int k, int l) { + if (this.q(world, i, j, k)) { + Block block = world.getType(i, j, k); + + if (this.material == Material.LAVA) { + this.fizz(world, i, j, k); + } else { + block.b(world, i, j, k, world.getData(i, j, k), 0); + } + + world.setTypeAndData(i, j, k, this, l, 3); + } + } + + private int c(World world, int i, int j, int k, int l, int i1) { + int j1 = 1000; + + for (int k1 = 0; k1 < 4; ++k1) { + if ((k1 != 0 || i1 != 1) && (k1 != 1 || i1 != 0) && (k1 != 2 || i1 != 3) && (k1 != 3 || i1 != 2)) { + int l1 = i; + int i2 = k; + + if (k1 == 0) { + l1 = i - 1; + } + + if (k1 == 1) { + ++l1; + } + + if (k1 == 2) { + i2 = k - 1; + } + + if (k1 == 3) { + ++i2; + } + + if (!this.p(world, l1, j, i2) && (world.getType(l1, j, i2).getMaterial() != this.material || world.getData(l1, j, i2) != 0)) { + if (!this.p(world, l1, j - 1, i2)) { + return l; + } + + if (l < 4) { + int j2 = this.c(world, l1, j, i2, l + 1, k1); + + if (j2 < j1) { + j1 = j2; + } + } + } + } + } + + return j1; + } + + private boolean[] o(World world, int i, int j, int k) { + int l; + int i1; + + for (l = 0; l < 4; ++l) { + this.M[l] = 1000; + i1 = i; + int j1 = k; + + if (l == 0) { + i1 = i - 1; + } + + if (l == 1) { + ++i1; + } + + if (l == 2) { + j1 = k - 1; + } + + if (l == 3) { + ++j1; + } + + if (!this.p(world, i1, j, j1) && (world.getType(i1, j, j1).getMaterial() != this.material || world.getData(i1, j, j1) != 0)) { + if (this.p(world, i1, j - 1, j1)) { + this.M[l] = this.c(world, i1, j, j1, 1, l); + } else { + this.M[l] = 0; + } + } + } + + l = this.M[0]; + + for (i1 = 1; i1 < 4; ++i1) { + if (this.M[i1] < l) { + l = this.M[i1]; + } + } + + for (i1 = 0; i1 < 4; ++i1) { + this.b[i1] = this.M[i1] == l; + } + + return this.b; + } + + private boolean p(World world, int i, int j, int k) { + Block block = world.getType(i, j, k); + + return block != Blocks.WOODEN_DOOR && block != Blocks.IRON_DOOR_BLOCK && block != Blocks.SIGN_POST && block != Blocks.LADDER && block != Blocks.SUGAR_CANE_BLOCK ? (block.material == Material.PORTAL ? true : block.material.isSolid()) : true; + } + + protected int a(World world, int i, int j, int k, int l) { + int i1 = this.e(world, i, j, k); + + if (i1 < 0) { + return l; + } else { + if (i1 == 0) { + ++this.a; + } + + if (i1 >= 8) { + i1 = 0; + } + + return l >= 0 && i1 >= l ? l : i1; + } + } + + private boolean q(World world, int i, int j, int k) { + Material material = world.getType(i, j, k).getMaterial(); + + return material == this.material ? false : (material == Material.LAVA ? false : !this.p(world, i, j, k)); + } + + public void onPlace(World world, int i, int j, int k) { + super.onPlace(world, i, j, k); + if (world.getType(i, j, k) == this) { + world.a(i, j, k, this, this.getFlowSpeed(world, i, j, k)); // PaperSpigot + } + } + + public boolean L() { + return true; + } + + /** + * PaperSpigot - Get flow speed. Throttle if its water and flowing adjacent to lava + */ + public int getFlowSpeed(World world, int x, int y, int z) { + if (this.getMaterial() == Material.LAVA) { + return world.worldProvider.g ? world.paperSpigotConfig.lavaFlowSpeedNether : world.paperSpigotConfig.lavaFlowSpeedNormal; + } + if (this.getMaterial() == Material.WATER && ( + world.getType(x, y, z - 1).getMaterial() == Material.LAVA || + world.getType(x, y, z + 1).getMaterial() == Material.LAVA || + world.getType(x - 1, y, z).getMaterial() == Material.LAVA || + world.getType(x + 1, y, z).getMaterial() == Material.LAVA)) { + return world.paperSpigotConfig.waterOverLavaFlowSpeed; + } + return super.a(world); + } + + /** + * PaperSpigot - Data check method for fast draining + */ + public int getData(World world, int x, int y, int z) { + int data = this.e(world, x, y, z); + return data < 8 ? data : 0; + } + + /** + * PaperSpigot - Checks surrounding blocks to determine if block can be fast drained + */ + public boolean canFastDrain(World world, int x, int y, int z) { + boolean result = false; + int data = getData(world, x, y, z); + if (this.material == Material.WATER) { + if (world.paperSpigotConfig.fastDrainWater) { + result = true; + if (getData(world, x, y - 1, z) < 0) { + result = false; + } else if (world.getType(x, y, z - 1).getMaterial() == Material.WATER && getData(world, x, y, z - 1) < data) { + result = false; + } else if (world.getType(x, y, z + 1).getMaterial() == Material.WATER && getData(world, x, y, z + 1) < data) { + result = false; + } else if (world.getType(x - 1, y, z).getMaterial() == Material.WATER && getData(world, x - 1, y, z) < data) { + result = false; + } else if (world.getType(x + 1, y, z).getMaterial() == Material.WATER && getData(world, x + 1, y, z) < data) { + result = false; + } + } + } else if (this.material == Material.LAVA) { + if (world.paperSpigotConfig.fastDrainLava) { + result = true; + if (getData(world, x, y - 1, z) < 0 || world.getType(x, y + 1, z).getMaterial() != Material.AIR) { + result = false; + } else if (world.getType(x, y, z - 1).getMaterial() == Material.LAVA && getData(world, x, y, z - 1) < data) { + result = false; + } else if (world.getType(x, y, z + 1).getMaterial() == Material.LAVA && getData(world, x, y, z + 1) < data) { + result = false; + } else if (world.getType(x - 1, y, z).getMaterial() == Material.LAVA && getData(world, x - 1, y, z) < data) { + result = false; + } else if (world.getType(x + 1, y, z).getMaterial() == Material.LAVA && getData(world, x + 1, y, z) < data) { + result = false; + } + } + } + return result; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockGrass.java b/vspigot-server/src/main/java/net/minecraft/server/BlockGrass.java new file mode 100644 index 0000000..a2bd505 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockGrass.java @@ -0,0 +1,125 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.block.BlockFadeEvent; +// CraftBukkit end + +public class BlockGrass extends Block implements IBlockFragilePlantElement { + + private static final Logger a = LogManager.getLogger(); + + protected BlockGrass() { + super(Material.GRASS); + this.a(true); + this.a(CreativeModeTab.b); + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + // Poweruser start + int lightLevel = world.getLightLevel(i, j + 1, k); + if (lightLevel < 4 && world.getType(i, j + 1, k).k() > 2) { + // Poweruser end + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + BlockState blockState = bworld.getBlockAt(i, j, k).getState(); + blockState.setType(CraftMagicNumbers.getMaterial(Blocks.DIRT)); + + BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } else if (lightLevel >= 9) { // Poweruser + int numGrowth = Math.min(4, Math.max(20, (int) (4 * 100F / world.growthOdds))); // Spigot + for (int l = 0; l < numGrowth; ++l) { // Spigot + int i1 = i + random.nextInt(3) - 1; + int j1 = j + random.nextInt(5) - 3; + int k1 = k + random.nextInt(3) - 1; + Block block = world.getType(i1, j1 + 1, k1); + + if (world.getType(i1, j1, k1) == Blocks.DIRT && world.getData(i1, j1, k1) == 0 && world.getLightLevel(i1, j1 + 1, k1) >= 4 && block.k() <= 2) { + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState(); + blockState.setType(CraftMagicNumbers.getMaterial(Blocks.GRASS)); + + BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(i, j, k), blockState); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } + } + } + } + } + + public Item getDropType(int i, Random random, int j) { + return Blocks.DIRT.getDropType(0, random, j); + } + + public boolean a(World world, int i, int j, int k, boolean flag) { + return true; + } + + public boolean a(World world, Random random, int i, int j, int k) { + return true; + } + + public void b(World world, Random random, int i, int j, int k) { + int l = 0; + + while (l < 128) { + int i1 = i; + int j1 = j + 1; + int k1 = k; + int l1 = 0; + + while (true) { + if (l1 < l / 16) { + i1 += random.nextInt(3) - 1; + j1 += (random.nextInt(3) - 1) * random.nextInt(3) / 2; + k1 += random.nextInt(3) - 1; + if (world.getType(i1, j1 - 1, k1) == Blocks.GRASS && !world.getType(i1, j1, k1).r()) { + ++l1; + continue; + } + } else if (world.getType(i1, j1, k1).material == Material.AIR) { + if (random.nextInt(8) != 0) { + if (Blocks.LONG_GRASS.j(world, i1, j1, k1)) { + CraftEventFactory.handleBlockGrowEvent(world, i1, j1, k1, Blocks.LONG_GRASS, 1); // CraftBukkit + } + } else { + String s = world.getBiome(i1, k1).a(random, i1, j1, k1); + + a.debug("Flower in " + world.getBiome(i1, k1).af + ": " + s); + BlockFlowers blockflowers = BlockFlowers.e(s); + + if (blockflowers != null && blockflowers.j(world, i1, j1, k1)) { + int i2 = BlockFlowers.f(s); + + CraftEventFactory.handleBlockGrowEvent(world, i1, j1, k1, blockflowers, i2); // CraftBukkit + } + } + } + + ++l; + break; + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockHopper.java b/vspigot-server/src/main/java/net/minecraft/server/BlockHopper.java new file mode 100644 index 0000000..b33ed64 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockHopper.java @@ -0,0 +1,190 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +public class BlockHopper extends BlockContainer { + + private final Random a = new Random(); + + public BlockHopper() { + super(Material.ORE); + this.a(CreativeModeTab.d); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + public void a(World world, int i, int j, int k, AxisAlignedBB axisalignedbb, List list, Entity entity) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + float f = 0.125F; + + this.a(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + int j1 = Facing.OPPOSITE_FACING[l]; + + if (j1 == 1) { + j1 = 0; + } + + return j1; + } + + public TileEntity a(World world, int i) { + return new TileEntityHopper(); + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + super.postPlace(world, i, j, k, entityliving, itemstack); + if (itemstack.hasName()) { + TileEntityHopper tileentityhopper = e((IBlockAccess) world, i, j, k); + + tileentityhopper.a(itemstack.getName()); + } + } + + public void onPlace(World world, int i, int j, int k) { + super.onPlace(world, i, j, k); + this.e(world, i, j, k); + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (world.isStatic) { + return true; + } else { + TileEntityHopper tileentityhopper = e((IBlockAccess) world, i, j, k); + + if (tileentityhopper != null) { + entityhuman.openHopper(tileentityhopper); + } + + return true; + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + this.e(world, i, j, k); + } + + private void e(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + int i1 = b(l); + boolean flag = !world.isBlockIndirectlyPowered(i, j, k); + boolean flag1 = c(l); + + if (flag != flag1) { + world.setData(i, j, k, i1 | (flag ? 0 : 8), 4); + // Spigot start - When this hopper becomes unpowered, make it active. + // Called when this block's power level changes. flag1 is the current + // isNotPowered from metadata. flag is the recalculated isNotPowered. + if (world.spigotConfig.altHopperTicking) { + // e returns the TileEntityHopper associated with this BlockHopper. + TileEntityHopper hopper = e((IBlockAccess) world, i, j, k); + if (flag && hopper != null) { + hopper.makeTick(); + } + } + // Spigot end + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + TileEntityHopper tileentityhopper = (TileEntityHopper) world.getTileEntity(i, j, k); + + if (tileentityhopper != null) { + for (int i1 = 0; i1 < tileentityhopper.getSize(); ++i1) { + ItemStack itemstack = tileentityhopper.getItem(i1); + + if (itemstack != null) { + float f = this.a.nextFloat() * 0.8F + 0.1F; + float f1 = this.a.nextFloat() * 0.8F + 0.1F; + float f2 = this.a.nextFloat() * 0.8F + 0.1F; + + while (itemstack.count > 0) { + int j1 = this.a.nextInt(21) + 10; + + if (j1 > itemstack.count) { + j1 = itemstack.count; + } + + itemstack.count -= j1; + EntityItem entityitem = new EntityItem(world, (double) ((float) i + f), (double) ((float) j + f1), (double) ((float) k + f2), new ItemStack(itemstack.getItem(), j1, itemstack.getData())); + + if (itemstack.hasTag()) { + entityitem.getItemStack().setTag((NBTTagCompound) itemstack.getTag().clone()); + } + + float f3 = 0.05F; + + entityitem.motX = (double) ((float) this.a.nextGaussian() * f3); + entityitem.motY = (double) ((float) this.a.nextGaussian() * f3 + 0.2F); + entityitem.motZ = (double) ((float) this.a.nextGaussian() * f3); + world.addEntity(entityitem); + } + } + } + + world.updateAdjacentComparators(i, j, k, block); + } + + super.remove(world, i, j, k, block, l); + } + + public int b() { + return 38; + } + + public boolean d() { + return false; + } + + public boolean c() { + return false; + } + + public static int b(int i) { + return Math.min(i & 7, 5); // CraftBukkit - Fix AIOOBE in callers + } + + public static boolean c(int i) { + return (i & 8) != 8; + } + + public boolean isComplexRedstone() { + return true; + } + + public int g(World world, int i, int j, int k, int l) { + return Container.b((IInventory) e((IBlockAccess) world, i, j, k)); + } + + public static TileEntityHopper e(IBlockAccess iblockaccess, int i, int j, int k) { + return (TileEntityHopper) iblockaccess.getTileEntity(i, j, k); + } + + // Spigot start - Use random block updates to make hoppers active. + @Override + public void a(World world, int i, int j, int k, Random random) { + if (world.spigotConfig.altHopperTicking) { + // e returns the TileEntityHopper associated with this BlockHopper. + TileEntityHopper hopper = e((IBlockAccess) world, i, j, k); + if (hopper != null) { + hopper.makeTick(); + } + } + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockIce.java b/vspigot-server/src/main/java/net/minecraft/server/BlockIce.java new file mode 100644 index 0000000..25cb327 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockIce.java @@ -0,0 +1,65 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockIce extends BlockHalfTransparent { + + public BlockIce() { + super("ice", Material.ICE, false); + this.frictionFactor = 0.98F; + this.a(true); + this.a(CreativeModeTab.b); + } + + public void a(World world, EntityHuman entityhuman, int i, int j, int k, int l) { + entityhuman.a(StatisticList.MINE_BLOCK_COUNT[Block.getId(this)], 1); + entityhuman.applyExhaustion(0.025F); + if (this.E() && EnchantmentManager.hasSilkTouchEnchantment(entityhuman)) { + ItemStack itemstack = this.j(l); + + if (itemstack != null) { + this.a(world, i, j, k, itemstack); + } + } else { + if (world.worldProvider.f) { + world.setAir(i, j, k); + return; + } + + int i1 = EnchantmentManager.getBonusBlockLootEnchantmentLevel(entityhuman); + + this.b(world, i, j, k, l, i1); + Material material = world.getType(i, j - 1, k).getMaterial(); + + if (material.isSolid() || material.isLiquid()) { + world.setTypeUpdate(i, j, k, Blocks.WATER); + } + } + } + + public int a(Random random) { + return 0; + } + + public void a(World world, int i, int j, int k, Random random) { + if (world.b(EnumSkyBlock.BLOCK, i, j, k) > 11 - this.k()) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(i, j, k), world.worldProvider.f ? Blocks.AIR : Blocks.STATIONARY_WATER).isCancelled()) { + return; + } + // CraftBukkit end + + if (world.worldProvider.f) { + world.setAir(i, j, k); + return; + } + + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setTypeUpdate(i, j, k, Blocks.STATIONARY_WATER); + } + } + + public int h() { + return 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockLeaves.java b/vspigot-server/src/main/java/net/minecraft/server/BlockLeaves.java new file mode 100644 index 0000000..d6afe5b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockLeaves.java @@ -0,0 +1,203 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.LeavesDecayEvent; // CraftBukkit + +public abstract class BlockLeaves extends BlockTransparent { + + int[] a; + protected IIcon[][] M = new IIcon[2][]; + + public BlockLeaves() { + super(Material.LEAVES, false); + this.a(true); + this.a(CreativeModeTab.c); + this.c(0.2F); + this.g(1); + this.a(h); + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + byte b0 = 1; + int i1 = b0 + 1; + + if (world.b(i - i1, j - i1, k - i1, i + i1, j + i1, k + i1)) { + for (int j1 = -b0; j1 <= b0; ++j1) { + for (int k1 = -b0; k1 <= b0; ++k1) { + for (int l1 = -b0; l1 <= b0; ++l1) { + if (world.getType(i + j1, j + k1, k + l1).getMaterial() == Material.LEAVES) { + int i2 = world.getData(i + j1, j + k1, k + l1); + + world.setData(i + j1, j + k1, k + l1, i2 | 8, 4); + } + } + } + } + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + int l = world.getData(i, j, k); + + if ((l & 8) != 0 && (l & 4) == 0) { + byte b0 = 4; + int i1 = b0 + 1; + byte b1 = 32; + int j1 = b1 * b1; + int k1 = b1 / 2; + + if (this.a == null) { + this.a = new int[b1 * b1 * b1]; + } + + int l1; + + if (world.b(i - i1, j - i1, k - i1, i + i1, j + i1, k + i1)) { + int i2; + int j2; + + for (l1 = -b0; l1 <= b0; ++l1) { + for (i2 = -b0; i2 <= b0; ++i2) { + for (j2 = -b0; j2 <= b0; ++j2) { + Block block = world.getType(i + l1, j + i2, k + j2); + + if (block != Blocks.LOG && block != Blocks.LOG2) { + if (block.getMaterial() == Material.LEAVES) { + this.a[(l1 + k1) * j1 + (i2 + k1) * b1 + j2 + k1] = -2; + } else { + this.a[(l1 + k1) * j1 + (i2 + k1) * b1 + j2 + k1] = -1; + } + } else { + this.a[(l1 + k1) * j1 + (i2 + k1) * b1 + j2 + k1] = 0; + } + } + } + } + + for (l1 = 1; l1 <= 4; ++l1) { + for (i2 = -b0; i2 <= b0; ++i2) { + for (j2 = -b0; j2 <= b0; ++j2) { + for (int k2 = -b0; k2 <= b0; ++k2) { + if (this.a[(i2 + k1) * j1 + (j2 + k1) * b1 + k2 + k1] == l1 - 1) { + if (this.a[(i2 + k1 - 1) * j1 + (j2 + k1) * b1 + k2 + k1] == -2) { + this.a[(i2 + k1 - 1) * j1 + (j2 + k1) * b1 + k2 + k1] = l1; + } + + if (this.a[(i2 + k1 + 1) * j1 + (j2 + k1) * b1 + k2 + k1] == -2) { + this.a[(i2 + k1 + 1) * j1 + (j2 + k1) * b1 + k2 + k1] = l1; + } + + if (this.a[(i2 + k1) * j1 + (j2 + k1 - 1) * b1 + k2 + k1] == -2) { + this.a[(i2 + k1) * j1 + (j2 + k1 - 1) * b1 + k2 + k1] = l1; + } + + if (this.a[(i2 + k1) * j1 + (j2 + k1 + 1) * b1 + k2 + k1] == -2) { + this.a[(i2 + k1) * j1 + (j2 + k1 + 1) * b1 + k2 + k1] = l1; + } + + if (this.a[(i2 + k1) * j1 + (j2 + k1) * b1 + (k2 + k1 - 1)] == -2) { + this.a[(i2 + k1) * j1 + (j2 + k1) * b1 + (k2 + k1 - 1)] = l1; + } + + if (this.a[(i2 + k1) * j1 + (j2 + k1) * b1 + k2 + k1 + 1] == -2) { + this.a[(i2 + k1) * j1 + (j2 + k1) * b1 + k2 + k1 + 1] = l1; + } + } + } + } + } + } + } + + l1 = this.a[k1 * j1 + k1 * b1 + k1]; + if (l1 >= 0) { + world.setData(i, j, k, l & -9, 4); + } else { + this.e(world, i, j, k); + } + } + } + } + + private void e(World world, int i, int j, int k) { + // CraftBukkit start + LeavesDecayEvent event = new LeavesDecayEvent(world.getWorld().getBlockAt(i, j, k)); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + + public int a(Random random) { + return random.nextInt(20) == 0 ? 1 : 0; + } + + public Item getDropType(int i, Random random, int j) { + return Item.getItemOf(Blocks.SAPLING); + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (!world.isStatic) { + int j1 = this.b(l); + + if (i1 > 0) { + j1 -= 2 << i1; + if (j1 < 10) { + j1 = 10; + } + } + + if (world.random.nextInt(j1) == 0) { + Item item = this.getDropType(l, world.random, i1); + + this.a(world, i, j, k, new ItemStack(item, 1, this.getDropData(l))); + } + + j1 = 200; + if (i1 > 0) { + j1 -= 10 << i1; + if (j1 < 40) { + j1 = 40; + } + } + + this.c(world, i, j, k, l, j1); + } + } + + protected void c(World world, int i, int j, int k, int l, int i1) {} + + protected int b(int i) { + return 20; + } + + public void a(World world, EntityHuman entityhuman, int i, int j, int k, int l) { + if (!world.isStatic && entityhuman.bF() != null && entityhuman.bF().getItem() == Items.SHEARS) { + entityhuman.a(StatisticList.MINE_BLOCK_COUNT[Block.getId(this)], 1); + this.a(world, i, j, k, new ItemStack(Item.getItemOf(this), 1, l & 3)); + } else { + super.a(world, entityhuman, i, j, k, l); + } + } + + public int getDropData(int i) { + return i & 3; + } + + public boolean c() { + return !this.P; + } + + protected ItemStack j(int i) { + return new ItemStack(Item.getItemOf(this), 1, i & 3); + } + + public abstract String[] e(); +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockLever.java b/vspigot-server/src/main/java/net/minecraft/server/BlockLever.java new file mode 100644 index 0000000..5885d5f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockLever.java @@ -0,0 +1,284 @@ +package net.minecraft.server; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit +import org.spigotmc.SpigotConfig; + +public class BlockLever extends Block { + + protected BlockLever() { + super(Material.ORIENTABLE); + this.a(CreativeModeTab.d); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 12; + } + + public boolean canPlace(World world, int i, int j, int k, int l) { + return l == 0 && world.getType(i, j + 1, k).r() ? true : (l == 1 && World.a((IBlockAccess) world, i, j - 1, k) ? true : (l == 2 && world.getType(i, j, k + 1).r() ? true : (l == 3 && world.getType(i, j, k - 1).r() ? true : (l == 4 && world.getType(i + 1, j, k).r() ? true : l == 5 && world.getType(i - 1, j, k).r())))); + } + + public boolean canPlace(World world, int i, int j, int k) { + return world.getType(i - 1, j, k).r() ? true : (world.getType(i + 1, j, k).r() ? true : (world.getType(i, j, k - 1).r() ? true : (world.getType(i, j, k + 1).r() ? true : (World.a((IBlockAccess) world, i, j - 1, k) ? true : world.getType(i, j + 1, k).r())))); + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + int j1 = i1 & 8; + int k1 = i1 & 7; + byte b0 = -1; + + if (l == 0 && world.getType(i, j + 1, k).r()) { + b0 = 0; + } + + if (l == 1 && World.a((IBlockAccess) world, i, j - 1, k)) { + b0 = 5; + } + + if (l == 2 && world.getType(i, j, k + 1).r()) { + b0 = 4; + } + + if (l == 3 && world.getType(i, j, k - 1).r()) { + b0 = 3; + } + + if (l == 4 && world.getType(i + 1, j, k).r()) { + b0 = 2; + } + + if (l == 5 && world.getType(i - 1, j, k).r()) { + b0 = 1; + } + + return b0 + j1; + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = world.getData(i, j, k); + int i1 = l & 7; + int j1 = l & 8; + + if (i1 == b(1)) { + if ((MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 1) == 0) { + world.setData(i, j, k, 5 | j1, 2); + } else { + world.setData(i, j, k, 6 | j1, 2); + } + } else if (i1 == b(0)) { + if ((MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 1) == 0) { + world.setData(i, j, k, 7 | j1, 2); + } else { + world.setData(i, j, k, 0 | j1, 2); + } + } + } + + public static int b(int i) { + switch (i) { + case 0: + return 0; + + case 1: + return 5; + + case 2: + return 4; + + case 3: + return 3; + + case 4: + return 2; + + case 5: + return 1; + + default: + return -1; + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (this.e(world, i, j, k)) { + int l = world.getData(i, j, k) & 7; + boolean flag = false; + + if (!world.getType(i - 1, j, k).r() && l == 1) { + flag = true; + } + + if (!world.getType(i + 1, j, k).r() && l == 2) { + flag = true; + } + + if (!world.getType(i, j, k - 1).r() && l == 3) { + flag = true; + } + + if (!world.getType(i, j, k + 1).r() && l == 4) { + flag = true; + } + + if (!World.a((IBlockAccess) world, i, j - 1, k) && l == 5) { + flag = true; + } + + if (!World.a((IBlockAccess) world, i, j - 1, k) && l == 6) { + flag = true; + } + + if (!world.getType(i, j + 1, k).r() && l == 0) { + flag = true; + } + + if (!world.getType(i, j + 1, k).r() && l == 7) { + flag = true; + } + + if (flag) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + } + } + + private boolean e(World world, int i, int j, int k) { + if (!this.canPlace(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + return false; + } else { + return true; + } + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + if (SpigotConfig.pearlThroughGatesAndTripwire) { + this.a(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F); + return; + } + + int l = iblockaccess.getData(i, j, k) & 7; + float f = 0.1875F; + + if (l == 1) { + this.a(0.0F, 0.2F, 0.5F - f, f * 2.0F, 0.8F, 0.5F + f); + } else if (l == 2) { + this.a(1.0F - f * 2.0F, 0.2F, 0.5F - f, 1.0F, 0.8F, 0.5F + f); + } else if (l == 3) { + this.a(0.5F - f, 0.2F, 0.0F, 0.5F + f, 0.8F, f * 2.0F); + } else if (l == 4) { + this.a(0.5F - f, 0.2F, 1.0F - f * 2.0F, 0.5F + f, 0.8F, 1.0F); + } else if (l != 5 && l != 6) { + if (l == 0 || l == 7) { + f = 0.25F; + this.a(0.5F - f, 0.4F, 0.5F - f, 0.5F + f, 1.0F, 0.5F + f); + } + } else { + f = 0.25F; + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.6F, 0.5F + f); + } + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (world.isStatic) { + return true; + } else { + int i1 = world.getData(i, j, k); + int j1 = i1 & 7; + int k1 = 8 - (i1 & 8); + + // CraftBukkit start - Interact Lever + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + int old = (k1 != 8) ? 15 : 0; + int current = (k1 == 8) ? 15 : 0; + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if ((eventRedstone.getNewCurrent() > 0) != (k1 == 8)) { + return true; + } + // CraftBukkit end + + world.setData(i, j, k, j1 + k1, 3); + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "random.click", 0.3F, k1 > 0 ? 0.6F : 0.5F); + world.applyPhysics(i, j, k, this); + if (j1 == 1) { + world.applyPhysics(i - 1, j, k, this); + } else if (j1 == 2) { + world.applyPhysics(i + 1, j, k, this); + } else if (j1 == 3) { + world.applyPhysics(i, j, k - 1, this); + } else if (j1 == 4) { + world.applyPhysics(i, j, k + 1, this); + } else if (j1 != 5 && j1 != 6) { + if (j1 == 0 || j1 == 7) { + world.applyPhysics(i, j + 1, k, this); + } + } else { + world.applyPhysics(i, j - 1, k, this); + } + + return true; + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + if ((l & 8) > 0) { + world.applyPhysics(i, j, k, this); + int i1 = l & 7; + + if (i1 == 1) { + world.applyPhysics(i - 1, j, k, this); + } else if (i1 == 2) { + world.applyPhysics(i + 1, j, k, this); + } else if (i1 == 3) { + world.applyPhysics(i, j, k - 1, this); + } else if (i1 == 4) { + world.applyPhysics(i, j, k + 1, this); + } else if (i1 != 5 && i1 != 6) { + if (i1 == 0 || i1 == 7) { + world.applyPhysics(i, j + 1, k, this); + } + } else { + world.applyPhysics(i, j - 1, k, this); + } + } + + super.remove(world, i, j, k, block, l); + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return (iblockaccess.getData(i, j, k) & 8) > 0 ? 15 : 0; + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + int i1 = iblockaccess.getData(i, j, k); + + if ((i1 & 8) == 0) { + return 0; + } else { + int j1 = i1 & 7; + + return j1 == 0 && l == 0 ? 15 : (j1 == 7 && l == 0 ? 15 : (j1 == 6 && l == 1 ? 15 : (j1 == 5 && l == 1 ? 15 : (j1 == 4 && l == 2 ? 15 : (j1 == 3 && l == 3 ? 15 : (j1 == 2 && l == 4 ? 15 : (j1 == 1 && l == 5 ? 15 : 0))))))); + } + } + + public boolean isPowerSource() { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockMinecartDetector.java b/vspigot-server/src/main/java/net/minecraft/server/BlockMinecartDetector.java new file mode 100644 index 0000000..207da24 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockMinecartDetector.java @@ -0,0 +1,120 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockMinecartDetector extends BlockMinecartTrackAbstract { + + public BlockMinecartDetector() { + super(true); + this.a(true); + } + + public int a(World world) { + return 20; + } + + public boolean isPowerSource() { + return true; + } + + public void a(World world, int i, int j, int k, Entity entity) { + if (!world.isStatic) { + int l = world.getData(i, j, k); + + if ((l & 8) == 0) { + this.a(world, i, j, k, l); + } + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + int l = world.getData(i, j, k); + + if ((l & 8) != 0) { + this.a(world, i, j, k, l); + } + } + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return (iblockaccess.getData(i, j, k) & 8) != 0 ? 15 : 0; + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return (iblockaccess.getData(i, j, k) & 8) == 0 ? 0 : (l == 1 ? 15 : 0); + } + + private void a(World world, int i, int j, int k, int l) { + boolean flag = (l & 8) != 0; + boolean flag1 = false; + float f = 0.125F; + List list = world.a(EntityMinecartAbstract.class, AxisAlignedBB.a((double) ((float) i + f), (double) j, (double) ((float) k + f), (double) ((float) (i + 1) - f), (double) ((float) (j + 1) - f), (double) ((float) (k + 1) - f))); + + if (!list.isEmpty()) { + flag1 = true; + } + + // CraftBukkit start + if (flag != flag1) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, flag ? 15 : 0, flag1 ? 15 : 0); + world.getServer().getPluginManager().callEvent(eventRedstone); + + flag1 = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end + + if (flag1 && !flag) { + world.setData(i, j, k, l | 8, 3); + world.applyPhysics(i, j, k, this); + world.applyPhysics(i, j - 1, k, this); + world.c(i, j, k, i, j, k); + } + + if (!flag1 && flag) { + world.setData(i, j, k, l & 7, 3); + world.applyPhysics(i, j, k, this); + world.applyPhysics(i, j - 1, k, this); + world.c(i, j, k, i, j, k); + } + + if (flag1) { + world.a(i, j, k, this, this.a(world)); + } + + world.updateAdjacentComparators(i, j, k, this); + } + + public void onPlace(World world, int i, int j, int k) { + super.onPlace(world, i, j, k); + this.a(world, i, j, k, world.getData(i, j, k)); + } + + public boolean isComplexRedstone() { + return true; + } + + public int g(World world, int i, int j, int k, int l) { + if ((world.getData(i, j, k) & 8) > 0) { + float f = 0.125F; + List list = world.a(EntityMinecartCommandBlock.class, AxisAlignedBB.a((double) ((float) i + f), (double) j, (double) ((float) k + f), (double) ((float) (i + 1) - f), (double) ((float) (j + 1) - f), (double) ((float) (k + 1) - f))); + + if (list.size() > 0) { + return ((EntityMinecartCommandBlock) list.get(0)).getCommandBlock().g(); + } + + List list1 = world.a(EntityMinecartAbstract.class, AxisAlignedBB.a((double) ((float) i + f), (double) j, (double) ((float) k + f), (double) ((float) (i + 1) - f), (double) ((float) (j + 1) - f), (double) ((float) (k + 1) - f)), IEntitySelector.c); + + if (list1.size() > 0) { + return Container.b((IInventory) list1.get(0)); + } + } + + return 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockMinecartTrackAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/BlockMinecartTrackAbstract.java new file mode 100644 index 0000000..b6b4146 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockMinecartTrackAbstract.java @@ -0,0 +1,149 @@ +package net.minecraft.server; + +import java.util.Random; + +public abstract class BlockMinecartTrackAbstract extends Block { + + protected final boolean a; + + public static final boolean b_(World world, int i, int j, int k) { + return a(world.getType(i, j, k)); + } + + public static final boolean a(Block block) { + return block == Blocks.RAILS || block == Blocks.GOLDEN_RAIL || block == Blocks.DETECTOR_RAIL || block == Blocks.ACTIVATOR_RAIL; + } + + protected BlockMinecartTrackAbstract(boolean flag) { + super(Material.ORIENTABLE); + this.a = flag; + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); + this.a(CreativeModeTab.e); + } + + public boolean e() { + return this.a; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean c() { + return false; + } + + public MovingObjectPosition a(World world, int i, int j, int k, Vec3D vec3d, Vec3D vec3d1) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k, vec3d, vec3d1); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + + if (l >= 2 && l <= 5) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F); + } else { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); + } + } + + public boolean d() { + return false; + } + + public int b() { + return 9; + } + + public int a(Random random) { + return 1; + } + + public boolean canPlace(World world, int i, int j, int k) { + return World.a((IBlockAccess) world, i, j - 1, k); + } + + public void onPlace(World world, int i, int j, int k) { + if (!world.isStatic) { + this.a(world, i, j, k, true); + if (this.a) { + this.doPhysics(world, i, j, k, this); + } + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!world.isStatic) { + int l = world.getData(i, j, k); + int i1 = l; + + if (this.a) { + i1 = l & 7; + } + + boolean flag = false; + + if (!World.a((IBlockAccess) world, i, j - 1, k)) { + flag = true; + } + + if (i1 == 2 && !World.a((IBlockAccess) world, i + 1, j, k)) { + flag = true; + } + + if (i1 == 3 && !World.a((IBlockAccess) world, i - 1, j, k)) { + flag = true; + } + + if (i1 == 4 && !World.a((IBlockAccess) world, i, j, k - 1)) { + flag = true; + } + + if (i1 == 5 && !World.a((IBlockAccess) world, i, j, k + 1)) { + flag = true; + } + + if (flag) { + // PaperSpigot start - Rails dupe workaround + if (world.getType(i, j, k).getMaterial() != Material.AIR) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + // PaperSpigot end + } else { + this.a(world, i, j, k, l, i1, block); + } + } + } + + protected void a(World world, int i, int j, int k, int l, int i1, Block block) {} + + protected void a(World world, int i, int j, int k, boolean flag) { + if (!world.isStatic) { + (new MinecartTrackLogic(this, world, i, j, k)).a(world.isBlockIndirectlyPowered(i, j, k), flag); + } + } + + public int h() { + return 0; + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + int i1 = l; + + if (this.a) { + i1 = l & 7; + } + + super.remove(world, i, j, k, block, l); + if (i1 == 2 || i1 == 3 || i1 == 4 || i1 == 5) { + world.applyPhysics(i, j + 1, k, block); + } + + if (this.a) { + world.applyPhysics(i, j, k, block); + world.applyPhysics(i, j - 1, k, block); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockMobSpawner.java b/vspigot-server/src/main/java/net/minecraft/server/BlockMobSpawner.java new file mode 100644 index 0000000..c32db8f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockMobSpawner.java @@ -0,0 +1,41 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockMobSpawner extends BlockContainer { + + protected BlockMobSpawner() { + super(Material.STONE); + } + + public TileEntity a(World world, int i) { + return new TileEntityMobSpawner(); + } + + public Item getDropType(int i, Random random, int j) { + return null; + } + + public int a(Random random) { + return 0; + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + super.dropNaturally(world, i, j, k, l, f, i1); + /* CraftBukkit start - Delegate to getExpDrop + int j1 = 15 + world.random.nextInt(15) + world.random.nextInt(15); + + this.dropExperience(world, i, j, k, j1)*/ + } + + public int getExpDrop(World world, int data, int enchantmentLevel) { + int j1 = 15 + world.random.nextInt(15) + world.random.nextInt(15); + + return j1; + // CraftBukkit end + } + + public boolean c() { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockMonsterEggs.java b/vspigot-server/src/main/java/net/minecraft/server/BlockMonsterEggs.java new file mode 100644 index 0000000..d9f8741 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockMonsterEggs.java @@ -0,0 +1,121 @@ +package net.minecraft.server; + +import java.util.Random; + +import net.minecraft.util.org.apache.commons.lang3.tuple.ImmutablePair; + +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; // CraftBukkit + +public class BlockMonsterEggs extends Block { + + public static final String[] a = new String[] { "stone", "cobble", "brick", "mossybrick", "crackedbrick", "chiseledbrick"}; + + public BlockMonsterEggs() { + super(Material.CLAY); + this.c(0.0F); + this.a(CreativeModeTab.c); + } + + public void postBreak(World world, int i, int j, int k, int l) { + if (!world.isStatic) { + EntitySilverfish entitysilverfish = new EntitySilverfish(world); + + entitysilverfish.setPositionRotation((double) i + 0.5D, (double) j, (double) k + 0.5D, 0.0F, 0.0F); + world.addEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason + entitysilverfish.s(); + } + + super.postBreak(world, i, j, k, l); + } + + public int a(Random random) { + return 0; + } + + public static boolean a(Block block) { + return block == Blocks.STONE || block == Blocks.COBBLESTONE || block == Blocks.SMOOTH_BRICK; + } + + public static int a(Block block, int i) { + if (i == 0) { + if (block == Blocks.COBBLESTONE) { + return 1; + } + + if (block == Blocks.SMOOTH_BRICK) { + return 2; + } + } else if (block == Blocks.SMOOTH_BRICK) { + switch (i) { + case 1: + return 3; + + case 2: + return 4; + + case 3: + return 5; + } + } + + return 0; + } + + public static ImmutablePair b(int i) { + switch (i) { + case 1: + return new ImmutablePair(Blocks.COBBLESTONE, Integer.valueOf(0)); + + case 2: + return new ImmutablePair(Blocks.SMOOTH_BRICK, Integer.valueOf(0)); + + case 3: + return new ImmutablePair(Blocks.SMOOTH_BRICK, Integer.valueOf(1)); + + case 4: + return new ImmutablePair(Blocks.SMOOTH_BRICK, Integer.valueOf(2)); + + case 5: + return new ImmutablePair(Blocks.SMOOTH_BRICK, Integer.valueOf(3)); + + default: + return new ImmutablePair(Blocks.STONE, Integer.valueOf(0)); + } + } + + protected ItemStack j(int i) { + switch (i) { + case 1: + return new ItemStack(Blocks.COBBLESTONE); + + case 2: + return new ItemStack(Blocks.SMOOTH_BRICK); + + case 3: + return new ItemStack(Blocks.SMOOTH_BRICK, 1, 1); + + case 4: + return new ItemStack(Blocks.SMOOTH_BRICK, 1, 2); + + case 5: + return new ItemStack(Blocks.SMOOTH_BRICK, 1, 3); + + default: + return new ItemStack(Blocks.STONE); + } + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (!world.isStatic) { + EntitySilverfish entitysilverfish = new EntitySilverfish(world); + + entitysilverfish.setPositionRotation((double) i + 0.5D, (double) j, (double) k + 0.5D, 0.0F, 0.0F); + world.addEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason + entitysilverfish.s(); + } + } + + public int getDropData(World world, int i, int j, int k) { + return world.getData(i, j, k); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockMushroom.java b/vspigot-server/src/main/java/net/minecraft/server/BlockMushroom.java new file mode 100644 index 0000000..4daa273 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockMushroom.java @@ -0,0 +1,126 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.TreeType; +import org.bukkit.block.BlockState; +import org.bukkit.event.block.BlockSpreadEvent; +// CraftBukkit end + +public class BlockMushroom extends BlockPlant implements IBlockFragilePlantElement { + + protected BlockMushroom() { + float f = 0.2F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, f * 2.0F, 0.5F + f); + this.a(true); + } + + public void a(World world, int i, int j, int k, Random random) { + final int sourceX = i, sourceY = j, sourceZ = k; // CraftBukkit + if (random.nextInt(Math.max(1, (int) world.growthOdds / world.spigotConfig.mushroomModifier * 25)) == 0) { // Spigot + byte b0 = 4; + int l = 5; + + int i1; + int j1; + int k1; + + for (i1 = i - b0; i1 <= i + b0; ++i1) { + for (j1 = k - b0; j1 <= k + b0; ++j1) { + for (k1 = j - 1; k1 <= j + 1; ++k1) { + if (world.getType(i1, k1, j1) == this) { + --l; + if (l <= 0) { + return; + } + } + } + } + } + + i1 = i + random.nextInt(3) - 1; + j1 = j + random.nextInt(2) - random.nextInt(2); + k1 = k + random.nextInt(3) - 1; + + for (int l1 = 0; l1 < 4; ++l1) { + if (world.isEmpty(i1, j1, k1) && this.j(world, i1, j1, k1)) { + i = i1; + j = j1; + k = k1; + } + + i1 = i + random.nextInt(3) - 1; + j1 = j + random.nextInt(2) - random.nextInt(2); + k1 = k + random.nextInt(3) - 1; + } + + if (world.isEmpty(i1, j1, k1) && this.j(world, i1, j1, k1)) { + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState(); + blockState.setType(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(this)); // nms: this.id, 0, 2 + + BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(sourceX, sourceY, sourceZ), blockState); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } + } + } + + public boolean canPlace(World world, int i, int j, int k) { + return super.canPlace(world, i, j, k) && this.j(world, i, j, k); + } + + protected boolean a(Block block) { + return block.j(); + } + + public boolean j(World world, int i, int j, int k) { + if (j >= 0 && j < 256) { + Block block = world.getType(i, j - 1, k); + + return block == Blocks.MYCEL || block == Blocks.DIRT && world.getData(i, j - 1, k) == 2 || world.j(i, j, k) < 13 && this.a(block); + } else { + return false; + } + } + + public boolean grow(World world, int i, int j, int k, Random random) { + int l = world.getData(i, j, k); + world.setAir(i, j, k); + WorldGenHugeMushroom worldgenhugemushroom = null; + + if (this == Blocks.BROWN_MUSHROOM) { + BlockSapling.treeType = TreeType.BROWN_MUSHROOM; // CraftBukkit + worldgenhugemushroom = new WorldGenHugeMushroom(0); + } else if (this == Blocks.RED_MUSHROOM) { + BlockSapling.treeType = TreeType.RED_MUSHROOM; // CraftBukkit + worldgenhugemushroom = new WorldGenHugeMushroom(1); + } + + if (worldgenhugemushroom != null && worldgenhugemushroom.generate(world, random, i, j, k)) { + return true; + } else { + world.setTypeAndData(i, j, k, this, l, 3); + return false; + } + } + + public boolean a(World world, int i, int j, int k, boolean flag) { + return true; + } + + public boolean a(World world, Random random, int i, int j, int k) { + return (double) random.nextFloat() < 0.4D; + } + + public void b(World world, Random random, int i, int j, int k) { + this.grow(world, i, j, k, random); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockMycel.java b/vspigot-server/src/main/java/net/minecraft/server/BlockMycel.java new file mode 100644 index 0000000..833dad2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockMycel.java @@ -0,0 +1,68 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.event.block.BlockFadeEvent; +import org.bukkit.event.block.BlockSpreadEvent; +// CraftBukkit end + +public class BlockMycel extends Block { + + protected BlockMycel() { + super(Material.GRASS); + this.a(true); + this.a(CreativeModeTab.b); + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + // Poweruser start + int lightLevel = world.getLightLevel(i, j + 1, k); + if (lightLevel < 4 && world.getType(i, j + 1, k).k() > 2) { + // Poweruser end + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + BlockState blockState = bworld.getBlockAt(i, j, k).getState(); + blockState.setType(CraftMagicNumbers.getMaterial(Blocks.DIRT)); + + BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } else if (lightLevel >= 9) { // Poweruser + int numGrowth = Math.min(4, Math.max(20, (int) (4 * 100F / world.growthOdds))); // Spigot + for (int l = 0; l < numGrowth; ++l) { // Spigot + int i1 = i + random.nextInt(3) - 1; + int j1 = j + random.nextInt(5) - 3; + int k1 = k + random.nextInt(3) - 1; + Block block = world.getType(i1, j1 + 1, k1); + + if (world.getType(i1, j1, k1) == Blocks.DIRT && world.getData(i1, j1, k1) == 0 && world.getLightLevel(i1, j1 + 1, k1) >= 4 && block.k() <= 2) { + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState(); + blockState.setType(CraftMagicNumbers.getMaterial(this)); + + BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(i, j, k), blockState); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } + } + } + } + } + + public Item getDropType(int i, Random random, int j) { + return Blocks.DIRT.getDropType(0, random, j); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockNetherWart.java b/vspigot-server/src/main/java/net/minecraft/server/BlockNetherWart.java new file mode 100644 index 0000000..ea12b91 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockNetherWart.java @@ -0,0 +1,62 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockNetherWart extends BlockPlant { + + protected BlockNetherWart() { + this.a(true); + float f = 0.5F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.25F, 0.5F + f); + this.a((CreativeModeTab) null); + } + + protected boolean a(Block block) { + return block == Blocks.SOUL_SAND; + } + + public boolean j(World world, int i, int j, int k) { + return this.a(world.getType(i, j - 1, k)); + } + + public void a(World world, int i, int j, int k, Random random) { + int l = world.getData(i, j, k); + + if (l < 3 && random.nextInt(10) == 0) { + ++l; + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, i, j, k, this, l); // CraftBukkit + } + + super.a(world, i, j, k, random); + } + + public int b() { + return 6; + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (!world.isStatic) { + int j1 = 1; + + if (l >= 3) { + j1 = 2 + world.random.nextInt(3); + if (i1 > 0) { + j1 += world.random.nextInt(i1 + 1); + } + } + + for (int k1 = 0; k1 < j1; ++k1) { + this.a(world, i, j, k, new ItemStack(Items.NETHER_STALK)); + } + } + } + + public Item getDropType(int i, Random random, int j) { + return null; + } + + public int a(Random random) { + return 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockOre.java b/vspigot-server/src/main/java/net/minecraft/server/BlockOre.java new file mode 100644 index 0000000..68214c1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockOre.java @@ -0,0 +1,83 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockOre extends Block { + + public BlockOre() { + super(Material.STONE); + this.a(CreativeModeTab.b); + } + + public Item getDropType(int i, Random random, int j) { + return this == Blocks.COAL_ORE ? Items.COAL : (this == Blocks.DIAMOND_ORE ? Items.DIAMOND : (this == Blocks.LAPIS_ORE ? Items.INK_SACK : (this == Blocks.EMERALD_ORE ? Items.EMERALD : (this == Blocks.QUARTZ_ORE ? Items.QUARTZ : Item.getItemOf(this))))); + } + + public int a(Random random) { + return this == Blocks.LAPIS_ORE ? 4 + random.nextInt(5) : 1; + } + + public int getDropCount(int i, Random random) { + if (i > 0 && Item.getItemOf(this) != this.getDropType(0, random, i)) { + int j = random.nextInt(i + 2) - 1; + + if (j < 0) { + j = 0; + } + + return this.a(random) * (j + 1); + } else { + return this.a(random); + } + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + super.dropNaturally(world, i, j, k, l, f, i1); + /* CraftBukkit start - Delegated to getExpDrop + if (this.getDropType(l, world.random, i1) != Item.getItemOf(this)) { + int j1 = 0; + + if (this == Blocks.COAL_ORE) { + j1 = MathHelper.nextInt(world.random, 0, 2); + } else if (this == Blocks.DIAMOND_ORE) { + j1 = MathHelper.nextInt(world.random, 3, 7); + } else if (this == Blocks.EMERALD_ORE) { + j1 = MathHelper.nextInt(world.random, 3, 7); + } else if (this == Blocks.LAPIS_ORE) { + j1 = MathHelper.nextInt(world.random, 2, 5); + } else if (this == Blocks.QUARTZ_ORE) { + j1 = MathHelper.nextInt(world.random, 2, 5); + } + + this.dropExperience(world, i, j, k, j1); + } + // */ + } + + public int getExpDrop(World world, int l, int i1) { + if (this.getDropType(l, world.random, i1) != Item.getItemOf(this)) { + int j1 = 0; + + if (this == Blocks.COAL_ORE) { + j1 = MathHelper.nextInt(world.random, 0, 2 * (i1 + 1)); // Kohi - boosted + } else if (this == Blocks.DIAMOND_ORE) { + j1 = MathHelper.nextInt(world.random, 3, 7 * (i1 + 1)); // Kohi - boosted + } else if (this == Blocks.EMERALD_ORE) { + j1 = MathHelper.nextInt(world.random, 3, 7 * (i1 + 1)); // Kohi - boosted + } else if (this == Blocks.LAPIS_ORE) { + j1 = MathHelper.nextInt(world.random, 2, 5 * (i1 + 1)); // Kohi - boosted + } else if (this == Blocks.QUARTZ_ORE) { + j1 = MathHelper.nextInt(world.random, 2, 5 * (i1 + 1)); // Kohi - boosted + } + + return j1; + } + + return 0; + // CraftBukkit end + } + + public int getDropData(int i) { + return this == Blocks.LAPIS_ORE ? 4 : 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPiston.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPiston.java new file mode 100644 index 0000000..8d8963c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPiston.java @@ -0,0 +1,400 @@ +package net.minecraft.server; + +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +// CraftBukkit end + +public class BlockPiston extends Block { + + private final boolean a; + + public BlockPiston(boolean flag) { + super(Material.PISTON); + this.a = flag; + this.a(i); + this.c(0.5F); + this.a(CreativeModeTab.d); + } + + public int b() { + return 16; + } + + public boolean c() { + return false; + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + return false; + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = a(world, i, j, k, entityliving); + + world.setData(i, j, k, l, 2); + if (!world.isStatic) { + this.e(world, i, j, k); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!world.isStatic) { + this.e(world, i, j, k); + } + } + + public void onPlace(World world, int i, int j, int k) { + if (!world.isStatic && world.getTileEntity(i, j, k) == null) { + this.e(world, i, j, k); + } + } + + private void e(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + int i1 = b(l); + + if (i1 != 7) { + boolean flag = this.a(world, i, j, k, i1); + + if (flag && !c(l)) { + // CraftBukkit start + int length = h(world, i, j, k, i1); + if (length >= 0) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + BlockPistonExtendEvent event = new BlockPistonExtendEvent(block, length, CraftBlock.notchToBlockFace(i1)); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + + world.playBlockAction(i, j, k, this, 0, i1); + } + } else if (!flag && c(l)) { + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, CraftBlock.notchToBlockFace(i1)); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + + world.setData(i, j, k, i1, 2); + world.playBlockAction(i, j, k, this, 1, i1); + } + } + } + + private boolean a(World world, int i, int j, int k, int l) { + return l != 0 && world.isBlockFacePowered(i, j - 1, k, 0) ? true : (l != 1 && world.isBlockFacePowered(i, j + 1, k, 1) ? true : (l != 2 && world.isBlockFacePowered(i, j, k - 1, 2) ? true : (l != 3 && world.isBlockFacePowered(i, j, k + 1, 3) ? true : (l != 5 && world.isBlockFacePowered(i + 1, j, k, 5) ? true : (l != 4 && world.isBlockFacePowered(i - 1, j, k, 4) ? true : (world.isBlockFacePowered(i, j, k, 0) ? true : (world.isBlockFacePowered(i, j + 2, k, 1) ? true : (world.isBlockFacePowered(i, j + 1, k - 1, 2) ? true : (world.isBlockFacePowered(i, j + 1, k + 1, 3) ? true : (world.isBlockFacePowered(i - 1, j + 1, k, 4) ? true : world.isBlockFacePowered(i + 1, j + 1, k, 5))))))))))); + } + + public boolean a(World world, int i, int j, int k, int l, int i1) { + if (!world.isStatic) { + boolean flag = this.a(world, i, j, k, i1); + + if (flag && l == 1) { + world.setData(i, j, k, i1 | 8, 2); + return false; + } + + if (!flag && l == 0) { + return false; + } + } + + if (l == 0) { + if (!this.i(world, i, j, k, i1)) { + return false; + } + + world.setData(i, j, k, i1 | 8, 2); + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "tile.piston.out", 0.5F, world.random.nextFloat() * 0.25F + 0.6F); + } else if (l == 1) { + TileEntity tileentity = world.getTileEntity(i + Facing.b[i1], j + Facing.c[i1], k + Facing.d[i1]); + + if (tileentity instanceof TileEntityPiston) { + ((TileEntityPiston) tileentity).f(); + } + + world.setTypeAndData(i, j, k, Blocks.PISTON_MOVING, i1, 3); + world.setTileEntity(i, j, k, BlockPistonMoving.a(this, i1, i1, false, true)); + if (this.a) { + int j1 = i + Facing.b[i1] * 2; + int k1 = j + Facing.c[i1] * 2; + int l1 = k + Facing.d[i1] * 2; + Block block = world.getType(j1, k1, l1); + int i2 = world.getData(j1, k1, l1); + boolean flag1 = false; + + if (block == Blocks.PISTON_MOVING) { + TileEntity tileentity1 = world.getTileEntity(j1, k1, l1); + + if (tileentity1 instanceof TileEntityPiston) { + TileEntityPiston tileentitypiston = (TileEntityPiston) tileentity1; + + if (tileentitypiston.c() == i1 && tileentitypiston.b()) { + tileentitypiston.f(); + block = tileentitypiston.a(); + i2 = tileentitypiston.p(); + flag1 = true; + } + } + } + + if (!flag1 && block.getMaterial() != Material.AIR && a(block, world, j1, k1, l1, false) && (block.h() == 0 || block == Blocks.PISTON || block == Blocks.PISTON_STICKY)) { + i += Facing.b[i1]; + j += Facing.c[i1]; + k += Facing.d[i1]; + world.setTypeAndData(i, j, k, Blocks.PISTON_MOVING, i2, 3); + world.setTileEntity(i, j, k, BlockPistonMoving.a(block, i2, i1, false, false)); + world.setAir(j1, k1, l1); + } else if (!flag1) { + world.setAir(i + Facing.b[i1], j + Facing.c[i1], k + Facing.d[i1]); + } + } else { + world.setAir(i + Facing.b[i1], j + Facing.c[i1], k + Facing.d[i1]); + } + + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "tile.piston.in", 0.5F, world.random.nextFloat() * 0.15F + 0.6F); + } + + return true; + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + + if (c(l)) { + float f = 0.25F; + + switch (b(l)) { + case 0: + this.a(0.0F, 0.25F, 0.0F, 1.0F, 1.0F, 1.0F); + break; + + case 1: + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.75F, 1.0F); + break; + + case 2: + this.a(0.0F, 0.0F, 0.25F, 1.0F, 1.0F, 1.0F); + break; + + case 3: + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.75F); + break; + + case 4: + this.a(0.25F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + break; + + case 5: + this.a(0.0F, 0.0F, 0.0F, 0.75F, 1.0F, 1.0F); + } + } else { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + } + + public void g() { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + public void a(World world, int i, int j, int k, AxisAlignedBB axisalignedbb, List list, Entity entity) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k); + } + + public boolean d() { + return false; + } + + public static int b(int i) { + if ((i & 7) >= Facing.OPPOSITE_FACING.length) return 7; // CraftBukkit - check for AIOOB on piston data + return i & 7; + } + + public static boolean c(int i) { + return (i & 8) != 0; + } + + public static int a(World world, int i, int j, int k, EntityLiving entityliving) { + if (MathHelper.abs((float) entityliving.locX - (float) i) < 2.0F && MathHelper.abs((float) entityliving.locZ - (float) k) < 2.0F) { + double d0 = entityliving.locY + 1.82D - (double) entityliving.height; + + if (d0 - (double) j > 2.0D) { + return 1; + } + + if ((double) j - d0 > 0.0D) { + return 0; + } + } + + int l = MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3; + + return l == 0 ? 2 : (l == 1 ? 5 : (l == 2 ? 3 : (l == 3 ? 4 : 0))); + } + + private static boolean a(Block block, World world, int i, int j, int k, boolean flag) { + if (block == Blocks.OBSIDIAN) { + return false; + } else { + if (block != Blocks.PISTON && block != Blocks.PISTON_STICKY) { + if (block.f(world, i, j, k) == -1.0F) { + return false; + } + + if (block.h() == 2) { + return false; + } + + if (block.h() == 1) { + if (!flag) { + return false; + } + + return true; + } + } else if (c(world.getData(i, j, k))) { + return false; + } + + return !(block instanceof IContainer); + } + } + + // CraftBukkit - boolean -> int return + private static int h(World world, int i, int j, int k, int l) { + int i1 = i + Facing.b[l]; + int j1 = j + Facing.c[l]; + int k1 = k + Facing.d[l]; + int l1 = 0; + + while (true) { + if (l1 < 13) { + if (j1 <= 0 || j1 >= 255) { + return -1; // CraftBukkit + } + + Block block = world.getType(i1, j1, k1); + + if (block.getMaterial() != Material.AIR) { + if (!a(block, world, i1, j1, k1, true)) { + return -1; // CraftBukkit + } + + if (block.h() != 1) { + if (l1 == 12) { + return -1; // CraftBukkit + } + + i1 += Facing.b[l]; + j1 += Facing.c[l]; + k1 += Facing.d[l]; + ++l1; + continue; + } + } + } + + return l1; // CraftBukkit + } + } + + private boolean i(World world, int i, int j, int k, int l) { + int i1 = i + Facing.b[l]; + int j1 = j + Facing.c[l]; + int k1 = k + Facing.d[l]; + int l1 = 0; + + while (true) { + if (l1 < 13) { + if (j1 <= 0 || j1 >= 255) { + return false; + } + + Block block = world.getType(i1, j1, k1); + + if (block.getMaterial() != Material.AIR) { + if (!a(block, world, i1, j1, k1, true)) { + return false; + } + + if (block.h() != 1) { + if (l1 == 12) { + return false; + } + + i1 += Facing.b[l]; + j1 += Facing.c[l]; + k1 += Facing.d[l]; + ++l1; + continue; + } + + block.b(world, i1, j1, k1, world.getData(i1, j1, k1), 0); + world.setAir(i1, j1, k1); + } + } + + l1 = i1; + int i2 = j1; + int j2 = k1; + int k2 = 0; + + Block[] ablock; + int l2; + int i3; + int j3; + + for (ablock = new Block[13]; i1 != i || j1 != j || k1 != k; k1 = j3) { + l2 = i1 - Facing.b[l]; + i3 = j1 - Facing.c[l]; + j3 = k1 - Facing.d[l]; + Block block1 = world.getType(l2, i3, j3); + int k3 = world.getData(l2, i3, j3); + + if (block1 == this && l2 == i && i3 == j && j3 == k) { + world.setTypeAndData(i1, j1, k1, Blocks.PISTON_MOVING, l | (this.a ? 8 : 0), 4); + world.setTileEntity(i1, j1, k1, BlockPistonMoving.a(Blocks.PISTON_EXTENSION, l | (this.a ? 8 : 0), l, true, false)); + } else { + world.setTypeAndData(i1, j1, k1, Blocks.PISTON_MOVING, k3, 4); + world.setTileEntity(i1, j1, k1, BlockPistonMoving.a(block1, k3, l, true, false)); + } + + ablock[k2++] = block1; + i1 = l2; + j1 = i3; + } + + i1 = l1; + j1 = i2; + k1 = j2; + + for (k2 = 0; i1 != i || j1 != j || k1 != k; k1 = j3) { + l2 = i1 - Facing.b[l]; + i3 = j1 - Facing.c[l]; + j3 = k1 - Facing.d[l]; + world.applyPhysics(l2, i3, j3, ablock[k2++]); + i1 = l2; + j1 = i3; + } + + return true; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPistonExtension.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPistonExtension.java new file mode 100644 index 0000000..cb7455d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPistonExtension.java @@ -0,0 +1,169 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +public class BlockPistonExtension extends Block { + + public BlockPistonExtension() { + super(Material.PISTON); + this.a(i); + this.c(0.5F); + } + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild) { + int i1 = b(l); + Block block = world.getType(i - Facing.b[i1], j - Facing.c[i1], k - Facing.d[i1]); + + if (block == Blocks.PISTON || block == Blocks.PISTON_STICKY) { + world.setAir(i - Facing.b[i1], j - Facing.c[i1], k - Facing.d[i1]); + } + } + + super.a(world, i, j, k, l, entityhuman); + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + super.remove(world, i, j, k, block, l); + if ((l & 7) >= Facing.OPPOSITE_FACING.length) return; // CraftBukkit - fix a piston AIOOBE issue + int i1 = Facing.OPPOSITE_FACING[b(l)]; + + i += Facing.b[i1]; + j += Facing.c[i1]; + k += Facing.d[i1]; + Block block1 = world.getType(i, j, k); + + if (block1 == Blocks.PISTON || block1 == Blocks.PISTON_STICKY) { + l = world.getData(i, j, k); + if (BlockPiston.c(l)) { + block1.b(world, i, j, k, l, 0); + world.setAir(i, j, k); + } + } + } + + public int b() { + return 17; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public boolean canPlace(World world, int i, int j, int k) { + return false; + } + + public boolean canPlace(World world, int i, int j, int k, int l) { + return false; + } + + public int a(Random random) { + return 0; + } + + public void a(World world, int i, int j, int k, AxisAlignedBB axisalignedbb, List list, Entity entity) { + int l = world.getData(i, j, k); + float f = 0.25F; + float f1 = 0.375F; + float f2 = 0.625F; + float f3 = 0.25F; + float f4 = 0.75F; + + switch (b(l)) { + case 0: + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.25F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.375F, 0.25F, 0.375F, 0.625F, 1.0F, 0.625F); + super.a(world, i, j, k, axisalignedbb, list, entity); + break; + + case 1: + this.a(0.0F, 0.75F, 0.0F, 1.0F, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.375F, 0.0F, 0.375F, 0.625F, 0.75F, 0.625F); + super.a(world, i, j, k, axisalignedbb, list, entity); + break; + + case 2: + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.25F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.25F, 0.375F, 0.25F, 0.75F, 0.625F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + break; + + case 3: + this.a(0.0F, 0.0F, 0.75F, 1.0F, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.25F, 0.375F, 0.0F, 0.75F, 0.625F, 0.75F); + super.a(world, i, j, k, axisalignedbb, list, entity); + break; + + case 4: + this.a(0.0F, 0.0F, 0.0F, 0.25F, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.375F, 0.25F, 0.25F, 0.625F, 0.75F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + break; + + case 5: + this.a(0.75F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + super.a(world, i, j, k, axisalignedbb, list, entity); + this.a(0.0F, 0.375F, 0.25F, 0.75F, 0.625F, 0.75F); + super.a(world, i, j, k, axisalignedbb, list, entity); + } + + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + float f = 0.25F; + + switch (b(l)) { + case 0: + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.25F, 1.0F); + break; + + case 1: + this.a(0.0F, 0.75F, 0.0F, 1.0F, 1.0F, 1.0F); + break; + + case 2: + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.25F); + break; + + case 3: + this.a(0.0F, 0.0F, 0.75F, 1.0F, 1.0F, 1.0F); + break; + + case 4: + this.a(0.0F, 0.0F, 0.0F, 0.25F, 1.0F, 1.0F); + break; + + case 5: + this.a(0.75F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + int l = b(world.getData(i, j, k)); + if ((l & 7) >= Facing.OPPOSITE_FACING.length) return; // CraftBukkit - fix a piston AIOOBE issue + Block block1 = world.getType(i - Facing.b[l], j - Facing.c[l], k - Facing.d[l]); + + if (block1 != Blocks.PISTON && block1 != Blocks.PISTON_STICKY) { + world.setAir(i, j, k); + } else { + block1.doPhysics(world, i - Facing.b[l], j - Facing.c[l], k - Facing.d[l], block); + } + } + + public static int b(int i) { + return MathHelper.a(i & 7, 0, Facing.b.length - 1); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPortal.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPortal.java new file mode 100644 index 0000000..222e3ac --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPortal.java @@ -0,0 +1,122 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit + +public class BlockPortal extends BlockHalfTransparent { + + public static final int[][] a = new int[][] { new int[0], { 3, 1}, { 2, 0}}; + + public BlockPortal() { + super("portal", Material.PORTAL, false); + this.a(true); + } + + public void a(World world, int i, int j, int k, Random random) { + super.a(world, i, j, k, random); + if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.worldProvider.d() && world.getGameRules().getBoolean("doMobSpawning") && random.nextInt(2000) < world.difficulty.a()) { // Spigot + int l; + + for (l = j; !World.a((IBlockAccess) world, i, l, k) && l > 0; --l) { + ; + } + + if (l > 0 && !world.getType(i, l + 1, k).r()) { + // CraftBukkit - set spawn reason to NETHER_PORTAL + Entity entity = ItemMonsterEgg.spawnCreature(world, 57, (double) i + 0.5D, (double) l + 1.1D, (double) k + 0.5D, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NETHER_PORTAL); + + if (entity != null) { + entity.portalCooldown = entity.ai(); + } + } + } + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = b(iblockaccess.getData(i, j, k)); + + if (l == 0) { + if (iblockaccess.getType(i - 1, j, k) != this && iblockaccess.getType(i + 1, j, k) != this) { + l = 2; + } else { + l = 1; + } + + if (iblockaccess instanceof World && !((World) iblockaccess).isStatic) { + ((World) iblockaccess).setData(i, j, k, l, 2); + } + } + + float f = 0.125F; + float f1 = 0.125F; + + if (l == 1) { + f = 0.5F; + } + + if (l == 2) { + f1 = 0.5F; + } + + this.a(0.5F - f, 0.0F, 0.5F - f1, 0.5F + f, 1.0F, 0.5F + f1); + } + + public boolean d() { + return false; + } + + public boolean e(World world, int i, int j, int k) { + PortalCreator portalcreator = new PortalCreator(world, i, j, k, 1); + PortalCreator portalcreator1 = new PortalCreator(world, i, j, k, 2); + + if (portalcreator.b() && PortalCreator.a(portalcreator) == 0) { + // CraftBukkit start - return portalcreator + return portalcreator.c(); + // return true; + } else if (portalcreator1.b() && PortalCreator.a(portalcreator1) == 0) { + return portalcreator1.c(); + // return true; + // CraftBukkit end + } else { + return false; + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + int l = b(world.getData(i, j, k)); + PortalCreator portalcreator = new PortalCreator(world, i, j, k, 1); + PortalCreator portalcreator1 = new PortalCreator(world, i, j, k, 2); + + if (l == 1 && (!portalcreator.b() || PortalCreator.a(portalcreator) < PortalCreator.b(portalcreator) * PortalCreator.c(portalcreator))) { + world.setTypeUpdate(i, j, k, Blocks.AIR); + } else if (l == 2 && (!portalcreator1.b() || PortalCreator.a(portalcreator1) < PortalCreator.b(portalcreator1) * PortalCreator.c(portalcreator1))) { + world.setTypeUpdate(i, j, k, Blocks.AIR); + } else if (l == 0 && !portalcreator.b() && !portalcreator1.b()) { + world.setTypeUpdate(i, j, k, Blocks.AIR); + } + } + + public int a(Random random) { + return 0; + } + + public void a(World world, int i, int j, int k, Entity entity) { + if (entity.vehicle == null && entity.passenger == null) { + // CraftBukkit start - Entity in portal + EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), i, j, k)); + world.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + + entity.ah(); + } + } + + public static int b(int i) { + return i & 3; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPoweredRail.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPoweredRail.java new file mode 100644 index 0000000..cd20905 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPoweredRail.java @@ -0,0 +1,146 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockPoweredRail extends BlockMinecartTrackAbstract { + protected BlockPoweredRail() { + super(true); + } + + protected boolean a(World world, int i, int j, int k, int l, boolean flag, int i1) { + if (i1 >= 8) { + return false; + } else { + int j1 = l & 0x7; + boolean flag1 = true; + + switch (j1) { + case 0: + if (flag) { + ++k; + } else { + --k; + } + break; + + case 1: + if (flag) { + --i; + } else { + ++i; + } + break; + + case 2: + if (flag) { + i--; + } else { + ++i; + ++j; + flag1 = false; + } + + j1 = 1; + break; + + case 3: + if (flag) { + --i; + ++j; + flag1 = false; + } else { + ++i; + } + + j1 = 1; + break; + + case 4: + if (flag) { + ++k; + } else { + --k; + ++j; + flag1 = false; + } + + j1 = 0; + break; + + case 5: + if (flag) { + ++k; + ++j; + flag1 = false; + } else { + --k; + } + + j1 = 0; + } + + return this.a(world, i, j, k, flag, i1, j1) ? true : flag1 && this.a(world, i, j - 1, k, flag, i1, j1); + } + } + + protected boolean a(World world, int i, int j, int k, boolean flag, int l, int i1) { + Block block = world.getType(i, j, k); + + if (block == this) { + int j1 = world.getData(i, j, k); + int k1 = j1 & 0x7; + + if (i1 == 1 && (k1 == 0 || k1 == 4 || k1 == 5)) { + return false; + } + + if (i1 == 0 && (k1 == 1 || k1 == 2 || k1 == 3)) { + return false; + } + + if ((j1 & 0x8) != 0) { + if (world.isBlockIndirectlyPowered(i, j, k)) { + return true; + } + + return this.a(world, i, j, k, j1, flag, l + 1); + } + } + + return false; + } + + protected void a(World world, int i, int j, int k, int l, int i1, Block block) { + boolean flag = world.isBlockIndirectlyPowered(i, j, k); + + flag = flag || this.a(world, i, j, k, l, true, 0) || this.a(world, i, j, k, l, false, 0); + boolean flag1 = false; + + if (flag && (l & 0x8) == 0) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, i, j, k, 0, 15).getNewCurrent() <= 0) { + return; + } + // CraftBukkit end + + world.setData(i, j, k, i1 | 0x8, 3); + flag1 = true; + } else if (!flag && (l & 0x8) != 0) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, i, j, k, 15, 0).getNewCurrent() > 0) { + return; + } + // CraftBukkit end + + world.setData(i, j, k, i1, 3); + flag1 = true; + } + + if (flag1) { + world.applyPhysics(i, j - 1, k, this); + if (i1 == 2 || i1 == 3 || i1 == 4 || i1 == 5) { + world.applyPhysics(i, j + 1, k, this); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java new file mode 100644 index 0000000..c2dfc86 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java @@ -0,0 +1,174 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public abstract class BlockPressurePlateAbstract extends Block { + + private String a; + + protected BlockPressurePlateAbstract(String s, Material material) { + super(material); + this.a = s; + this.a(CreativeModeTab.d); + this.a(true); + this.b(this.d(15)); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.b(iblockaccess.getData(i, j, k)); + } + + protected void b(int i) { + boolean flag = this.c(i) > 0; + float f = 0.0625F; + + if (flag) { + this.a(f, 0.0F, f, 1.0F - f, 0.03125F, 1.0F - f); + } else { + this.a(f, 0.0F, f, 1.0F - f, 0.0625F, 1.0F - f); + } + } + + public int a(World world) { + return 20; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public boolean b(IBlockAccess iblockaccess, int i, int j, int k) { + return true; + } + + public boolean canPlace(World world, int i, int j, int k) { + return World.a((IBlockAccess) world, i, j - 1, k) || BlockFence.a(world.getType(i, j - 1, k)); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + boolean flag = false; + + if (!World.a((IBlockAccess) world, i, j - 1, k) && !BlockFence.a(world.getType(i, j - 1, k))) { + flag = true; + } + + if (flag) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + int l = this.c(world.getData(i, j, k)); + + if (l > 0) { + this.a(world, i, j, k, l); + } + } + } + + public void a(World world, int i, int j, int k, Entity entity) { + if (!world.isStatic) { + int l = this.c(world.getData(i, j, k)); + + if (l == 0) { + this.a(world, i, j, k, l); + } + } + } + + protected void a(World world, int i, int j, int k, int l) { + int i1 = this.e(world, i, j, k); + boolean flag = l > 0; + boolean flag1 = i1 > 0; + + // CraftBukkit start - Interact Pressure Plate + org.bukkit.World bworld = world.getWorld(); + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + + if (flag != flag1) { + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bworld.getBlockAt(i, j, k), l, i1); + manager.callEvent(eventRedstone); + + flag1 = eventRedstone.getNewCurrent() > 0; + i1 = eventRedstone.getNewCurrent(); + } + // CraftBukkit end + + if (l != i1) { + world.setData(i, j, k, this.d(i1), 2); + this.a_(world, i, j, k); + world.c(i, j, k, i, j, k); + } + + if (!flag1 && flag) { + world.makeSound((double) i + 0.5D, (double) j + 0.1D, (double) k + 0.5D, "random.click", 0.3F, 0.5F); + } else if (flag1 && !flag) { + world.makeSound((double) i + 0.5D, (double) j + 0.1D, (double) k + 0.5D, "random.click", 0.3F, 0.6F); + } + + if (flag1) { + world.a(i, j, k, this, this.a(world)); + } + } + + protected AxisAlignedBB a(int i, int j, int k) { + float f = 0.125F; + + return AxisAlignedBB.a((double) ((float) i + f), (double) j, (double) ((float) k + f), (double) ((float) (i + 1) - f), (double) j + 0.25D, (double) ((float) (k + 1) - f)); + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + if (this.c(l) > 0) { + this.a_(world, i, j, k); + } + + super.remove(world, i, j, k, block, l); + } + + protected void a_(World world, int i, int j, int k) { + world.applyPhysics(i, j, k, this); + world.applyPhysics(i, j - 1, k, this); + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return this.c(iblockaccess.getData(i, j, k)); + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return l == 1 ? this.c(iblockaccess.getData(i, j, k)) : 0; + } + + public boolean isPowerSource() { + return true; + } + + public void g() { + float f = 0.5F; + float f1 = 0.125F; + float f2 = 0.5F; + + this.a(0.5F - f, 0.5F - f1, 0.5F - f2, 0.5F + f, 0.5F + f1, 0.5F + f2); + } + + public int h() { + return 1; + } + + protected abstract int e(World world, int i, int j, int k); + + protected abstract int c(int i); + + protected abstract int d(int i); +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateBinary.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateBinary.java new file mode 100644 index 0000000..25ef883 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateBinary.java @@ -0,0 +1,74 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit + +public class BlockPressurePlateBinary extends BlockPressurePlateAbstract { + + private EnumMobType a; + + protected BlockPressurePlateBinary(String s, Material material, EnumMobType enummobtype) { + super(s, material); + this.a = enummobtype; + } + + protected int d(int i) { + return i > 0 ? 1 : 0; + } + + protected int c(int i) { + return i == 1 ? 15 : 0; + } + + protected int e(World world, int i, int j, int k) { + List list = null; + + if (this.a == EnumMobType.EVERYTHING) { + list = world.getEntities((Entity) null, this.a(i, j, k)); + } + + if (this.a == EnumMobType.MOBS) { + list = world.a(EntityLiving.class, this.a(i, j, k)); + } + + if (this.a == EnumMobType.PLAYERS) { + list = world.a(EntityHuman.class, this.a(i, j, k)); + } + + if (list != null && !list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + // CraftBukkit start - Call interact event when turning on a pressure plate + if (this.c(world.getData(i, j, k)) == 0) { + org.bukkit.World bworld = world.getWorld(); + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + org.bukkit.event.Cancellable cancellable; + + if (entity instanceof EntityHuman) { + cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, i, j, k, -1, null); + } else { + cancellable = new EntityInteractEvent(entity.getBukkitEntity(), bworld.getBlockAt(i, j, k)); + manager.callEvent((EntityInteractEvent) cancellable); + } + + // We only want to block turning the plate on if all events are cancelled + if (cancellable.isCancelled()) { + continue; + } + } + // CraftBukkit end + + if (!entity.az()) { + return 15; + } + } + } + + return 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java new file mode 100644 index 0000000..b6f22f0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit + +public class BlockPressurePlateWeighted extends BlockPressurePlateAbstract { + private final int a; + + protected BlockPressurePlateWeighted(String s, Material material, int i) { + super(s, material); + this.a = i; + } + + protected int e(World world, int i, int j, int k) { + // CraftBukkit start + int l = 0; + java.util.Iterator iterator = world.a(Entity.class, this.a(i, j, k)).iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + org.bukkit.event.Cancellable cancellable; + + if (entity instanceof EntityHuman) { + cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, i, j, k, -1, null); + } else { + cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(i, j, k)); + world.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); + } + + // We only want to block turning the plate on if all events are cancelled + if (!cancellable.isCancelled()) { + l++; + } + } + + l = Math.min(l, this.a); + // CraftBukkit end + + if (l <= 0) { + return 0; + } + + float f = (float) Math.min(this.a, l) / (float) this.a; + return MathHelper.f(f * 15.0F); + } + + protected int c(int i) { + return i; + } + + protected int d(int i) { + return i; + } + + public int a(World world) { + return 10; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockPumpkin.java b/vspigot-server/src/main/java/net/minecraft/server/BlockPumpkin.java new file mode 100644 index 0000000..a8632a7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockPumpkin.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.util.BlockStateListPopulator; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + +public class BlockPumpkin extends BlockDirectional { + + private boolean a; + + protected BlockPumpkin(boolean flag) { + super(Material.PUMPKIN); + this.a(true); + this.a = flag; + this.a(CreativeModeTab.b); + } + + public void onPlace(World world, int i, int j, int k) { + super.onPlace(world, i, j, k); + if (world.getType(i, j - 1, k) == Blocks.SNOW_BLOCK && world.getType(i, j - 2, k) == Blocks.SNOW_BLOCK) { + if (!world.isStatic) { + // CraftBukkit start - Use BlockStateListPopulator + BlockStateListPopulator blockList = new BlockStateListPopulator(world.getWorld()); + + blockList.setTypeId(i, j, k, 0); + blockList.setTypeId(i, j - 1, k, 0); + blockList.setTypeId(i, j - 2, k, 0); + EntitySnowman entitysnowman = new EntitySnowman(world); + + entitysnowman.setPositionRotation((double) i + 0.5D, (double) j - 1.95D, (double) k + 0.5D, 0.0F, 0.0F); + if (world.addEntity(entitysnowman, SpawnReason.BUILD_SNOWMAN)) { + blockList.updateList(); + } + // CraftBukkit end + } + + for (int l = 0; l < 120; ++l) { + world.addParticle("snowshovel", (double) i + world.random.nextDouble(), (double) (j - 2) + world.random.nextDouble() * 2.5D, (double) k + world.random.nextDouble(), 0.0D, 0.0D, 0.0D); + } + } else if (world.getType(i, j - 1, k) == Blocks.IRON_BLOCK && world.getType(i, j - 2, k) == Blocks.IRON_BLOCK) { + boolean flag = world.getType(i - 1, j - 1, k) == Blocks.IRON_BLOCK && world.getType(i + 1, j - 1, k) == Blocks.IRON_BLOCK; + boolean flag1 = world.getType(i, j - 1, k - 1) == Blocks.IRON_BLOCK && world.getType(i, j - 1, k + 1) == Blocks.IRON_BLOCK; + + if (flag || flag1) { + // CraftBukkit start - Use BlockStateListPopulator + BlockStateListPopulator blockList = new BlockStateListPopulator(world.getWorld()); + + blockList.setTypeId(i, j, k, 0); + blockList.setTypeId(i, j - 1, k, 0); + blockList.setTypeId(i, j - 2, k, 0); + if (flag) { + blockList.setTypeId(i - 1, j - 1, k, 0); + blockList.setTypeId(i + 1, j - 1, k, 0); + } else { + blockList.setTypeId(i, j - 1, k - 1, 0); + blockList.setTypeId(i, j - 1, k + 1, 0); + } + + EntityIronGolem entityirongolem = new EntityIronGolem(world); + + entityirongolem.setPlayerCreated(true); + entityirongolem.setPositionRotation((double) i + 0.5D, (double) j - 1.95D, (double) k + 0.5D, 0.0F, 0.0F); + if (world.addEntity(entityirongolem, SpawnReason.BUILD_IRONGOLEM)) { + for (int i1 = 0; i1 < 120; ++i1) { + world.addParticle("snowballpoof", (double) i + world.random.nextDouble(), (double) (j - 2) + world.random.nextDouble() * 3.9D, (double) k + world.random.nextDouble(), 0.0D, 0.0D, 0.0D); + } + + blockList.updateList(); + } + // CraftBukkit end + } + } + } + + public boolean canPlace(World world, int i, int j, int k) { + return world.getType(i, j, k).material.isReplaceable() && World.a((IBlockAccess) world, i, j - 1, k); + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 2.5D) & 3; + + world.setData(i, j, k, l, 2); + } + + // CraftBukkit start + public void doPhysics(World world, int i, int j, int k, Block block) { + if (block != null && block.isPowerSource()) { + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(i, j, k); + int power = bukkitBlock.getBlockPower(); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, power, power); + world.getServer().getPluginManager().callEvent(eventRedstone); + } + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneLamp.java b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneLamp.java new file mode 100644 index 0000000..cb802e8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneLamp.java @@ -0,0 +1,70 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockRedstoneLamp extends Block { + + private final boolean a; + + public BlockRedstoneLamp(boolean flag) { + super(Material.BUILDABLE_GLASS); + this.a = flag; + if (flag) { + this.a(1.0F); + } + } + + public void onPlace(World world, int i, int j, int k) { + if (!world.isStatic) { + if (this.a && !world.isBlockIndirectlyPowered(i, j, k)) { + world.a(i, j, k, this, 4); + } else if (!this.a && world.isBlockIndirectlyPowered(i, j, k)) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, i, j, k, 0, 15).getNewCurrent() != 15) { + return; + } + // CraftBukkit end + + world.setTypeAndData(i, j, k, Blocks.REDSTONE_LAMP_ON, 0, 2); + } + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!world.isStatic) { + if (this.a && !world.isBlockIndirectlyPowered(i, j, k)) { + world.a(i, j, k, this, 4); + } else if (!this.a && world.isBlockIndirectlyPowered(i, j, k)) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, i, j, k, 0, 15).getNewCurrent() != 15) { + return; + } + // CraftBukkit end + + world.setTypeAndData(i, j, k, Blocks.REDSTONE_LAMP_ON, 0, 2); + } + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic && this.a && !world.isBlockIndirectlyPowered(i, j, k)) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, i, j, k, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end + + world.setTypeAndData(i, j, k, Blocks.REDSTONE_LAMP_OFF, 0, 2); + } + } + + public Item getDropType(int i, Random random, int j) { + return Item.getItemOf(Blocks.REDSTONE_LAMP_OFF); + } + + protected ItemStack j(int i) { + return new ItemStack(Blocks.REDSTONE_LAMP_OFF); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneOre.java b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneOre.java new file mode 100644 index 0000000..0cd2e04 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneOre.java @@ -0,0 +1,155 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityInteractEvent; +// CraftBukkit end + +public class BlockRedstoneOre extends Block { + + private boolean a; + + public BlockRedstoneOre(boolean flag) { + super(Material.STONE); + if (flag) { + this.a(true); + } + + this.a = flag; + } + + public int a(World world) { + return 30; + } + + public void attack(World world, int i, int j, int k, EntityHuman entityhuman) { + this.e(world, i, j, k, entityhuman); // CraftBukkit - add entityhuman + super.attack(world, i, j, k, entityhuman); + } + + public void b(World world, int i, int j, int k, Entity entity) { + // CraftBukkit start + if (entity instanceof EntityHuman) { + org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, i, j, k, -1, null); + if (!event.isCancelled()) { + this.e(world, i, j, k, entity); // add entity + super.b(world, i, j, k, entity); + } + } else { + EntityInteractEvent event = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(i, j, k)); + world.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.e(world, i, j, k, entity); // add entity + super.b(world, i, j, k, entity); + } + } + // CraftBukkit end + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + this.e(world, i, j, k, entityhuman); // CraftBukkit - add entityhuman + return super.interact(world, i, j, k, entityhuman, l, f, f1, f2); + } + + private void e(World world, int i, int j, int k, Entity entity) { // CraftBukkit - add Entity + this.m(world, i, j, k); + if (this == Blocks.REDSTONE_ORE) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(entity, i, j, k, Blocks.GLOWING_REDSTONE_ORE, 0).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeUpdate(i, j, k, Blocks.GLOWING_REDSTONE_ORE); + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (this == Blocks.GLOWING_REDSTONE_ORE) { + // CraftBukkit start + if (CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(i, j, k), Blocks.REDSTONE_ORE).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeUpdate(i, j, k, Blocks.REDSTONE_ORE); + } + } + + public Item getDropType(int i, Random random, int j) { + return Items.REDSTONE; + } + + public int getDropCount(int i, Random random) { + return this.a(random) + random.nextInt(i + 1); + } + + public int a(Random random) { + return 4 + random.nextInt(2); + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + super.dropNaturally(world, i, j, k, l, f, i1); + /* CraftBukkit start - Delegated to getExpDrop + if (this.getDropType(l, world.random, i1) != Item.getItemOf(this)) { + int j1 = 1 + world.random.nextInt(5); + + this.dropExperience(world, i, j, k, j1); + } + // */ + } + + public int getExpDrop(World world, int l, int i1) { + if (this.getDropType(l, world.random, i1) != Item.getItemOf(this)) { + int j1 = 1 + world.random.nextInt(5); + + return j1; + } + + return 0; + // CraftBukkit end + } + + private void m(World world, int i, int j, int k) { + Random random = world.random; + double d0 = 0.0625D; + + for (int l = 0; l < 6; ++l) { + double d1 = (double) ((float) i + random.nextFloat()); + double d2 = (double) ((float) j + random.nextFloat()); + double d3 = (double) ((float) k + random.nextFloat()); + + if (l == 0 && !world.getType(i, j + 1, k).c()) { + d2 = (double) (j + 1) + d0; + } + + if (l == 1 && !world.getType(i, j - 1, k).c()) { + d2 = (double) (j + 0) - d0; + } + + if (l == 2 && !world.getType(i, j, k + 1).c()) { + d3 = (double) (k + 1) + d0; + } + + if (l == 3 && !world.getType(i, j, k - 1).c()) { + d3 = (double) (k + 0) - d0; + } + + if (l == 4 && !world.getType(i + 1, j, k).c()) { + d1 = (double) (i + 1) + d0; + } + + if (l == 5 && !world.getType(i - 1, j, k).c()) { + d1 = (double) (i + 0) - d0; + } + + if (d1 < (double) i || d1 > (double) (i + 1) || d2 < 0.0D || d2 > (double) (j + 1) || d3 < (double) k || d3 > (double) (k + 1)) { + world.addParticle("reddust", d1, d2, d3, 0.0D, 0.0D, 0.0D); + } + } + } + + protected ItemStack j(int i) { + return new ItemStack(Blocks.REDSTONE_ORE); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneTorch.java b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneTorch.java new file mode 100644 index 0000000..e0469bb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneTorch.java @@ -0,0 +1,176 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockRedstoneTorch extends BlockTorch { + + private boolean isOn; + private static Map b = new java.util.WeakHashMap(); // Spigot + + private boolean a(World world, int i, int j, int k, boolean flag) { + if (!b.containsKey(world)) { + b.put(world, new ArrayList()); + } + + List list = (List) b.get(world); + + if (flag) { + list.add(new RedstoneUpdateInfo(i, j, k, world.getTime())); + } + + int l = 0; + + for (int i1 = 0; i1 < list.size(); ++i1) { + RedstoneUpdateInfo redstoneupdateinfo = (RedstoneUpdateInfo) list.get(i1); + + if (redstoneupdateinfo.a == i && redstoneupdateinfo.b == j && redstoneupdateinfo.c == k) { + ++l; + if (l >= 8) { + return true; + } + } + } + + return false; + } + + protected BlockRedstoneTorch(boolean flag) { + this.isOn = flag; + this.a(true); + this.a((CreativeModeTab) null); + } + + public int a(World world) { + return 2; + } + + public void onPlace(World world, int i, int j, int k) { + if (world.getData(i, j, k) == 0) { + super.onPlace(world, i, j, k); + } + + if (this.isOn) { + world.applyPhysics(i, j - 1, k, this); + world.applyPhysics(i, j + 1, k, this); + world.applyPhysics(i - 1, j, k, this); + world.applyPhysics(i + 1, j, k, this); + world.applyPhysics(i, j, k - 1, this); + world.applyPhysics(i, j, k + 1, this); + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + if (this.isOn) { + world.applyPhysics(i, j - 1, k, this); + world.applyPhysics(i, j + 1, k, this); + world.applyPhysics(i - 1, j, k, this); + world.applyPhysics(i + 1, j, k, this); + world.applyPhysics(i, j, k - 1, this); + world.applyPhysics(i, j, k + 1, this); + } + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + if (!this.isOn) { + return 0; + } else { + int i1 = iblockaccess.getData(i, j, k); + + return i1 == 5 && l == 1 ? 0 : (i1 == 3 && l == 3 ? 0 : (i1 == 4 && l == 2 ? 0 : (i1 == 1 && l == 5 ? 0 : (i1 == 2 && l == 4 ? 0 : 15)))); + } + } + + private boolean m(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + + return l == 5 && world.isBlockFacePowered(i, j - 1, k, 0) ? true : (l == 3 && world.isBlockFacePowered(i, j, k - 1, 2) ? true : (l == 4 && world.isBlockFacePowered(i, j, k + 1, 3) ? true : (l == 1 && world.isBlockFacePowered(i - 1, j, k, 4) ? true : l == 2 && world.isBlockFacePowered(i + 1, j, k, 5)))); + } + + public void a(World world, int i, int j, int k, Random random) { + boolean flag = this.m(world, i, j, k); + List list = (List) b.get(world); + + while (list != null && !list.isEmpty() && world.getTime() - ((RedstoneUpdateInfo) list.get(0)).d > 60L) { + list.remove(0); + } + + // CraftBukkit start + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + int oldCurrent = this.isOn ? 15 : 0; + + BlockRedstoneEvent event = new BlockRedstoneEvent(block, oldCurrent, oldCurrent); + // CraftBukkit end + + if (this.isOn) { + if (flag) { + // CraftBukkit start + if (oldCurrent != 0) { + event.setNewCurrent(0); + manager.callEvent(event); + if (event.getNewCurrent() != 0) { + return; + } + } + // CraftBukkit end + + world.setTypeAndData(i, j, k, Blocks.REDSTONE_TORCH_OFF, world.getData(i, j, k), 3); + if (this.a(world, i, j, k, true)) { + world.makeSound((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), "random.fizz", 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); + + for (int l = 0; l < 5; ++l) { + double d0 = (double) i + random.nextDouble() * 0.6D + 0.2D; + double d1 = (double) j + random.nextDouble() * 0.6D + 0.2D; + double d2 = (double) k + random.nextDouble() * 0.6D + 0.2D; + + world.addParticle("smoke", d0, d1, d2, 0.0D, 0.0D, 0.0D); + } + } + } + } else if (!flag && !this.a(world, i, j, k, false)) { + // CraftBukkit start + if (oldCurrent != 15) { + event.setNewCurrent(15); + manager.callEvent(event); + if (event.getNewCurrent() != 15) { + return; + } + } + // CraftBukkit end + + world.setTypeAndData(i, j, k, Blocks.REDSTONE_TORCH_ON, world.getData(i, j, k), 3); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!this.b(world, i, j, k, block)) { + boolean flag = this.m(world, i, j, k); + + if (this.isOn && flag || !this.isOn && !flag) { + world.a(i, j, k, this, this.a(world)); + } + } + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return l == 0 ? this.b(iblockaccess, i, j, k, l) : 0; + } + + public Item getDropType(int i, Random random, int j) { + return Item.getItemOf(Blocks.REDSTONE_TORCH_ON); + } + + public boolean isPowerSource() { + return true; + } + + public boolean c(Block block) { + return block == Blocks.REDSTONE_TORCH_OFF || block == Blocks.REDSTONE_TORCH_ON; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneWire.java b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneWire.java new file mode 100644 index 0000000..3cec378 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockRedstoneWire.java @@ -0,0 +1,323 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockRedstoneWire extends Block { + + private boolean a = true; + private Set b = new HashSet(); + + public BlockRedstoneWire() { + super(Material.ORIENTABLE); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.0625F, 1.0F); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 5; + } + + public boolean canPlace(World world, int i, int j, int k) { + return World.a((IBlockAccess) world, i, j - 1, k) || world.getType(i, j - 1, k) == Blocks.GLOWSTONE; + } + + private void e(World world, int i, int j, int k) { + this.a(world, i, j, k, i, j, k); + ArrayList arraylist = new ArrayList(this.b); + + this.b.clear(); + + for (int l = 0; l < arraylist.size(); ++l) { + ChunkPosition chunkposition = (ChunkPosition) arraylist.get(l); + + world.applyPhysics(chunkposition.x, chunkposition.y, chunkposition.z, this); + } + } + + private void a(World world, int i, int j, int k, int l, int i1, int j1) { + int k1 = world.getData(i, j, k); + byte b0 = 0; + int l1 = this.getPower(world, l, i1, j1, b0); + + this.a = false; + int i2 = world.getHighestNeighborSignal(i, j, k); + + this.a = true; + if (i2 > 0 && i2 > l1 - 1) { + l1 = i2; + } + + int j2 = 0; + + for (int k2 = 0; k2 < 4; ++k2) { + int l2 = i; + int i3 = k; + + if (k2 == 0) { + l2 = i - 1; + } + + if (k2 == 1) { + ++l2; + } + + if (k2 == 2) { + i3 = k - 1; + } + + if (k2 == 3) { + ++i3; + } + + if (l2 != l || i3 != j1) { + j2 = this.getPower(world, l2, j, i3, j2); + } + + if (world.getType(l2, j, i3).r() && !world.getType(i, j + 1, k).r()) { + if ((l2 != l || i3 != j1) && j >= i1) { + j2 = this.getPower(world, l2, j + 1, i3, j2); + } + } else if (!world.getType(l2, j, i3).r() && (l2 != l || i3 != j1) && j <= i1) { + j2 = this.getPower(world, l2, j - 1, i3, j2); + } + } + + if (j2 > l1) { + l1 = j2 - 1; + } else if (l1 > 0) { + --l1; + } else { + l1 = 0; + } + + if (i2 > l1 - 1) { + l1 = i2; + } + + // CraftBukkit start + if (k1 != l1) { + BlockRedstoneEvent event = new BlockRedstoneEvent(world.getWorld().getBlockAt(i, j, k), k1, l1); + world.getServer().getPluginManager().callEvent(event); + + l1 = event.getNewCurrent(); + } + // CraftBukkit end + if (k1 != l1) { + world.setData(i, j, k, l1, 2); + this.b.add(new ChunkPosition(i, j, k)); + this.b.add(new ChunkPosition(i - 1, j, k)); + this.b.add(new ChunkPosition(i + 1, j, k)); + this.b.add(new ChunkPosition(i, j - 1, k)); + this.b.add(new ChunkPosition(i, j + 1, k)); + this.b.add(new ChunkPosition(i, j, k - 1)); + this.b.add(new ChunkPosition(i, j, k + 1)); + } + } + + private void m(World world, int i, int j, int k) { + if (world.getType(i, j, k) == this) { + world.applyPhysics(i, j, k, this); + world.applyPhysics(i - 1, j, k, this); + world.applyPhysics(i + 1, j, k, this); + world.applyPhysics(i, j, k - 1, this); + world.applyPhysics(i, j, k + 1, this); + world.applyPhysics(i, j - 1, k, this); + world.applyPhysics(i, j + 1, k, this); + } + } + + public void onPlace(World world, int i, int j, int k) { + super.onPlace(world, i, j, k); + if (!world.isStatic) { + this.e(world, i, j, k); + world.applyPhysics(i, j + 1, k, this); + world.applyPhysics(i, j - 1, k, this); + this.m(world, i - 1, j, k); + this.m(world, i + 1, j, k); + this.m(world, i, j, k - 1); + this.m(world, i, j, k + 1); + if (world.getType(i - 1, j, k).r()) { + this.m(world, i - 1, j + 1, k); + } else { + this.m(world, i - 1, j - 1, k); + } + + if (world.getType(i + 1, j, k).r()) { + this.m(world, i + 1, j + 1, k); + } else { + this.m(world, i + 1, j - 1, k); + } + + if (world.getType(i, j, k - 1).r()) { + this.m(world, i, j + 1, k - 1); + } else { + this.m(world, i, j - 1, k - 1); + } + + if (world.getType(i, j, k + 1).r()) { + this.m(world, i, j + 1, k + 1); + } else { + this.m(world, i, j - 1, k + 1); + } + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + super.remove(world, i, j, k, block, l); + if (!world.isStatic) { + world.applyPhysics(i, j + 1, k, this); + world.applyPhysics(i, j - 1, k, this); + world.applyPhysics(i + 1, j, k, this); + world.applyPhysics(i - 1, j, k, this); + world.applyPhysics(i, j, k + 1, this); + world.applyPhysics(i, j, k - 1, this); + this.e(world, i, j, k); + this.m(world, i - 1, j, k); + this.m(world, i + 1, j, k); + this.m(world, i, j, k - 1); + this.m(world, i, j, k + 1); + if (world.getType(i - 1, j, k).r()) { + this.m(world, i - 1, j + 1, k); + } else { + this.m(world, i - 1, j - 1, k); + } + + if (world.getType(i + 1, j, k).r()) { + this.m(world, i + 1, j + 1, k); + } else { + this.m(world, i + 1, j - 1, k); + } + + if (world.getType(i, j, k - 1).r()) { + this.m(world, i, j + 1, k - 1); + } else { + this.m(world, i, j - 1, k - 1); + } + + if (world.getType(i, j, k + 1).r()) { + this.m(world, i, j + 1, k + 1); + } else { + this.m(world, i, j - 1, k + 1); + } + } + } + + // CraftBukkit - private -> public + public int getPower(World world, int i, int j, int k, int l) { + if (world.getType(i, j, k) != this) { + return l; + } else { + int i1 = world.getData(i, j, k); + + return i1 > l ? i1 : l; + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!world.isStatic) { + boolean flag = this.canPlace(world, i, j, k); + + if (flag) { + this.e(world, i, j, k); + } else { + this.b(world, i, j, k, 0, 0); + world.setAir(i, j, k); + } + + super.doPhysics(world, i, j, k, block); + } + } + + public Item getDropType(int i, Random random, int j) { + return Items.REDSTONE; + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return !this.a ? 0 : this.b(iblockaccess, i, j, k, l); + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + if (!this.a) { + return 0; + } else { + int i1 = iblockaccess.getData(i, j, k); + + if (i1 == 0) { + return 0; + } else if (l == 1) { + return i1; + } else { + boolean flag = g(iblockaccess, i - 1, j, k, 1) || !iblockaccess.getType(i - 1, j, k).r() && g(iblockaccess, i - 1, j - 1, k, -1); + boolean flag1 = g(iblockaccess, i + 1, j, k, 3) || !iblockaccess.getType(i + 1, j, k).r() && g(iblockaccess, i + 1, j - 1, k, -1); + boolean flag2 = g(iblockaccess, i, j, k - 1, 2) || !iblockaccess.getType(i, j, k - 1).r() && g(iblockaccess, i, j - 1, k - 1, -1); + boolean flag3 = g(iblockaccess, i, j, k + 1, 0) || !iblockaccess.getType(i, j, k + 1).r() && g(iblockaccess, i, j - 1, k + 1, -1); + + if (!iblockaccess.getType(i, j + 1, k).r()) { + if (iblockaccess.getType(i - 1, j, k).r() && g(iblockaccess, i - 1, j + 1, k, -1)) { + flag = true; + } + + if (iblockaccess.getType(i + 1, j, k).r() && g(iblockaccess, i + 1, j + 1, k, -1)) { + flag1 = true; + } + + if (iblockaccess.getType(i, j, k - 1).r() && g(iblockaccess, i, j + 1, k - 1, -1)) { + flag2 = true; + } + + if (iblockaccess.getType(i, j, k + 1).r() && g(iblockaccess, i, j + 1, k + 1, -1)) { + flag3 = true; + } + } + + return !flag2 && !flag1 && !flag && !flag3 && l >= 2 && l <= 5 ? i1 : (l == 2 && flag2 && !flag && !flag1 ? i1 : (l == 3 && flag3 && !flag && !flag1 ? i1 : (l == 4 && flag && !flag2 && !flag3 ? i1 : (l == 5 && flag1 && !flag2 && !flag3 ? i1 : 0)))); + } + } + } + + public boolean isPowerSource() { + return this.a; + } + + public static boolean f(IBlockAccess iblockaccess, int i, int j, int k, int l) { + Block block = iblockaccess.getType(i, j, k); + + if (block == Blocks.REDSTONE_WIRE) { + return true; + } else if (!Blocks.DIODE_OFF.e(block)) { + return block.isPowerSource() && l != -1; + } else { + int i1 = iblockaccess.getData(i, j, k); + + return l == (i1 & 3) || l == Direction.f[i1 & 3]; + } + } + + public static boolean g(IBlockAccess iblockaccess, int i, int j, int k, int l) { + if (f(iblockaccess, i, j, k, l)) { + return true; + } else if (iblockaccess.getType(i, j, k) == Blocks.DIODE_ON) { + int i1 = iblockaccess.getData(i, j, k); + + return l == (i1 & 3); + } else { + return false; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockReed.java b/vspigot-server/src/main/java/net/minecraft/server/BlockReed.java new file mode 100644 index 0000000..0fa3349 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockReed.java @@ -0,0 +1,81 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockReed extends Block { + + protected BlockReed() { + super(Material.PLANT); + float f = 0.375F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 1.0F, 0.5F + f); + this.a(true); + } + + public void a(World world, int i, int j, int k, Random random) { + if (world.getType(i, j - 1, k) == Blocks.SUGAR_CANE_BLOCK || this.e(world, i, j, k)) { + if (world.isEmpty(i, j + 1, k)) { + int l; + + for (l = 1; world.getType(i, j - l, k) == this; ++l) { + ; + } + + if (l < world.paperSpigotConfig.reedMaxHeight) { // PaperSpigot - Configurable max growth height for reed blocks + int i1 = world.getData(i, j, k); + + if (i1 >= (byte) range(3, (world.growthOdds / world.spigotConfig.caneModifier * 15) + 0.5F, 15)) { // Spigot + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, i, j + 1, k, this, 0); // CraftBukkit + world.setData(i, j, k, 0, 4); + } else { + world.setData(i, j, k, i1 + 1, 4); + } + } + } + } + } + + public boolean canPlace(World world, int i, int j, int k) { + Block block = world.getType(i, j - 1, k); + + return block == this ? true : (block != Blocks.GRASS && block != Blocks.DIRT && block != Blocks.SAND ? false : (world.getType(i - 1, j - 1, k).getMaterial() == Material.WATER ? true : (world.getType(i + 1, j - 1, k).getMaterial() == Material.WATER ? true : (world.getType(i, j - 1, k - 1).getMaterial() == Material.WATER ? true : world.getType(i, j - 1, k + 1).getMaterial() == Material.WATER)))); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + this.e(world, i, j, k); + } + + protected final boolean e(World world, int i, int j, int k) { + if (!this.j(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + return false; + } else { + return true; + } + } + + public boolean j(World world, int i, int j, int k) { + return this.canPlace(world, i, j, k); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public Item getDropType(int i, Random random, int j) { + return Items.SUGAR_CANE; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 1; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockSapling.java b/vspigot-server/src/main/java/net/minecraft/server/BlockSapling.java new file mode 100644 index 0000000..9383e2e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockSapling.java @@ -0,0 +1,203 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.TreeType; +import org.bukkit.block.BlockState; +import org.bukkit.event.world.StructureGrowEvent; +// CraftBukkit end + +public class BlockSapling extends BlockPlant implements IBlockFragilePlantElement { + + public static final String[] a = new String[] { "oak", "spruce", "birch", "jungle", "acacia", "roofed_oak"}; + private static final IIcon[] b = new IIcon[a.length]; + public static TreeType treeType; // CraftBukkit + + protected BlockSapling() { + float f = 0.4F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, f * 2.0F, 0.5F + f); + this.a(CreativeModeTab.c); + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + super.a(world, i, j, k, random); + if (world.isLightLevel(i, j + 1, k, 9) && (random.nextInt(Math.max(2, (int) ((world.growthOdds / world.spigotConfig.saplingModifier * 7) + 0.5F))) == 0)) { // Spigot // MineHQ + // CraftBukkit start + world.captureTreeGeneration = true; + // CraftBukkit end + this.grow(world, i, j, k, random); + // CraftBukkit start + world.captureTreeGeneration = false; + if (world.capturedBlockStates.size() > 0) { + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; + Location location = new Location(world.getWorld(), i, j, k); + List blocks = (List) world.capturedBlockStates.clone(); + world.capturedBlockStates.clear(); + StructureGrowEvent event = null; + if (treeType != null) { + event = new StructureGrowEvent(location, treeType, false, null, blocks); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + } + if (event == null || !event.isCancelled()) { + for (BlockState blockstate : blocks) { + blockstate.update(true); + } + } + } + // CraftBukkit end + } + } + } + + public void grow(World world, int i, int j, int k, Random random) { + int l = world.getData(i, j, k); + + if ((l & 8) == 0) { + world.setData(i, j, k, l | 8, 4); + } else { + this.d(world, i, j, k, random); + } + } + + public void d(World world, int i, int j, int k, Random random) { + int l = world.getData(i, j, k) & 7; + // CraftBukkit start - Turn ternary operator into if statement to set treeType + // Object object = random.nextInt(10) == 0 ? new WorldGenBigTree(true) : new WorldGenTrees(true); + Object object; + if (random.nextInt(10) == 0) { + treeType = TreeType.BIG_TREE; + object = new WorldGenBigTree(true); + } else { + treeType = TreeType.TREE; + object = new WorldGenTrees(true); + } + // CraftBukkit end + int i1 = 0; + int j1 = 0; + boolean flag = false; + + switch (l) { + case 0: + default: + break; + + case 1: + label78: + for (i1 = 0; i1 >= -1; --i1) { + for (j1 = 0; j1 >= -1; --j1) { + if (this.a(world, i + i1, j, k + j1, 1) && this.a(world, i + i1 + 1, j, k + j1, 1) && this.a(world, i + i1, j, k + j1 + 1, 1) && this.a(world, i + i1 + 1, j, k + j1 + 1, 1)) { + treeType = TreeType.MEGA_REDWOOD; // CraftBukkit + object = new WorldGenMegaTree(false, random.nextBoolean()); + flag = true; + break label78; + } + } + } + + if (!flag) { + j1 = 0; + i1 = 0; + treeType = TreeType.REDWOOD; // CraftBukkit + object = new WorldGenTaiga2(true); + } + break; + + case 2: + treeType = TreeType.BIRCH; // CraftBukkit + object = new WorldGenForest(true, false); + break; + + case 3: + label93: + for (i1 = 0; i1 >= -1; --i1) { + for (j1 = 0; j1 >= -1; --j1) { + if (this.a(world, i + i1, j, k + j1, 3) && this.a(world, i + i1 + 1, j, k + j1, 3) && this.a(world, i + i1, j, k + j1 + 1, 3) && this.a(world, i + i1 + 1, j, k + j1 + 1, 3)) { + treeType = TreeType.JUNGLE; // CraftBukkit + object = new WorldGenJungleTree(true, 10, 20, 3, 3); + flag = true; + break label93; + } + } + } + + if (!flag) { + j1 = 0; + i1 = 0; + treeType = TreeType.SMALL_JUNGLE; // CraftBukkit + object = new WorldGenTrees(true, 4 + random.nextInt(7), 3, 3, false); + } + break; + + case 4: + treeType = TreeType.ACACIA; // CraftBukkit + object = new WorldGenAcaciaTree(true); + break; + + case 5: + label108: + for (i1 = 0; i1 >= -1; --i1) { + for (j1 = 0; j1 >= -1; --j1) { + if (this.a(world, i + i1, j, k + j1, 5) && this.a(world, i + i1 + 1, j, k + j1, 5) && this.a(world, i + i1, j, k + j1 + 1, 5) && this.a(world, i + i1 + 1, j, k + j1 + 1, 5)) { + object = new WorldGenForestTree(true); + treeType = TreeType.DARK_OAK; // CraftBukkit + flag = true; + break label108; + } + } + } + + if (!flag) { + return; + } + } + + Block block = Blocks.AIR; + + if (flag) { + world.setTypeAndData(i + i1, j, k + j1, block, 0, 4); + world.setTypeAndData(i + i1 + 1, j, k + j1, block, 0, 4); + world.setTypeAndData(i + i1, j, k + j1 + 1, block, 0, 4); + world.setTypeAndData(i + i1 + 1, j, k + j1 + 1, block, 0, 4); + } else { + world.setTypeAndData(i, j, k, block, 0, 4); + } + + if (!((WorldGenerator) object).generate(world, random, i + i1, j, k + j1)) { + if (flag) { + world.setTypeAndData(i + i1, j, k + j1, this, l, 4); + world.setTypeAndData(i + i1 + 1, j, k + j1, this, l, 4); + world.setTypeAndData(i + i1, j, k + j1 + 1, this, l, 4); + world.setTypeAndData(i + i1 + 1, j, k + j1 + 1, this, l, 4); + } else { + world.setTypeAndData(i, j, k, this, l, 4); + } + } + } + + public boolean a(World world, int i, int j, int k, int l) { + return world.getType(i, j, k) == this && (world.getData(i, j, k) & 7) == l; + } + + public int getDropData(int i) { + return MathHelper.a(i & 7, 0, 5); + } + + public boolean a(World world, int i, int j, int k, boolean flag) { + return true; + } + + public boolean a(World world, Random random, int i, int j, int k) { + return (double) world.random.nextFloat() < 0.45D; + } + + public void b(World world, Random random, int i, int j, int k) { + this.grow(world, i, j, k, random); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockSign.java b/vspigot-server/src/main/java/net/minecraft/server/BlockSign.java new file mode 100644 index 0000000..4ee3319 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockSign.java @@ -0,0 +1,127 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockSign extends BlockContainer { + + private Class a; + private boolean b; + + protected BlockSign(Class oclass, boolean flag) { + super(Material.WOOD); + this.b = flag; + this.a = oclass; + float f = 0.25F; + float f1 = 1.0F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, f1, 0.5F + f); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + if (!this.b) { + int l = iblockaccess.getData(i, j, k); + float f = 0.28125F; + float f1 = 0.78125F; + float f2 = 0.0F; + float f3 = 1.0F; + float f4 = 0.125F; + + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + if (l == 2) { + this.a(f2, f, 1.0F - f4, f3, f1, 1.0F); + } + + if (l == 3) { + this.a(f2, f, 0.0F, f3, f1, f4); + } + + if (l == 4) { + this.a(1.0F - f4, f, f2, 1.0F, f1, f3); + } + + if (l == 5) { + this.a(0.0F, f, f2, f4, f1, f3); + } + } + } + + public int b() { + return -1; + } + + public boolean d() { + return false; + } + + public boolean b(IBlockAccess iblockaccess, int i, int j, int k) { + return true; + } + + public boolean c() { + return false; + } + + public TileEntity a(World world, int i) { + try { + return (TileEntity) this.a.newInstance(); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + public Item getDropType(int i, Random random, int j) { + return Items.SIGN; + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + boolean flag = false; + + if (this.b) { + if (!world.getType(i, j - 1, k).getMaterial().isBuildable()) { + flag = true; + } + } else { + int l = world.getData(i, j, k); + + flag = true; + if (l == 2 && world.getType(i, j, k + 1).getMaterial().isBuildable()) { + flag = false; + } + + if (l == 3 && world.getType(i, j, k - 1).getMaterial().isBuildable()) { + flag = false; + } + + if (l == 4 && world.getType(i + 1, j, k).getMaterial().isBuildable()) { + flag = false; + } + + if (l == 5 && world.getType(i - 1, j, k).getMaterial().isBuildable()) { + flag = false; + } + } + + if (flag) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + + super.doPhysics(world, i, j, k, block); + + // CraftBukkit start + if (block != null && block.isPowerSource()) { + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(i, j, k); + int power = bukkitBlock.getBlockPower(); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, power, power); + world.getServer().getPluginManager().callEvent(eventRedstone); + } + // CraftBukkit end + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockSkull.java b/vspigot-server/src/main/java/net/minecraft/server/BlockSkull.java new file mode 100644 index 0000000..3075dbc --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockSkull.java @@ -0,0 +1,244 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; + +// CraftBukkit start +import org.bukkit.craftbukkit.util.BlockStateListPopulator; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + +public class BlockSkull extends BlockContainer { + + protected BlockSkull() { + super(Material.ORIENTABLE); + this.a(0.25F, 0.0F, 0.25F, 0.75F, 0.5F, 0.75F); + } + + public int b() { + return -1; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k) & 7; + + switch (l) { + case 1: + default: + this.a(0.25F, 0.0F, 0.25F, 0.75F, 0.5F, 0.75F); + break; + + case 2: + this.a(0.25F, 0.25F, 0.5F, 0.75F, 0.75F, 1.0F); + break; + + case 3: + this.a(0.25F, 0.25F, 0.0F, 0.75F, 0.75F, 0.5F); + break; + + case 4: + this.a(0.5F, 0.25F, 0.25F, 1.0F, 0.75F, 0.75F); + break; + + case 5: + this.a(0.0F, 0.25F, 0.25F, 0.5F, 0.75F, 0.75F); + } + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k); + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 2.5D) & 3; + + world.setData(i, j, k, l, 2); + } + + public TileEntity a(World world, int i) { + return new TileEntitySkull(); + } + + public int getDropData(World world, int i, int j, int k) { + TileEntity tileentity = world.getTileEntity(i, j, k); + + return tileentity != null && tileentity instanceof TileEntitySkull ? ((TileEntitySkull) tileentity).getSkullType() : super.getDropData(world, i, j, k); + } + + public int getDropData(int i) { + return i; + } + + // CraftBukkit start - Special case dropping so we can get info from the tile entity + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (world.random.nextFloat() < f) { + ItemStack itemstack = new ItemStack(Items.SKULL, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getGameProfile() != null) { + itemstack.setTag(new NBTTagCompound()); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound, tileentityskull.getGameProfile()); + itemstack.getTag().set("SkullOwner", nbttagcompound); + } + + this.a(world, i, j, k, itemstack); + } + } + // CraftBukkit end + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild) { + l |= 8; + world.setData(i, j, k, l, 4); + } + + super.a(world, i, j, k, l, entityhuman); + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + if (!world.isStatic) { + // CraftBukkit start - Drop item in code above, not here + // if ((l & 8) == 0) { + if (false) { + // CraftBukkit end + ItemStack itemstack = new ItemStack(Items.SKULL, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getGameProfile() != null) { + itemstack.setTag(new NBTTagCompound()); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound, tileentityskull.getGameProfile()); + itemstack.getTag().set("SkullOwner", nbttagcompound); + } + + this.a(world, i, j, k, itemstack); + } + + super.remove(world, i, j, k, block, l); + } + } + + public Item getDropType(int i, Random random, int j) { + return Items.SKULL; + } + + public void a(World world, int i, int j, int k, TileEntitySkull tileentityskull) { + if (tileentityskull.getSkullType() == 1 && j >= 2 && world.difficulty != EnumDifficulty.PEACEFUL && !world.isStatic) { + int l; + EntityWither entitywither; + Iterator iterator; + EntityHuman entityhuman; + int i1; + + for (l = -2; l <= 0; ++l) { + if (world.getType(i, j - 1, k + l) == Blocks.SOUL_SAND && world.getType(i, j - 1, k + l + 1) == Blocks.SOUL_SAND && world.getType(i, j - 2, k + l + 1) == Blocks.SOUL_SAND && world.getType(i, j - 1, k + l + 2) == Blocks.SOUL_SAND && this.a(world, i, j, k + l, 1) && this.a(world, i, j, k + l + 1, 1) && this.a(world, i, j, k + l + 2, 1)) { + // CraftBukkit start - Use BlockStateListPopulator + BlockStateListPopulator blockList = new BlockStateListPopulator(world.getWorld()); + + world.setData(i, j, k + l, 8, 2); + world.setData(i, j, k + l + 1, 8, 2); + world.setData(i, j, k + l + 2, 8, 2); + + blockList.setTypeAndData(i, j, k + l, getById(0), 0, 2); + blockList.setTypeAndData(i, j, k + l + 1, getById(0), 0, 2); + blockList.setTypeAndData(i, j, k + l + 2, getById(0), 0, 2); + blockList.setTypeAndData(i, j - 1, k + l, getById(0), 0, 2); + blockList.setTypeAndData(i, j - 1, k + l + 1, getById(0), 0, 2); + blockList.setTypeAndData(i, j - 1, k + l + 2, getById(0), 0, 2); + blockList.setTypeAndData(i, j - 2, k + l + 1, getById(0), 0, 2); + + if (!world.isStatic) { + entitywither = new EntityWither(world); + entitywither.setPositionRotation((double) i + 0.5D, (double) j - 1.45D, (double) (k + l) + 1.5D, 90.0F, 0.0F); + entitywither.aM = 90.0F; + entitywither.bZ(); + + if (world.addEntity(entitywither, SpawnReason.BUILD_WITHER)) { + if (!world.isStatic) { + iterator = world.a(EntityHuman.class, entitywither.boundingBox.grow(50.0D, 50.0D, 50.0D)).iterator(); + + while (iterator.hasNext()) { + entityhuman = (EntityHuman) iterator.next(); + entityhuman.a((Statistic) AchievementList.I); + } + } + + blockList.updateList(); + } + } + + for (i1 = 0; i1 < 120; ++i1) { + world.addParticle("snowballpoof", (double) i + world.random.nextDouble(), (double) (j - 2) + world.random.nextDouble() * 3.9D, (double) (k + l + 1) + world.random.nextDouble(), 0.0D, 0.0D, 0.0D); + } + // CraftBukkit end + return; + } + } + + for (l = -2; l <= 0; ++l) { + if (world.getType(i + l, j - 1, k) == Blocks.SOUL_SAND && world.getType(i + l + 1, j - 1, k) == Blocks.SOUL_SAND && world.getType(i + l + 1, j - 2, k) == Blocks.SOUL_SAND && world.getType(i + l + 2, j - 1, k) == Blocks.SOUL_SAND && this.a(world, i + l, j, k, 1) && this.a(world, i + l + 1, j, k, 1) && this.a(world, i + l + 2, j, k, 1)) { + // CraftBukkit start - Use BlockStateListPopulator + BlockStateListPopulator blockList = new BlockStateListPopulator(world.getWorld()); + + world.setData(i + l, j, k, 8, 2); + world.setData(i + l + 1, j, k, 8, 2); + world.setData(i + l + 2, j, k, 8, 2); + + blockList.setTypeAndData(i + l, j, k, getById(0), 0, 2); + blockList.setTypeAndData(i + l + 1, j, k, getById(0), 0, 2); + blockList.setTypeAndData(i + l + 2, j, k, getById(0), 0, 2); + blockList.setTypeAndData(i + l, j - 1, k, getById(0), 0, 2); + blockList.setTypeAndData(i + l + 1, j - 1, k, getById(0), 0, 2); + blockList.setTypeAndData(i + l + 2, j - 1, k, getById(0), 0, 2); + blockList.setTypeAndData(i + l + 1, j - 2, k, getById(0), 0, 2); + if (!world.isStatic) { + entitywither = new EntityWither(world); + entitywither.setPositionRotation((double) (i + l) + 1.5D, (double) j - 1.45D, (double) k + 0.5D, 0.0F, 0.0F); + entitywither.bZ(); + + if (world.addEntity(entitywither, SpawnReason.BUILD_WITHER)) { + if (!world.isStatic) { + iterator = world.a(EntityHuman.class, entitywither.boundingBox.grow(50.0D, 50.0D, 50.0D)).iterator(); + + while (iterator.hasNext()) { + entityhuman = (EntityHuman) iterator.next(); + entityhuman.a((Statistic) AchievementList.I); + } + } + blockList.updateList(); + } + } + + for (i1 = 0; i1 < 120; ++i1) { + world.addParticle("snowballpoof", (double) (i + l + 1) + world.random.nextDouble(), (double) (j - 2) + world.random.nextDouble() * 3.9D, (double) k + world.random.nextDouble(), 0.0D, 0.0D, 0.0D); + } + // CraftBukkit end + + return; + } + } + } + } + + private boolean a(World world, int i, int j, int k, int l) { + if (world.getType(i, j, k) != this) { + return false; + } else { + TileEntity tileentity = world.getTileEntity(i, j, k); + + return tileentity != null && tileentity instanceof TileEntitySkull ? ((TileEntitySkull) tileentity).getSkullType() == l : false; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockSnow.java b/vspigot-server/src/main/java/net/minecraft/server/BlockSnow.java new file mode 100644 index 0000000..02f428e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockSnow.java @@ -0,0 +1,93 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockSnow extends Block { + + protected BlockSnow() { + super(Material.PACKED_ICE); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); + this.a(true); + this.a(CreativeModeTab.c); + this.b(0); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + int l = world.getData(i, j, k) & 7; + float f = 0.125F; + + return AxisAlignedBB.a((double) i + this.minX, (double) j + this.minY, (double) k + this.minZ, (double) i + this.maxX, (double) ((float) j + (float) l * f), (double) k + this.maxZ); + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public void g() { + this.b(0); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.b(iblockaccess.getData(i, j, k)); + } + + protected void b(int i) { + int j = i & 7; + float f = (float) (2 * (1 + j)) / 16.0F; + + this.a(0.0F, 0.0F, 0.0F, 1.0F, f, 1.0F); + } + + public boolean canPlace(World world, int i, int j, int k) { + Block block = world.getType(i, j - 1, k); + + return block != Blocks.ICE && block != Blocks.PACKED_ICE ? (block.getMaterial() == Material.LEAVES ? true : (block == this && (world.getData(i, j - 1, k) & 7) == 7 ? true : block.c() && block.material.isSolid())) : false; + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + this.m(world, i, j, k); + } + + private boolean m(World world, int i, int j, int k) { + if (!this.canPlace(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + return false; + } else { + return true; + } + } + + public void a(World world, EntityHuman entityhuman, int i, int j, int k, int l) { + int i1 = l & 7; + + this.a(world, i, j, k, new ItemStack(Items.SNOW_BALL, i1 + 1, 0)); + world.setAir(i, j, k); + entityhuman.a(StatisticList.MINE_BLOCK_COUNT[Block.getId(this)], 1); + } + + public Item getDropType(int i, Random random, int j) { + return Items.SNOW_BALL; + } + + public int a(Random random) { + return 0; + } + + public void a(World world, int i, int j, int k, Random random) { + if (world.b(EnumSkyBlock.BLOCK, i, j, k) > 11) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(i, j, k), Blocks.AIR).isCancelled()) { + return; + } + // CraftBukkit end + + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockSoil.java b/vspigot-server/src/main/java/net/minecraft/server/BlockSoil.java new file mode 100644 index 0000000..4db09b6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockSoil.java @@ -0,0 +1,122 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.event.entity.EntityInteractEvent; +import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + +public class BlockSoil extends Block { + + protected BlockSoil() { + super(Material.EARTH); + this.a(true); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.9375F, 1.0F); + this.g(255); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return AxisAlignedBB.a((double) (i + 0), (double) (j + 0), (double) (k + 0), (double) (i + 1), (double) (j + 1), (double) (k + 1)); + } + + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public void a(World world, int i, int j, int k, Random random) { + if (!this.m(world, i, j, k) && !world.isRainingAt(i, j + 1, k)) { + int l = world.getData(i, j, k); + + if (l > 0) { + world.setData(i, j, k, l - 1, 2); + } else if (!this.e(world, i, j, k)) { + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + if (CraftEventFactory.callBlockFadeEvent(block, Blocks.DIRT).isCancelled()) { + return; + } + // CraftBukkit end + + world.setTypeUpdate(i, j, k, Blocks.DIRT); + } + } else { + world.setData(i, j, k, 7, 2); + } + } + + public void a(World world, int i, int j, int k, Entity entity, float f) { + if (!world.isStatic && world.random.nextFloat() < f - 0.5F) { + if (!(entity instanceof EntityHuman) && !world.getGameRules().getBoolean("mobGriefing")) { + return; + } + + // CraftBukkit start - Interact soil + org.bukkit.event.Cancellable cancellable; + if (entity instanceof EntityHuman) { + cancellable = CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, i, j, k, -1, null); + } else { + cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(i, j, k)); + world.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); + } + + if (cancellable.isCancelled()) { + return; + } + + if (CraftEventFactory.callEntityChangeBlockEvent(entity, i, j, k, Blocks.DIRT, 0).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeUpdate(i, j, k, Blocks.DIRT); + } + } + + private boolean e(World world, int i, int j, int k) { + byte b0 = 0; + + for (int l = i - b0; l <= i + b0; ++l) { + for (int i1 = k - b0; i1 <= k + b0; ++i1) { + Block block = world.getType(l, j + 1, i1); + + if (block == Blocks.CROPS || block == Blocks.MELON_STEM || block == Blocks.PUMPKIN_STEM || block == Blocks.POTATOES || block == Blocks.CARROTS) { + return true; + } + } + } + + return false; + } + + private boolean m(World world, int i, int j, int k) { + for (int l = i - 4; l <= i + 4; ++l) { + for (int i1 = j; i1 <= j + 1; ++i1) { + for (int j1 = k - 4; j1 <= k + 4; ++j1) { + if (world.getType(l, i1, j1).getMaterial() == Material.WATER) { + return true; + } + } + } + } + + return false; + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + super.doPhysics(world, i, j, k, block); + Material material = world.getType(i, j + 1, k).getMaterial(); + + if (material.isBuildable()) { + world.setTypeUpdate(i, j, k, Blocks.DIRT); + } + } + + public Item getDropType(int i, Random random, int j) { + return Blocks.DIRT.getDropType(0, random, j); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockStationary.java b/vspigot-server/src/main/java/net/minecraft/server/BlockStationary.java new file mode 100644 index 0000000..51b9604 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockStationary.java @@ -0,0 +1,93 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockStationary extends BlockFluids { + + protected BlockStationary(Material material) { + super(material); + this.a(false); + if (material == Material.LAVA) { + this.a(true); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + super.doPhysics(world, i, j, k, block); + if (world.getType(i, j, k) == this) { + this.n(world, i, j, k); + } + } + + private void n(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + + world.setTypeAndData(i, j, k, Block.getById(Block.getId(this) - 1), l, 2); + world.a(i, j, k, Block.getById(Block.getId(this) - 1), this.a(world)); + } + + public void a(World world, int i, int j, int k, Random random) { + if (this.material == Material.LAVA) { + int l = random.nextInt(3); + + int i1; + + // CraftBukkit start - Prevent lava putting something on fire, remember igniter block coords + int x = i; + int y = j; + int z = k; + // CraftBukkit end + + for (i1 = 0; i1 < l; ++i1) { + i += random.nextInt(3) - 1; + ++j; + k += random.nextInt(3) - 1; + Block block = world.getType(i, j, k); + + if (block.material == Material.AIR) { + if (this.o(world, i - 1, j, k) || this.o(world, i + 1, j, k) || this.o(world, i, j, k - 1) || this.o(world, i, j, k + 1) || this.o(world, i, j - 1, k) || this.o(world, i, j + 1, k)) { + // CraftBukkit start - Prevent lava putting something on fire + if (world.getType(i, j, k) != Blocks.FIRE) { + if (CraftEventFactory.callBlockIgniteEvent(world, i, j, k, x, y, z).isCancelled()) { + continue; + } + } + // CraftBukkit end + + world.setTypeUpdate(i, j, k, Blocks.FIRE); + return; + } + } else if (block.material.isSolid()) { + return; + } + } + + if (l == 0) { + i1 = i; + int j1 = k; + + for (int k1 = 0; k1 < 3; ++k1) { + i = i1 + random.nextInt(3) - 1; + k = j1 + random.nextInt(3) - 1; + if (world.isEmpty(i, j + 1, k) && this.o(world, i, j, k)) { + // CraftBukkit start - Prevent lava putting something on fire + if (world.getType(i, j + 1, k) != Blocks.FIRE) { + if (CraftEventFactory.callBlockIgniteEvent(world, i, j + 1, k, x, y, z).isCancelled()) { + continue; + } + } + // CraftBukkit end + + world.setTypeUpdate(i, j + 1, k, Blocks.FIRE); + } + } + } + } + } + + private boolean o(World world, int i, int j, int k) { + return world.getType(i, j, k).getMaterial().isBurnable(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockStem.java b/vspigot-server/src/main/java/net/minecraft/server/BlockStem.java new file mode 100644 index 0000000..3262f4d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockStem.java @@ -0,0 +1,190 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockStem extends BlockPlant implements IBlockFragilePlantElement { + + private final Block blockFruit; + + protected BlockStem(Block block) { + this.blockFruit = block; + this.a(true); + float f = 0.125F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.25F, 0.5F + f); + this.a((CreativeModeTab) null); + } + + protected boolean a(Block block) { + return block == Blocks.SOIL; + } + + public void a(World world, int i, int j, int k, Random random) { + super.a(world, i, j, k, random); + if (world.isLightLevel(i, j + 1, k, 9)) { // MineHQ + float f = this.n(world, i, j, k); + + if (random.nextInt((int) (world.growthOdds / (this == Blocks.PUMPKIN_STEM? world.spigotConfig.pumpkinModifier : world.spigotConfig.melonModifier) * (25.0F / f)) + 1) == 0) { // Spigot + int l = world.getData(i, j, k); + + if (l < 7) { + ++l; + CraftEventFactory.handleBlockGrowEvent(world, i, j, k, this, l); // CraftBukkit + } else { + if (world.getType(i - 1, j, k) == this.blockFruit) { + return; + } + + if (world.getType(i + 1, j, k) == this.blockFruit) { + return; + } + + if (world.getType(i, j, k - 1) == this.blockFruit) { + return; + } + + if (world.getType(i, j, k + 1) == this.blockFruit) { + return; + } + + int i1 = random.nextInt(4); + int j1 = i; + int k1 = k; + + if (i1 == 0) { + j1 = i - 1; + } + + if (i1 == 1) { + ++j1; + } + + if (i1 == 2) { + k1 = k - 1; + } + + if (i1 == 3) { + ++k1; + } + + Block block = world.getType(j1, j - 1, k1); + + if (world.getType(j1, j, k1).material == Material.AIR && (block == Blocks.SOIL || block == Blocks.DIRT || block == Blocks.GRASS)) { + CraftEventFactory.handleBlockGrowEvent(world, j1, j, k1, this.blockFruit, 0); // CraftBukkit + } + } + } + } + } + + public void m(World world, int i, int j, int k) { + int l = world.getData(i, j, k) + MathHelper.nextInt(world.random, 2, 5); + + if (l > 7) { + l = 7; + } + + CraftEventFactory.handleBlockGrowEvent(world, i, j, k, this, l); // CraftBukkit + } + + private float n(World world, int i, int j, int k) { + float f = 1.0F; + Block block = world.getType(i, j, k - 1); + Block block1 = world.getType(i, j, k + 1); + Block block2 = world.getType(i - 1, j, k); + Block block3 = world.getType(i + 1, j, k); + Block block4 = world.getType(i - 1, j, k - 1); + Block block5 = world.getType(i + 1, j, k - 1); + Block block6 = world.getType(i + 1, j, k + 1); + Block block7 = world.getType(i - 1, j, k + 1); + boolean flag = block2 == this || block3 == this; + boolean flag1 = block == this || block1 == this; + boolean flag2 = block4 == this || block5 == this || block6 == this || block7 == this; + + for (int l = i - 1; l <= i + 1; ++l) { + for (int i1 = k - 1; i1 <= k + 1; ++i1) { + Block block8 = world.getType(l, j - 1, i1); + float f1 = 0.0F; + + if (block8 == Blocks.SOIL) { + f1 = 1.0F; + if (world.getData(l, j - 1, i1) > 0) { + f1 = 3.0F; + } + } + + if (l != i || i1 != k) { + f1 /= 4.0F; + } + + f += f1; + } + } + + if (flag2 || flag && flag1) { + f /= 2.0F; + } + + return f; + } + + public void g() { + float f = 0.125F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.25F, 0.5F + f); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.maxY = (double) ((float) (iblockaccess.getData(i, j, k) * 2 + 2) / 16.0F); + float f = 0.125F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, (float) this.maxY, 0.5F + f); + } + + public int b() { + return 19; + } + + public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + super.dropNaturally(world, i, j, k, l, f, i1); + if (!world.isStatic) { + Item item = null; + + if (this.blockFruit == Blocks.PUMPKIN) { + item = Items.PUMPKIN_SEEDS; + } + + if (this.blockFruit == Blocks.MELON) { + item = Items.MELON_SEEDS; + } + + for (int j1 = 0; j1 < 3; ++j1) { + if (world.random.nextInt(15) <= l) { + this.a(world, i, j, k, new ItemStack(item)); + } + } + } + } + + public Item getDropType(int i, Random random, int j) { + return null; + } + + public int a(Random random) { + return 1; + } + + public boolean a(World world, int i, int j, int k, boolean flag) { + return world.getData(i, j, k) != 7; + } + + public boolean a(World world, Random random, int i, int j, int k) { + return true; + } + + public void b(World world, Random random, int i, int j, int k) { + this.m(world, i, j, k); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockTNT.java b/vspigot-server/src/main/java/net/minecraft/server/BlockTNT.java new file mode 100644 index 0000000..9f48d1f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockTNT.java @@ -0,0 +1,91 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockTNT extends Block { + + public BlockTNT() { + super(Material.TNT); + this.a(CreativeModeTab.d); + } + + public void onPlace(World world, int i, int j, int k) { + super.onPlace(world, i, j, k); + if (world.isBlockIndirectlyPowered(i, j, k)) { + this.postBreak(world, i, j, k, 1); + world.setAir(i, j, k); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (world.isBlockIndirectlyPowered(i, j, k)) { + this.postBreak(world, i, j, k, 1); + world.setAir(i, j, k); + } + } + + public int a(Random random) { + return 1; + } + + public void wasExploded(World world, int i, int j, int k, Explosion explosion) { + if (!world.isStatic) { + // PaperSpigot start - Add FallingBlock and TNT source location API + org.bukkit.Location loc = explosion.source instanceof EntityTNTPrimed ? ((EntityTNTPrimed) explosion.source).sourceLoc : new org.bukkit.Location(world.getWorld(), (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)); + EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(loc, world, (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), explosion.c()); + // PaperSpigot end + + entitytntprimed.fuseTicks = world.random.nextInt(entitytntprimed.fuseTicks / 4) + entitytntprimed.fuseTicks / 8; + world.addEntity(entitytntprimed); + } + } + + public void postBreak(World world, int i, int j, int k, int l) { + this.a(world, i, j, k, l, (EntityLiving) null); + } + + public void a(World world, int i, int j, int k, int l, EntityLiving entityliving) { + if (!world.isStatic) { + if ((l & 1) == 1) { + // PaperSpigot start - Add FallingBlock and TNT source location API + org.bukkit.Location loc = new org.bukkit.Location(world.getWorld(), (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)); + EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(loc, world, (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), entityliving); + // PaperSpigot end + + world.addEntity(entitytntprimed); + world.makeSound(entitytntprimed, "game.tnt.primed", 1.0F, 1.0F); + } + } + } + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (entityhuman.bF() != null && entityhuman.bF().getItem() == Items.FLINT_AND_STEEL) { + this.a(world, i, j, k, 1, (EntityLiving) entityhuman); // Spigot - Fix decompile error! + world.setAir(i, j, k); + entityhuman.bF().damage(1, entityhuman); + return true; + } else { + return super.interact(world, i, j, k, entityhuman, l, f, f1, f2); + } + } + + public void a(World world, int i, int j, int k, Entity entity) { + if (entity instanceof EntityArrow && !world.isStatic) { + EntityArrow entityarrow = (EntityArrow) entity; + + if (entityarrow.isBurning()) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entityarrow, i, j, k, Blocks.AIR, 0).isCancelled()) { + return; + } + // CraftBukkit end + this.a(world, i, j, k, 1, entityarrow.shooter instanceof EntityLiving ? (EntityLiving) entityarrow.shooter : null); + world.setAir(i, j, k); + } + } + } + + public boolean a(Explosion explosion) { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockTallPlant.java b/vspigot-server/src/main/java/net/minecraft/server/BlockTallPlant.java new file mode 100644 index 0000000..f8adc61 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockTallPlant.java @@ -0,0 +1,160 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockTallPlant extends BlockPlant implements IBlockFragilePlantElement { + + public static final String[] a = new String[] { "sunflower", "syringa", "grass", "fern", "rose", "paeonia"}; + + public BlockTallPlant() { + super(Material.PLANT); + this.c(0.0F); + this.a(h); + this.c("doublePlant"); + } + + public int b() { + return 40; + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + public int e(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + + return !c(l) ? l & 7 : iblockaccess.getData(i, j - 1, k) & 7; + } + + public boolean canPlace(World world, int i, int j, int k) { + return super.canPlace(world, i, j, k) && world.isEmpty(i, j + 1, k); + } + + protected void e(World world, int i, int j, int k) { + if (!this.j(world, i, j, k)) { + int l = world.getData(i, j, k); + + if (!c(l)) { + this.b(world, i, j, k, l, 0); + if (world.getType(i, j + 1, k) == this) { + world.setTypeAndData(i, j + 1, k, Blocks.AIR, 0, 2); + } + } + + world.setTypeAndData(i, j, k, Blocks.AIR, 0, 2); + } + } + + public boolean j(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + + return c(l) ? world.getType(i, j - 1, k) == this : world.getType(i, j + 1, k) == this && super.j(world, i, j, k); + } + + public Item getDropType(int i, Random random, int j) { + if (c(i)) { + return null; + } else { + int k = d(i); + + return k != 3 && k != 2 ? Item.getItemOf(this) : null; + } + } + + public int getDropData(int i) { + return c(i) ? 0 : i & 7; + } + + public static boolean c(int i) { + return (i & 8) != 0; + } + + public static int d(int i) { + return i & 7; + } + + public void c(World world, int i, int j, int k, int l, int i1) { + world.setTypeAndData(i, j, k, this, l, i1); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, i, j + 1, k, this, 8); // CraftBukkit + } + + public void postPlace(World world, int i, int j, int k, EntityLiving entityliving, ItemStack itemstack) { + int l = ((MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4; + + world.setTypeAndData(i, j + 1, k, this, 8 | l, 2); + } + + public void a(World world, EntityHuman entityhuman, int i, int j, int k, int l) { + if (world.isStatic || entityhuman.bF() == null || entityhuman.bF().getItem() != Items.SHEARS || c(l) || !this.b(world, i, j, k, l, entityhuman)) { + super.a(world, entityhuman, i, j, k, l); + } + } + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + if (c(l)) { + if (world.getType(i, j - 1, k) == this) { + if (!entityhuman.abilities.canInstantlyBuild) { + int i1 = world.getData(i, j - 1, k); + int j1 = d(i1); + + if (j1 != 3 && j1 != 2) { + world.setAir(i, j - 1, k, true); + } else { + if (!world.isStatic && entityhuman.bF() != null && entityhuman.bF().getItem() == Items.SHEARS) { + this.b(world, i, j, k, i1, entityhuman); + } + + world.setAir(i, j - 1, k); + } + } else { + world.setAir(i, j - 1, k); + } + } + } else if (entityhuman.abilities.canInstantlyBuild && world.getType(i, j + 1, k) == this) { + world.setTypeAndData(i, j + 1, k, Blocks.AIR, 0, 2); + } + + super.a(world, i, j, k, l, entityhuman); + } + + private boolean b(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + int i1 = d(l); + + if (i1 != 3 && i1 != 2) { + return false; + } else { + entityhuman.a(StatisticList.MINE_BLOCK_COUNT[Block.getId(this)], 1); + byte b0 = 1; + + if (i1 == 3) { + b0 = 2; + } + + this.a(world, i, j, k, new ItemStack(Blocks.LONG_GRASS, 2, b0)); + return true; + } + } + + public int getDropData(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + + return c(l) ? d(world.getData(i, j - 1, k)) : d(l); + } + + public boolean a(World world, int i, int j, int k, boolean flag) { + int l = this.e((IBlockAccess) world, i, j, k); + + return l != 2 && l != 3; + } + + public boolean a(World world, Random random, int i, int j, int k) { + return true; + } + + public void b(World world, Random random, int i, int j, int k) { + int l = this.e((IBlockAccess) world, i, j, k); + + this.a(world, i, j, k, new ItemStack(this, 1, l)); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockTorch.java b/vspigot-server/src/main/java/net/minecraft/server/BlockTorch.java new file mode 100644 index 0000000..8de5581 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockTorch.java @@ -0,0 +1,168 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockTorch extends Block { + + protected BlockTorch() { + super(Material.ORIENTABLE); + this.a(true); + this.a(CreativeModeTab.c); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 2; + } + + private boolean m(World world, int i, int j, int k) { + if (World.a((IBlockAccess) world, i, j, k)) { + return true; + } else { + Block block = world.getType(i, j, k); + + // PaperSpigot - Allow torch placement on stained glass as well + return world.paperSpigotConfig.lessPickyTorches ? block == Blocks.FENCE || block == Blocks.NETHER_FENCE || block == Blocks.GLASS || block == Blocks.STAINED_GLASS || block == Blocks.COBBLE_WALL : block == Blocks.FENCE || block == Blocks.NETHER_FENCE || block == Blocks.GLASS || block == Blocks.COBBLE_WALL; + } + } + + public boolean canPlace(World world, int i, int j, int k) { + return world.c(i - 1, j, k, true) ? true : (world.c(i + 1, j, k, true) ? true : (world.c(i, j, k - 1, true) ? true : (world.c(i, j, k + 1, true) ? true : this.m(world, i, j - 1, k)))); + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + int j1 = i1; + + if (l == 1 && this.m(world, i, j - 1, k)) { + j1 = 5; + } + + if (l == 2 && world.c(i, j, k + 1, true)) { + j1 = 4; + } + + if (l == 3 && world.c(i, j, k - 1, true)) { + j1 = 3; + } + + if (l == 4 && world.c(i + 1, j, k, true)) { + j1 = 2; + } + + if (l == 5 && world.c(i - 1, j, k, true)) { + j1 = 1; + } + + return j1; + } + + public void a(World world, int i, int j, int k, Random random) { + super.a(world, i, j, k, random); + if (world.getData(i, j, k) == 0) { + this.onPlace(world, i, j, k); + } + } + + public void onPlace(World world, int i, int j, int k) { + if (world.getData(i, j, k) == 0) { + if (world.c(i - 1, j, k, true)) { + world.setData(i, j, k, 1, 2); + } else if (world.c(i + 1, j, k, true)) { + world.setData(i, j, k, 2, 2); + } else if (world.c(i, j, k - 1, true)) { + world.setData(i, j, k, 3, 2); + } else if (world.c(i, j, k + 1, true)) { + world.setData(i, j, k, 4, 2); + } else if (this.m(world, i, j - 1, k)) { + world.setData(i, j, k, 5, 2); + } + } + + this.e(world, i, j, k); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + this.b(world, i, j, k, block); + } + + protected boolean b(World world, int i, int j, int k, Block block) { + if (this.e(world, i, j, k)) { + int l = world.getData(i, j, k); + boolean flag = false; + + if (!world.c(i - 1, j, k, true) && l == 1) { + flag = true; + } + + if (!world.c(i + 1, j, k, true) && l == 2) { + flag = true; + } + + if (!world.c(i, j, k - 1, true) && l == 3) { + flag = true; + } + + if (!world.c(i, j, k + 1, true) && l == 4) { + flag = true; + } + + if (!this.m(world, i, j - 1, k) && l == 5) { + flag = true; + } + + if (flag) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + return true; + } else { + return false; + } + } else { + return true; + } + } + + protected boolean e(World world, int i, int j, int k) { + if (!this.canPlace(world, i, j, k)) { + if (world.getType(i, j, k) == this) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + + return false; + } else { + return true; + } + } + + public MovingObjectPosition a(World world, int i, int j, int k, Vec3D vec3d, Vec3D vec3d1) { + int l = world.getData(i, j, k) & 7; + float f = 0.15F; + + if (l == 1) { + this.a(0.0F, 0.2F, 0.5F - f, f * 2.0F, 0.8F, 0.5F + f); + } else if (l == 2) { + this.a(1.0F - f * 2.0F, 0.2F, 0.5F - f, 1.0F, 0.8F, 0.5F + f); + } else if (l == 3) { + this.a(0.5F - f, 0.2F, 0.0F, 0.5F + f, 0.8F, f * 2.0F); + } else if (l == 4) { + this.a(0.5F - f, 0.2F, 1.0F - f * 2.0F, 0.5F + f, 0.8F, 1.0F); + } else { + f = 0.1F; + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.6F, 0.5F + f); + } + + return super.a(world, i, j, k, vec3d, vec3d1); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockTrapdoor.java b/vspigot-server/src/main/java/net/minecraft/server/BlockTrapdoor.java new file mode 100644 index 0000000..ecc429e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockTrapdoor.java @@ -0,0 +1,212 @@ +package net.minecraft.server; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockTrapdoor extends Block { + + protected BlockTrapdoor(Material material) { + super(material); + float f = 0.5F; + float f1 = 1.0F; + + this.a(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, f1, 0.5F + f); + this.a(CreativeModeTab.d); + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public boolean b(IBlockAccess iblockaccess, int i, int j, int k) { + return !d(iblockaccess.getData(i, j, k)); + } + + public int b() { + return 0; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k); + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + this.b(iblockaccess.getData(i, j, k)); + } + + public void g() { + float f = 0.1875F; + + this.a(0.0F, 0.5F - f / 2.0F, 0.0F, 1.0F, 0.5F + f / 2.0F, 1.0F); + } + + public void b(int i) { + float f = 0.1875F; + + if ((i & 8) != 0) { + this.a(0.0F, 1.0F - f, 0.0F, 1.0F, 1.0F, 1.0F); + } else { + this.a(0.0F, 0.0F, 0.0F, 1.0F, f, 1.0F); + } + + if (d(i)) { + if ((i & 3) == 0) { + this.a(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F); + } + + if ((i & 3) == 1) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f); + } + + if ((i & 3) == 2) { + this.a(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + if ((i & 3) == 3) { + this.a(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F); + } + } + } + + public void attack(World world, int i, int j, int k, EntityHuman entityhuman) {} + + public boolean interact(World world, int i, int j, int k, EntityHuman entityhuman, int l, float f, float f1, float f2) { + if (this.material == Material.ORE) { + return true; + } else { + int i1 = world.getData(i, j, k); + + world.setData(i, j, k, i1 ^ 4, 2); + world.a(entityhuman, 1003, i, j, k, 0); + return true; + } + } + + public void setOpen(World world, int i, int j, int k, boolean flag) { + int l = world.getData(i, j, k); + boolean flag1 = (l & 4) > 0; + + if (flag1 != flag) { + world.setData(i, j, k, l ^ 4, 2); + world.a((EntityHuman) null, 1003, i, j, k, 0); + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!world.isStatic) { + int l = world.getData(i, j, k); + int i1 = i; + int j1 = k; + + if ((l & 3) == 0) { + j1 = k + 1; + } + + if ((l & 3) == 1) { + --j1; + } + + if ((l & 3) == 2) { + i1 = i + 1; + } + + if ((l & 3) == 3) { + --i1; + } + + if (!a(world.getType(i1, j, j1))) { + world.setAir(i, j, k); + this.b(world, i, j, k, l, 0); + } + + boolean flag = world.isBlockIndirectlyPowered(i, j, k); + + if (flag || block.isPowerSource()) { + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + org.bukkit.block.Block bblock = bworld.getBlockAt(i, j, k); + + int power = bblock.getBlockPower(); + int oldPower = (world.getData(i, j, k) & 4) > 0 ? 15 : 0; + + if (oldPower == 0 ^ power == 0 || block.isPowerSource()) { + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bblock, oldPower, power); + world.getServer().getPluginManager().callEvent(eventRedstone); + flag = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end + + this.setOpen(world, i, j, k, flag); + } + } + } + + public MovingObjectPosition a(World world, int i, int j, int k, Vec3D vec3d, Vec3D vec3d1) { + this.updateShape(world, i, j, k); + return super.a(world, i, j, k, vec3d, vec3d1); + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + int j1 = 0; + + if (l == 2) { + j1 = 0; + } + + if (l == 3) { + j1 = 1; + } + + if (l == 4) { + j1 = 2; + } + + if (l == 5) { + j1 = 3; + } + + if (l != 1 && l != 0 && f1 > 0.5F) { + j1 |= 8; + } + + return j1; + } + + public boolean canPlace(World world, int i, int j, int k, int l) { + if (l == 0) { + return false; + } else if (l == 1) { + return false; + } else { + if (l == 2) { + ++k; + } + + if (l == 3) { + --k; + } + + if (l == 4) { + ++i; + } + + if (l == 5) { + --i; + } + + return a(world.getType(i, j, k)); + } + } + + public static boolean d(int i) { + return (i & 4) != 0; + } + + private static boolean a(Block block) { + return block.material.k() && block.d() || block == Blocks.GLOWSTONE || block instanceof BlockStepAbstract || block instanceof BlockStairs; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockTripwire.java b/vspigot-server/src/main/java/net/minecraft/server/BlockTripwire.java new file mode 100644 index 0000000..d345361 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockTripwire.java @@ -0,0 +1,211 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit +import org.spigotmc.SpigotConfig; + +public class BlockTripwire extends Block { + + public BlockTripwire() { + super(Material.ORIENTABLE); + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.15625F, 1.0F); + this.a(true); + } + + public int a(World world) { + return 10; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + @Override + public MovingObjectPosition a(World world, int i, int j, int k, Vec3D vec3d, Vec3D vec3d1) { + if (SpigotConfig.pearlThroughGatesAndTripwire) { + return null; + } + + return super.a(world, i, j, k, vec3d, vec3d1); + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 30; + } + + public Item getDropType(int i, Random random, int j) { + return Items.STRING; + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + int l = world.getData(i, j, k); + boolean flag = (l & 2) == 2; + boolean flag1 = !World.a((IBlockAccess) world, i, j - 1, k); + + if (flag != flag1) { + this.b(world, i, j, k, l, 0); + world.setAir(i, j, k); + } + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k); + boolean flag = (l & 4) == 4; + boolean flag1 = (l & 2) == 2; + + if (!flag1) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.09375F, 1.0F); + } else if (!flag) { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.5F, 1.0F); + } else { + this.a(0.0F, 0.0625F, 0.0F, 1.0F, 0.15625F, 1.0F); + } + } + + public void onPlace(World world, int i, int j, int k) { + int l = World.a((IBlockAccess) world, i, j - 1, k) ? 0 : 2; + + world.setData(i, j, k, l, 3); + this.a(world, i, j, k, l); + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + this.a(world, i, j, k, l | 1); + } + + public void a(World world, int i, int j, int k, int l, EntityHuman entityhuman) { + if (!world.isStatic) { + if (entityhuman.bF() != null && entityhuman.bF().getItem() == Items.SHEARS) { + world.setData(i, j, k, l | 8, 4); + } + } + } + + private void a(World world, int i, int j, int k, int l) { + int i1 = 0; + + while (i1 < 2) { + int j1 = 1; + + while (true) { + if (j1 < 42) { + int k1 = i + Direction.a[i1] * j1; + int l1 = k + Direction.b[i1] * j1; + Block block = world.getType(k1, j, l1); + + if (block == Blocks.TRIPWIRE_SOURCE) { + int i2 = world.getData(k1, j, l1) & 3; + + if (i2 == Direction.f[i1]) { + Blocks.TRIPWIRE_SOURCE.a(world, k1, j, l1, false, world.getData(k1, j, l1), true, j1, l); + } + } else if (block == Blocks.TRIPWIRE) { + ++j1; + continue; + } + } + + ++i1; + break; + } + } + } + + public void a(World world, int i, int j, int k, Entity entity) { + if (!world.isStatic) { + if ((world.getData(i, j, k) & 1) != 1) { + this.e(world, i, j, k); + } + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic) { + if ((world.getData(i, j, k) & 1) == 1) { + this.e(world, i, j, k); + } + } + } + + private void e(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + boolean flag = (l & 1) == 1; + boolean flag1 = false; + List list = world.getEntities((Entity) null, AxisAlignedBB.a((double) i + this.minX, (double) j + this.minY, (double) k + this.minZ, (double) i + this.maxX, (double) j + this.maxY, (double) k + this.maxZ)); + + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (!entity.az()) { + flag1 = true; + break; + } + } + } + + // CraftBukkit start - Call interact even when triggering connected tripwire + if (flag != flag1 && flag1 && (world.getData(i, j, k) & 4) == 4) { + org.bukkit.World bworld = world.getWorld(); + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + org.bukkit.block.Block block = bworld.getBlockAt(i, j, k); + boolean allowed = false; + + // If all of the events are cancelled block the tripwire trigger, else allow + for (Object object : list) { + if (object != null) { + org.bukkit.event.Cancellable cancellable; + + if (object instanceof EntityHuman) { + cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) object, org.bukkit.event.block.Action.PHYSICAL, i, j, k, -1, null); + } else if (object instanceof Entity) { + cancellable = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); + manager.callEvent((EntityInteractEvent) cancellable); + } else { + continue; + } + + if (!cancellable.isCancelled()) { + allowed = true; + break; + } + } + } + + if (!allowed) { + return; + } + } + // CraftBukkit end + + if (flag1 && !flag) { + l |= 1; + } + + if (!flag1 && flag) { + l &= -2; + } + + if (flag1 != flag) { + world.setData(i, j, k, l, 3); + this.a(world, i, j, k, l); + } + + if (flag1) { + world.a(i, j, k, this, this.a(world)); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockTripwireHook.java b/vspigot-server/src/main/java/net/minecraft/server/BlockTripwireHook.java new file mode 100644 index 0000000..e8e6ce5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockTripwireHook.java @@ -0,0 +1,301 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit +import org.spigotmc.SpigotConfig; + +public class BlockTripwireHook extends Block { + + public BlockTripwireHook() { + super(Material.ORIENTABLE); + this.a(CreativeModeTab.d); + this.a(true); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public int b() { + return 29; + } + + public int a(World world) { + return 10; + } + + public boolean canPlace(World world, int i, int j, int k, int l) { + return l == 2 && world.getType(i, j, k + 1).r() ? true : (l == 3 && world.getType(i, j, k - 1).r() ? true : (l == 4 && world.getType(i + 1, j, k).r() ? true : l == 5 && world.getType(i - 1, j, k).r())); + } + + public boolean canPlace(World world, int i, int j, int k) { + return world.getType(i - 1, j, k).r() ? true : (world.getType(i + 1, j, k).r() ? true : (world.getType(i, j, k - 1).r() ? true : world.getType(i, j, k + 1).r())); + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + byte b0 = 0; + + if (l == 2 && world.c(i, j, k + 1, true)) { + b0 = 2; + } + + if (l == 3 && world.c(i, j, k - 1, true)) { + b0 = 0; + } + + if (l == 4 && world.c(i + 1, j, k, true)) { + b0 = 1; + } + + if (l == 5 && world.c(i - 1, j, k, true)) { + b0 = 3; + } + + return b0; + } + + public void postPlace(World world, int i, int j, int k, int l) { + this.a(world, i, j, k, false, l, false, -1, 0); + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (block != this) { + if (this.e(world, i, j, k)) { + int l = world.getData(i, j, k); + int i1 = l & 3; + boolean flag = false; + + if (!world.getType(i - 1, j, k).r() && i1 == 3) { + flag = true; + } + + if (!world.getType(i + 1, j, k).r() && i1 == 1) { + flag = true; + } + + if (!world.getType(i, j, k - 1).r() && i1 == 0) { + flag = true; + } + + if (!world.getType(i, j, k + 1).r() && i1 == 2) { + flag = true; + } + + if (flag) { + this.b(world, i, j, k, l, 0); + world.setAir(i, j, k); + } + } + } + } + + public void a(World world, int i, int j, int k, boolean flag, int l, boolean flag1, int i1, int j1) { + int k1 = l & 3; + boolean flag2 = (l & 4) == 4; + boolean flag3 = (l & 8) == 8; + boolean flag4 = !flag; + boolean flag5 = false; + boolean flag6 = !World.a((IBlockAccess) world, i, j - 1, k); + int l1 = Direction.a[k1]; + int i2 = Direction.b[k1]; + int j2 = 0; + int[] aint = new int[42]; + + int k2; + int l2; + int i3; + int j3; + + for (l2 = 1; l2 < 42; ++l2) { + k2 = i + l1 * l2; + i3 = k + i2 * l2; + Block block = world.getType(k2, j, i3); + + if (block == Blocks.TRIPWIRE_SOURCE) { + j3 = world.getData(k2, j, i3); + if ((j3 & 3) == Direction.f[k1]) { + j2 = l2; + } + break; + } + + if (block != Blocks.TRIPWIRE && l2 != i1) { + aint[l2] = -1; + flag4 = false; + } else { + j3 = l2 == i1 ? j1 : world.getData(k2, j, i3); + boolean flag7 = (j3 & 8) != 8; + boolean flag8 = (j3 & 1) == 1; + boolean flag9 = (j3 & 2) == 2; + + flag4 &= flag9 == flag6; + flag5 |= flag7 && flag8; + aint[l2] = j3; + if (l2 == i1) { + world.a(i, j, k, this, this.a(world)); + flag4 &= flag7; + } + } + } + + flag4 &= j2 > 1; + flag5 &= flag4; + l2 = (flag4 ? 4 : 0) | (flag5 ? 8 : 0); + l = k1 | l2; + int k3; + + if (j2 > 0) { + k2 = i + l1 * j2; + i3 = k + i2 * j2; + k3 = Direction.f[k1]; + world.setData(k2, j, i3, k3 | l2, 3); + this.a(world, k2, j, i3, k3); + this.a(world, k2, j, i3, flag4, flag5, flag2, flag3); + } + + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if (eventRedstone.getNewCurrent() > 0) { + return; + } + // CraftBukkit end + + this.a(world, i, j, k, flag4, flag5, flag2, flag3); + if (!flag) { + world.setData(i, j, k, l, 3); + if (flag1) { + this.a(world, i, j, k, k1); + } + } + + if (flag2 != flag4) { + for (k2 = 1; k2 < j2; ++k2) { + i3 = i + l1 * k2; + k3 = k + i2 * k2; + j3 = aint[k2]; + if (j3 >= 0) { + if (flag4) { + j3 |= 4; + } else { + j3 &= -5; + } + + world.setData(i3, j, k3, j3, 3); + } + } + } + } + + public void a(World world, int i, int j, int k, Random random) { + this.a(world, i, j, k, false, world.getData(i, j, k), true, -1, 0); + } + + private void a(World world, int i, int j, int k, boolean flag, boolean flag1, boolean flag2, boolean flag3) { + if (flag1 && !flag3) { + world.makeSound((double) i + 0.5D, (double) j + 0.1D, (double) k + 0.5D, "random.click", 0.4F, 0.6F); + } else if (!flag1 && flag3) { + world.makeSound((double) i + 0.5D, (double) j + 0.1D, (double) k + 0.5D, "random.click", 0.4F, 0.5F); + } else if (flag && !flag2) { + world.makeSound((double) i + 0.5D, (double) j + 0.1D, (double) k + 0.5D, "random.click", 0.4F, 0.7F); + } else if (!flag && flag2) { + world.makeSound((double) i + 0.5D, (double) j + 0.1D, (double) k + 0.5D, "random.bowhit", 0.4F, 1.2F / (world.random.nextFloat() * 0.2F + 0.9F)); + } + } + + private void a(World world, int i, int j, int k, int l) { + world.applyPhysics(i, j, k, this); + if (l == 3) { + world.applyPhysics(i - 1, j, k, this); + } else if (l == 1) { + world.applyPhysics(i + 1, j, k, this); + } else if (l == 0) { + world.applyPhysics(i, j, k - 1, this); + } else if (l == 2) { + world.applyPhysics(i, j, k + 1, this); + } + } + + private boolean e(World world, int i, int j, int k) { + if (!this.canPlace(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + return false; + } else { + return true; + } + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + int l = iblockaccess.getData(i, j, k) & 3; + float f = 0.1875F; + + if (l == 3) { + this.a(0.0F, 0.2F, 0.5F - f, f * 2.0F, 0.8F, 0.5F + f); + } else if (l == 1) { + this.a(1.0F - f * 2.0F, 0.2F, 0.5F - f, 1.0F, 0.8F, 0.5F + f); + } else if (l == 0) { + this.a(0.5F - f, 0.2F, 0.0F, 0.5F + f, 0.8F, f * 2.0F); + } else if (l == 2) { + this.a(0.5F - f, 0.2F, 1.0F - f * 2.0F, 0.5F + f, 0.8F, 1.0F); + } + } + + public void remove(World world, int i, int j, int k, Block block, int l) { + boolean flag = (l & 4) == 4; + boolean flag1 = (l & 8) == 8; + + if (flag || flag1) { + this.a(world, i, j, k, true, l, false, -1, 0); + } + + if (flag1) { + world.applyPhysics(i, j, k, this); + int i1 = l & 3; + + if (i1 == 3) { + world.applyPhysics(i - 1, j, k, this); + } else if (i1 == 1) { + world.applyPhysics(i + 1, j, k, this); + } else if (i1 == 0) { + world.applyPhysics(i, j, k - 1, this); + } else if (i1 == 2) { + world.applyPhysics(i, j, k + 1, this); + } + } + + super.remove(world, i, j, k, block, l); + } + + public int b(IBlockAccess iblockaccess, int i, int j, int k, int l) { + return (iblockaccess.getData(i, j, k) & 8) == 8 ? 15 : 0; + } + + public int c(IBlockAccess iblockaccess, int i, int j, int k, int l) { + int i1 = iblockaccess.getData(i, j, k); + + if ((i1 & 8) != 8) { + return 0; + } else { + int j1 = i1 & 3; + + return j1 == 2 && l == 2 ? 15 : (j1 == 0 && l == 3 ? 15 : (j1 == 1 && l == 4 ? 15 : (j1 == 3 && l == 5 ? 15 : 0))); + } + } + + public boolean isPowerSource() { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockVine.java b/vspigot-server/src/main/java/net/minecraft/server/BlockVine.java new file mode 100644 index 0000000..ed00714 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockVine.java @@ -0,0 +1,304 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockVine extends Block { + + public BlockVine() { + super(Material.REPLACEABLE_PLANT); + this.a(true); + this.a(CreativeModeTab.c); + } + + public void g() { + this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); + } + + public int b() { + return 20; + } + + public boolean c() { + return false; + } + + public boolean d() { + return false; + } + + public void updateShape(IBlockAccess iblockaccess, int i, int j, int k) { + float f = 0.0625F; + int l = iblockaccess.getData(i, j, k); + float f1 = 1.0F; + float f2 = 1.0F; + float f3 = 1.0F; + float f4 = 0.0F; + float f5 = 0.0F; + float f6 = 0.0F; + boolean flag = l > 0; + + if ((l & 2) != 0) { + f4 = Math.max(f4, 0.0625F); + f1 = 0.0F; + f2 = 0.0F; + f5 = 1.0F; + f3 = 0.0F; + f6 = 1.0F; + flag = true; + } + + if ((l & 8) != 0) { + f1 = Math.min(f1, 0.9375F); + f4 = 1.0F; + f2 = 0.0F; + f5 = 1.0F; + f3 = 0.0F; + f6 = 1.0F; + flag = true; + } + + if ((l & 4) != 0) { + f6 = Math.max(f6, 0.0625F); + f3 = 0.0F; + f1 = 0.0F; + f4 = 1.0F; + f2 = 0.0F; + f5 = 1.0F; + flag = true; + } + + if ((l & 1) != 0) { + f3 = Math.min(f3, 0.9375F); + f6 = 1.0F; + f1 = 0.0F; + f4 = 1.0F; + f2 = 0.0F; + f5 = 1.0F; + flag = true; + } + + if (!flag && this.a(iblockaccess.getType(i, j + 1, k))) { + f2 = Math.min(f2, 0.9375F); + f5 = 1.0F; + f1 = 0.0F; + f4 = 1.0F; + f3 = 0.0F; + f6 = 1.0F; + } + + this.a(f1, f2, f3, f4, f5, f6); + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public boolean canPlace(World world, int i, int j, int k, int l) { + switch (l) { + case 1: + return this.a(world.getType(i, j + 1, k)); + + case 2: + return this.a(world.getType(i, j, k + 1)); + + case 3: + return this.a(world.getType(i, j, k - 1)); + + case 4: + return this.a(world.getType(i + 1, j, k)); + + case 5: + return this.a(world.getType(i - 1, j, k)); + + default: + return false; + } + } + + private boolean a(Block block) { + return block.d() && block.material.isSolid(); + } + + private boolean e(World world, int i, int j, int k) { + int l = world.getData(i, j, k); + int i1 = l; + + if (l > 0) { + for (int j1 = 0; j1 <= 3; ++j1) { + int k1 = 1 << j1; + + if ((l & k1) != 0 && !this.a(world.getType(i + Direction.a[j1], j, k + Direction.b[j1])) && (world.getType(i, j + 1, k) != this || (world.getData(i, j + 1, k) & k1) == 0)) { + i1 &= ~k1; + } + } + } + + if (i1 == 0 && !this.a(world.getType(i, j + 1, k))) { + return false; + } else { + if (i1 != l) { + world.setData(i, j, k, i1, 2); + } + + return true; + } + } + + public void doPhysics(World world, int i, int j, int k, Block block) { + if (!world.isStatic && !this.e(world, i, j, k)) { + this.b(world, i, j, k, world.getData(i, j, k), 0); + world.setAir(i, j, k); + } + } + + public void a(World world, int i, int j, int k, Random random) { + if (!world.isStatic && world.random.nextInt(4) == 0) { + byte b0 = 4; + int l = 5; + boolean flag = false; + + int i1; + int j1; + int k1; + + label134: + for (i1 = i - b0; i1 <= i + b0; ++i1) { + for (j1 = k - b0; j1 <= k + b0; ++j1) { + for (k1 = j - 1; k1 <= j + 1; ++k1) { + if (world.getType(i1, k1, j1) == this) { + --l; + if (l <= 0) { + flag = true; + break label134; + } + } + } + } + } + + i1 = world.getData(i, j, k); + j1 = world.random.nextInt(6); + k1 = Direction.e[j1]; + int l1; + + if (j1 == 1 && j < 255 && world.isEmpty(i, j + 1, k)) { + if (flag) { + return; + } + + int i2 = world.random.nextInt(16) & i1; + + if (i2 > 0) { + for (l1 = 0; l1 <= 3; ++l1) { + if (!this.a(world.getType(i + Direction.a[l1], j + 1, k + Direction.b[l1]))) { + i2 &= ~(1 << l1); + } + } + + if (i2 > 0) { + // CraftBukkit start - Call BlockSpreadEvent + org.bukkit.block.Block source = world.getWorld().getBlockAt(i, j, k); + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j + 1, k); + CraftEventFactory.handleBlockSpreadEvent(block, source, this, l1); + // CraftBukkit end + } + } + } else { + Block block; + int j2; + + if (j1 >= 2 && j1 <= 5 && (i1 & 1 << k1) == 0) { + if (flag) { + return; + } + + block = world.getType(i + Direction.a[k1], j, k + Direction.b[k1]); + if (block.material == Material.AIR) { + l1 = k1 + 1 & 3; + j2 = k1 + 3 & 3; + + // CraftBukkit start - Call BlockSpreadEvent + org.bukkit.block.Block source = world.getWorld().getBlockAt(i, j, k); + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(i + Direction.a[k1], j, k + Direction.b[k1]); + if ((i1 & 1 << l1) != 0 && this.a(world.getType(i + Direction.a[k1] + Direction.a[l1], j, k + Direction.b[k1] + Direction.b[l1]))) { + CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << l1); + } else if ((i1 & 1 << j2) != 0 && this.a(world.getType(i + Direction.a[k1] + Direction.a[j2], j, k + Direction.b[k1] + Direction.b[j2]))) { + CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << j2); + } else if ((i1 & 1 << l1) != 0 && world.isEmpty(i + Direction.a[k1] + Direction.a[l1], j, k + Direction.b[k1] + Direction.b[l1]) && this.a(world.getType(i + Direction.a[l1], j, k + Direction.b[l1]))) { + bukkitBlock = world.getWorld().getBlockAt(i + Direction.a[k1] + Direction.a[l1], j, k + Direction.b[k1] + Direction.b[l1]); + CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << (k1 + 2 & 3)); + } else if ((i1 & 1 << j2) != 0 && world.isEmpty(i + Direction.a[k1] + Direction.a[j2], j, k + Direction.b[k1] + Direction.b[j2]) && this.a(world.getType(i + Direction.a[j2], j, k + Direction.b[j2]))) { + bukkitBlock = world.getWorld().getBlockAt(i + Direction.a[k1] + Direction.a[j2], j, k + Direction.b[k1] + Direction.b[j2]); + CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << (k1 + 2 & 3)); + } else if (this.a(world.getType(i + Direction.a[k1], j + 1, k + Direction.b[k1]))) { + CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 0); + } + // CraftBukkit end + } else if (block.material.k() && block.d()) { + world.setData(i, j, k, i1 | 1 << k1, 2); + } + } else if (j > 1) { + block = world.getType(i, j - 1, k); + if (block.material == Material.AIR) { + l1 = world.random.nextInt(16) & i1; + if (l1 > 0) { + // CraftBukkit start - Call BlockSpreadEvent + org.bukkit.block.Block source = world.getWorld().getBlockAt(i, j, k); + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(i, j - 1, k); + CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, l1); + // CraftBukkit end + } + } else if (block == this) { + l1 = world.random.nextInt(16) & i1; + j2 = world.getData(i, j - 1, k); + if (j2 != (j2 | l1)) { + world.setData(i, j - 1, k, j2 | l1, 2); + } + } + } + } + } + } + + public int getPlacedData(World world, int i, int j, int k, int l, float f, float f1, float f2, int i1) { + byte b0 = 0; + + switch (l) { + case 2: + b0 = 1; + break; + + case 3: + b0 = 4; + break; + + case 4: + b0 = 8; + break; + + case 5: + b0 = 2; + } + + return b0 != 0 ? b0 : i1; + } + + public Item getDropType(int i, Random random, int j) { + return null; + } + + public int a(Random random) { + return 0; + } + + public void a(World world, EntityHuman entityhuman, int i, int j, int k, int l) { + if (!world.isStatic && entityhuman.bF() != null && entityhuman.bF().getItem() == Items.SHEARS) { + entityhuman.a(StatisticList.MINE_BLOCK_COUNT[Block.getId(this)], 1); + this.a(world, i, j, k, new ItemStack(Blocks.VINE, 1, 0)); + } else { + super.a(world, entityhuman, i, j, k, l); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/BlockWeb.java b/vspigot-server/src/main/java/net/minecraft/server/BlockWeb.java new file mode 100644 index 0000000..ab88573 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/BlockWeb.java @@ -0,0 +1,40 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockWeb extends Block { + + public BlockWeb() { + super(Material.WEB); + this.a(CreativeModeTab.c); + this.a(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F); // Velt + } + + public void a(World world, int i, int j, int k, Entity entity) { + entity.as(); + } + + public boolean c() { + return false; + } + + public AxisAlignedBB a(World world, int i, int j, int k) { + return null; + } + + public int b() { + return 1; + } + + public boolean d() { + return false; + } + + public Item getDropType(int i, Random random, int j) { + return Items.STRING; + } + + protected boolean E() { + return true; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/Chunk.java b/vspigot-server/src/main/java/net/minecraft/server/Chunk.java new file mode 100644 index 0000000..a192194 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Chunk.java @@ -0,0 +1,1265 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicInteger; // PaperSpigot + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; // CraftBukkit +import org.spigotmc.SpigotConfig; + +public class Chunk { + + private static final Logger t = LogManager.getLogger(); + public static boolean a; + private ChunkSection[] sections; + private byte[] v; + public int[] b; + public boolean[] c; + public boolean d; + public World world; + public int[] heightMap; + public final int locX; + public final int locZ; + private boolean w; + public Map tileEntities; // MineHQ - added generic + public List[] entitySlices; + public boolean done; + public boolean lit; + public boolean m; + public boolean n; + public boolean o; + public long lastSaved; + public boolean q; + public int r; + public long s; + private int x; + protected net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap entityCount = new net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap(); // Spigot + // PaperSpigot start - Asynchronous light updates + public AtomicInteger pendingLightUpdates = new AtomicInteger(); + public long lightUpdateTime; + // PaperSpigot end + + // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking + private int neighbors = 0x1 << 12; + + public boolean areNeighborsLoaded(final int radius) { + switch(radius) { + case 2: + return this.neighbors == Integer.MAX_VALUE >> 6; + case 1: + final int mask = + // x z offset x z offset x z offset + ( 0x1 << (1 * 5 + 1 + 12) ) | ( 0x1 << (0 * 5 + 1 + 12) ) | ( 0x1 << (-1 * 5 + 1 + 12) ) | + ( 0x1 << (1 * 5 + 0 + 12) ) | ( 0x1 << (0 * 5 + 0 + 12) ) | ( 0x1 << (-1 * 5 + 0 + 12) ) | + ( 0x1 << (1 * 5 + -1 + 12) ) | ( 0x1 << (0 * 5 + -1 + 12) ) | ( 0x1 << (-1 * 5 + -1 + 12) ); + return (this.neighbors & mask) == mask; + default: + throw new UnsupportedOperationException(String.valueOf(radius)); + } + } + + public void setNeighborLoaded(final int x, final int z) { + this.neighbors |= 0x1 << (x * 5 + 12 + z); + } + + public void setNeighborUnloaded(final int x, final int z) { + this.neighbors &= ~(0x1 << (x * 5 + 12 + z)); + } + // CraftBukkit end + + // MineHQ start + private ChunkMap chunkMap17; + private ChunkMap chunkMap18; + private int emptySectionBits; + + public ChunkMap getChunkMap(boolean groundUpContinuous, int primaryBitMask, int version) { + if (!SpigotConfig.cacheChunkMaps || !groundUpContinuous || (primaryBitMask != 0 && primaryBitMask != '\uffff')) { + return PacketPlayOutMapChunk.a(this, groundUpContinuous, primaryBitMask, version); + } + + if (primaryBitMask == 0) { + ChunkMap chunkMap = new ChunkMap(); + chunkMap.a = new byte[0]; + return chunkMap; + } + + boolean isDirty = false; + for (int i = 0; i < sections.length; ++i) { + ChunkSection section = sections[i]; + if (section == null) { + if ((emptySectionBits & (1 << i)) == 0) { + isDirty = true; + emptySectionBits |= (1 << i); + } + } else { + if ((emptySectionBits & (1 << i)) == 1) { + isDirty = true; + emptySectionBits &= ~(1 << i); + section.isDirty = false; + } else if (section.isDirty) { + isDirty = true; + section.isDirty = false; + } + } + } + + if (isDirty) { + chunkMap17 = null; + chunkMap18 = null; + } + + if (version < 24) { + if (chunkMap17 == null) { + chunkMap17 = PacketPlayOutMapChunk.a(this, true, '\uffff', version); + } + + return chunkMap17; + } else { + if (chunkMap18 == null) { + chunkMap18 = PacketPlayOutMapChunk.a(this, true, '\uffff', version); + } + + return chunkMap18; + } + } + // MineHQ end + + public Chunk(World world, int i, int j) { + this.sections = new ChunkSection[16]; + this.v = new byte[256]; + this.b = new int[256]; + this.c = new boolean[256]; + this.tileEntities = new HashMap(); + this.x = 4096; + this.entitySlices = new List[16]; + this.world = world; + this.locX = i; + this.locZ = j; + this.heightMap = new int[256]; + + for (int k = 0; k < this.entitySlices.length; ++k) { + this.entitySlices[k] = new org.bukkit.craftbukkit.util.UnsafeList(); // CraftBukkit - ArrayList -> UnsafeList + } + + Arrays.fill(this.b, -999); + Arrays.fill(this.v, (byte) -1); + + // CraftBukkit start + if (!(this instanceof EmptyChunk)) { + this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); + } + } + + public org.bukkit.Chunk bukkitChunk; + public boolean mustSave; + // CraftBukkit end + + public Chunk(World world, Block[] ablock, int i, int j) { + this(world, i, j); + int k = ablock.length / 256; + boolean flag = !world.worldProvider.g; + + for (int l = 0; l < 16; ++l) { + for (int i1 = 0; i1 < 16; ++i1) { + for (int j1 = 0; j1 < k; ++j1) { + Block block = ablock[l << 11 | i1 << 7 | j1]; + + if (block != null && block.getMaterial() != Material.AIR) { + int k1 = j1 >> 4; + + if (this.sections[k1] == null) { + this.sections[k1] = new ChunkSection(k1 << 4, flag); + } + + this.sections[k1].setTypeId(l, j1 & 15, i1, block); + } + } + } + } + } + + public Chunk(World world, Block[] ablock, byte[] abyte, int i, int j) { + this(world, i, j); + int k = ablock.length / 256; + boolean flag = !world.worldProvider.g; + + for (int l = 0; l < 16; ++l) { + for (int i1 = 0; i1 < 16; ++i1) { + for (int j1 = 0; j1 < k; ++j1) { + int k1 = l * k * 16 | i1 * k | j1; + Block block = ablock[k1]; + + if (block != null && block != Blocks.AIR) { + int l1 = j1 >> 4; + + if (this.sections[l1] == null) { + this.sections[l1] = new ChunkSection(l1 << 4, flag); + } + + this.sections[l1].setTypeId(l, j1 & 15, i1, block); + this.sections[l1].setData(l, j1 & 15, i1, checkData( block, abyte[k1] ) ); + } + } + } + } + } + + public boolean a(int i, int j) { + return i == this.locX && j == this.locZ; + } + + public int b(int i, int j) { + return this.heightMap[j << 4 | i]; + } + + public int h() { + for (int i = this.sections.length - 1; i >= 0; --i) { + if (this.sections[i] != null) { + return this.sections[i].getYPosition(); + } + } + + return 0; + } + + public ChunkSection[] getSections() { + return this.sections; + } + + public void initLighting() { + int i = this.h(); + + this.r = Integer.MAX_VALUE; + + for (int j = 0; j < 16; ++j) { + int k = 0; + + while (k < 16) { + this.b[j + (k << 4)] = -999; + int l = i + 16 - 1; + + while (true) { + if (l > 0) { + if (this.b(j, l - 1, k) == 0) { + --l; + continue; + } + + this.heightMap[k << 4 | j] = l; + if (l < this.r) { + this.r = l; + } + } + + if (!this.world.worldProvider.g) { + l = 15; + int i1 = i + 16 - 1; + + do { + int j1 = this.b(j, i1, k); + + if (j1 == 0 && l != 15) { + j1 = 1; + } + + l -= j1; + if (l > 0) { + ChunkSection chunksection = this.sections[i1 >> 4]; + + if (chunksection != null) { + chunksection.setSkyLight(j, i1 & 15, k, l); + this.world.m((this.locX << 4) + j, i1, (this.locZ << 4) + k); + } + } + + --i1; + } while (i1 > 0 && l > 0); + } + + ++k; + break; + } + } + } + + this.n = true; + } + + private void e(int i, int j) { + this.c[i + j * 16] = true; + this.w = true; + } + + private void c(boolean flag) { + this.world.methodProfiler.a("recheckGaps"); + if (this.areNeighborsLoaded(1)) { // Poweruser + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 16; ++j) { + if (this.c[i + j * 16]) { + this.c[i + j * 16] = false; + int k = this.b(i, j); + int l = this.locX * 16 + i; + int i1 = this.locZ * 16 + j; + // Poweruser start - pass that chunks have already been checked if they are loaded + int j1 = this.world.g(l - 1, i1, true); + int k1 = this.world.g(l + 1, i1, true); + int l1 = this.world.g(l, i1 - 1, true); + int i2 = this.world.g(l, i1 + 1, true); + // Poweruser end + + if (k1 < j1) { + j1 = k1; + } + + if (l1 < j1) { + j1 = l1; + } + + if (i2 < j1) { + j1 = i2; + } + + // Poweruser start - pass that chunks have already been checked if they are loaded + this.g(l, i1, j1, true); + this.g(l - 1, i1, k, true); + this.g(l + 1, i1, k, true); + this.g(l, i1 - 1, k, true); + this.g(l, i1 + 1, k, true); + // Poweruser end + if (flag) { + this.world.methodProfiler.b(); + return; + } + } + } + } + + this.w = false; + } + + this.world.methodProfiler.b(); + } + + private void g(int i, int j, int k) { + // Poweruser start + this.g(i, j, k, false); + } + + private void g(int i, int j, int k, boolean chunksHaveAlreadyBeenChecked) { + int l = this.world.getHighestBlockYAt(i, j, chunksHaveAlreadyBeenChecked); + // Poweruser end + + if (l > k) { + this.c(i, j, k, l + 1); + } else if (l < k) { + this.c(i, j, l, k + 1); + } + } + + private void c(int i, int j, int k, int l) { + // Poweruser start + if (l > k) { + Chunk chunk = this.world.getChunkIfLoaded(i >> 4, j >> 4); + if(chunk == null || !chunk.areNeighborsLoaded(1)) { + return; + } + // Poweruser end + for (int i1 = k; i1 < l; ++i1) { + this.world.updateLight(EnumSkyBlock.SKY, i, i1, j); // PaperSpigot - Asynchronous lighting updates + } + + this.n = true; + } + } + + private void h(int i, int j, int k) { + int l = this.heightMap[k << 4 | i] & 255; + int i1 = l; + + if (j > l) { + i1 = j; + } + + while (i1 > 0 && this.b(i, i1 - 1, k) == 0) { + --i1; + } + + if (i1 != l) { + this.world.b(i + this.locX * 16, k + this.locZ * 16, i1, l); + this.heightMap[k << 4 | i] = i1; + int j1 = this.locX * 16 + i; + int k1 = this.locZ * 16 + k; + int l1; + int i2; + + if (!this.world.worldProvider.g) { + ChunkSection chunksection; + + if (i1 < l) { + for (l1 = i1; l1 < l; ++l1) { + chunksection = this.sections[l1 >> 4]; + if (chunksection != null) { + chunksection.setSkyLight(i, l1 & 15, k, 15); + this.world.m((this.locX << 4) + i, l1, (this.locZ << 4) + k); + } + } + } else { + for (l1 = l; l1 < i1; ++l1) { + chunksection = this.sections[l1 >> 4]; + if (chunksection != null) { + chunksection.setSkyLight(i, l1 & 15, k, 0); + this.world.m((this.locX << 4) + i, l1, (this.locZ << 4) + k); + } + } + } + + l1 = 15; + + while (i1 > 0 && l1 > 0) { + --i1; + i2 = this.b(i, i1, k); + if (i2 == 0) { + i2 = 1; + } + + l1 -= i2; + if (l1 < 0) { + l1 = 0; + } + + ChunkSection chunksection1 = this.sections[i1 >> 4]; + + if (chunksection1 != null) { + chunksection1.setSkyLight(i, i1 & 15, k, l1); + } + } + } + + l1 = this.heightMap[k << 4 | i]; + i2 = l; + int j2 = l1; + + if (l1 < l) { + i2 = l1; + j2 = l; + } + + if (l1 < this.r) { + this.r = l1; + } + + if (!this.world.worldProvider.g) { + this.c(j1 - 1, k1, i2, j2); + this.c(j1 + 1, k1, i2, j2); + this.c(j1, k1 - 1, i2, j2); + this.c(j1, k1 + 1, i2, j2); + this.c(j1, k1, i2, j2); + } + + this.n = true; + } + } + + public int b(int i, int j, int k) { + return this.getType(i, j, k).k(); + } + + public Block getType(int i, int j, int k) { + Block block = Blocks.AIR; + + if (j >> 4 < this.sections.length) { + ChunkSection chunksection = this.sections[j >> 4]; + + if (chunksection != null) { + try { + block = chunksection.getTypeId(i, j & 15, k); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Getting block"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being got"); + + crashreportsystemdetails.a("Location", (Callable) (new CrashReportLocation(this, i, j, k))); + throw new ReportedException(crashreport); + } + } + } + + return block; + } + + public int getData(int i, int j, int k) { + if (j >> 4 >= this.sections.length) { + return 0; + } else { + ChunkSection chunksection = this.sections[j >> 4]; + + return chunksection != null ? chunksection.getData(i, j & 15, k) : 0; + } + } + + // Spigot start - prevent invalid data values + public static int checkData( Block block, int data ) + { + if (block == Blocks.DOUBLE_PLANT ) + { + return data < 6 || data >= 8 ? data : 0; + } + return data; + } + // Spigot end + + public boolean a(int i, int j, int k, Block block, int l) { + int i1 = k << 4 | i; + + if (j >= this.b[i1] - 1) { + this.b[i1] = -999; + } + + int j1 = this.heightMap[i1]; + Block block1 = this.getType(i, j, k); + int k1 = this.getData(i, j, k); + + if (block1 == block && k1 == l) { + return false; + } else { + ChunkSection chunksection = this.sections[j >> 4]; + boolean flag = false; + + if (chunksection == null) { + if (block == Blocks.AIR) { + return false; + } + + chunksection = this.sections[j >> 4] = new ChunkSection(j >> 4 << 4, !this.world.worldProvider.g); + flag = j >= j1; + } + + int l1 = this.locX * 16 + i; + int i2 = this.locZ * 16 + k; + + if (!this.world.isStatic) { + block1.f(this.world, l1, j, i2, k1); + } + + // CraftBukkit start - Delay removing containers until after they're cleaned up + if (!(block1 instanceof IContainer)) { + chunksection.setTypeId(i, j & 15, k, block); + } + // CraftBukkit end + + if (!this.world.isStatic) { + block1.remove(this.world, l1, j, i2, block1, k1); + } else if (block1 instanceof IContainer && block1 != block) { + this.world.p(l1, j, i2); + } + + // CraftBukkit start - Remove containers now after cleanup + if (block1 instanceof IContainer) { + chunksection.setTypeId(i, j & 15, k, block); + } + // CraftBukkit end + + if (chunksection.getTypeId(i, j & 15, k) != block) { + return false; + } else { + chunksection.setData(i, j & 15, k, checkData( block, l ) ); + if (flag) { + this.initLighting(); + } else { + int j2 = block.k(); + int k2 = block1.k(); + + if (j2 > 0) { + if (j >= j1) { + this.h(i, j + 1, k); + } + } else if (j == j1 - 1) { + this.h(i, j, k); + } + + if (j2 != k2 && (j2 < k2 || this.getBrightness(EnumSkyBlock.SKY, i, j, k) > 0 || this.getBrightness(EnumSkyBlock.BLOCK, i, j, k) > 0)) { + this.e(i, k); + } + } + + TileEntity tileentity; + + if (block1 instanceof IContainer) { + tileentity = this.e(i, j, k); + if (tileentity != null) { + tileentity.u(); + } + } + + // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. + if (!this.world.isStatic && (!this.world.captureBlockStates || block instanceof BlockContainer)) { + block.onPlace(this.world, l1, j, i2); + } + + if (block instanceof IContainer) { + + tileentity = this.e(i, j, k); + if (tileentity == null) { + tileentity = ((IContainer) block).a(this.world, l); + this.world.setTileEntity(l1, j, i2, tileentity); + } + + if (tileentity != null) { + tileentity.u(); + } + } + + this.n = true; + return true; + } + } + } + + public boolean a(int i, int j, int k, int l) { + ChunkSection chunksection = this.sections[j >> 4]; + + if (chunksection == null) { + return false; + } else { + int i1 = chunksection.getData(i, j & 15, k); + + if (i1 == l) { + return false; + } else { + this.n = true; + Block block = chunksection.getTypeId( i, j & 15, k ); + chunksection.setData(i, j & 15, k, checkData( block, l ) ); + if (block instanceof IContainer) { + TileEntity tileentity = this.e(i, j, k); + + if (tileentity != null) { + tileentity.u(); + tileentity.g = l; + } + } + + return true; + } + } + } + + public int getBrightness(EnumSkyBlock enumskyblock, int i, int j, int k) { + ChunkSection chunksection = this.sections[j >> 4]; + + return chunksection == null ? (this.d(i, j, k) ? enumskyblock.c : 0) : (enumskyblock == EnumSkyBlock.SKY ? (this.world.worldProvider.g ? 0 : chunksection.getSkyLight(i, j & 15, k)) : (enumskyblock == EnumSkyBlock.BLOCK ? chunksection.getEmittedLight(i, j & 15, k) : enumskyblock.c)); + } + + public void a(EnumSkyBlock enumskyblock, int i, int j, int k, int l) { + ChunkSection chunksection = this.sections[j >> 4]; + + if (chunksection == null) { + chunksection = this.sections[j >> 4] = new ChunkSection(j >> 4 << 4, !this.world.worldProvider.g); + this.initLighting(); + } + + this.n = true; + if (enumskyblock == EnumSkyBlock.SKY) { + if (!this.world.worldProvider.g) { + chunksection.setSkyLight(i, j & 15, k, l); + } + } else if (enumskyblock == EnumSkyBlock.BLOCK) { + chunksection.setEmittedLight(i, j & 15, k, l); + } + } + + public int b(int i, int j, int k, int l) { + ChunkSection chunksection = this.sections[j >> 4]; + + if (chunksection == null) { + return !this.world.worldProvider.g && l < EnumSkyBlock.SKY.c ? EnumSkyBlock.SKY.c - l : 0; + } else { + int i1 = this.world.worldProvider.g ? 0 : chunksection.getSkyLight(i, j & 15, k); + + if (i1 > 0) { + a = true; + } + + i1 -= l; + int j1 = chunksection.getEmittedLight(i, j & 15, k); + + if (j1 > i1) { + i1 = j1; + } + + return i1; + } + } + + public void a(Entity entity) { + this.o = true; + int i = MathHelper.floor(entity.locX / 16.0D); + int j = MathHelper.floor(entity.locZ / 16.0D); + + if (i != this.locX || j != this.locZ) { + // CraftBukkit start + Bukkit.getLogger().warning("Wrong location for " + entity + " in world '" + world.getWorld().getName() + "'!"); + // t.warn("Wrong location! " + entity + " (at " + i + ", " + j + " instead of " + this.locX + ", " + this.locZ + ")"); + // Thread.dumpStack(); + Bukkit.getLogger().warning("Entity is at " + entity.locX + "," + entity.locZ + " (chunk " + i + "," + j + ") but was stored in chunk " + this.locX + "," + this.locZ); + // CraftBukkit end + } + + int k = MathHelper.floor(entity.locY / 16.0D); + + if (k < 0) { + k = 0; + } + + if (k >= this.entitySlices.length) { + k = this.entitySlices.length - 1; + } + + entity.ag = true; + entity.ah = this.locX; + entity.ai = k; + entity.aj = this.locZ; + this.entitySlices[k].add(entity); + // Spigot start - increment creature type count + // Keep this synced up with World.a(Class) + if (entity instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) { + return; + } + } + for ( EnumCreatureType creatureType : EnumCreatureType.values() ) + { + if ( creatureType.a().isAssignableFrom( entity.getClass() ) ) + { + this.entityCount.adjustOrPutValue( creatureType.a(), 1, 1 ); + } + } + // Spigot end + } + + public void b(Entity entity) { + this.a(entity, entity.ai); + } + + public void a(Entity entity, int i) { + if (i < 0) { + i = 0; + } + + if (i >= this.entitySlices.length) { + i = this.entitySlices.length - 1; + } + + this.entitySlices[i].remove(entity); + // Spigot start - decrement creature type count + // Keep this synced up with World.a(Class) + if (entity instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) { + return; + } + } + for ( EnumCreatureType creatureType : EnumCreatureType.values() ) + { + if ( creatureType.a().isAssignableFrom( entity.getClass() ) ) + { + this.entityCount.adjustValue( creatureType.a(), -1 ); + } + } + // Spigot end + } + + public boolean d(int i, int j, int k) { + return j >= this.heightMap[k << 4 | i]; + } + + public TileEntity e(int i, int j, int k) { + ChunkPosition chunkposition = new ChunkPosition(i, j, k); + TileEntity tileentity = (TileEntity) this.tileEntities.get(chunkposition); + + if (tileentity == null) { + Block block = this.getType(i, j, k); + + if (!block.isTileEntity()) { + return null; + } + + tileentity = ((IContainer) block).a(this.world, this.getData(i, j, k)); + this.world.setTileEntity(this.locX * 16 + i, j, this.locZ * 16 + k, tileentity); + } + + if (tileentity != null && tileentity.r()) { + this.tileEntities.remove(chunkposition); + return null; + } else { + return tileentity; + } + } + + public void a(TileEntity tileentity) { + int i = tileentity.x - this.locX * 16; + int j = tileentity.y; + int k = tileentity.z - this.locZ * 16; + + this.a(i, j, k, tileentity); + if (this.d) { + this.world.tileEntityList.add(tileentity); + } + } + + public void a(int i, int j, int k, TileEntity tileentity) { + ChunkPosition chunkposition = new ChunkPosition(i, j, k); + + tileentity.a(this.world); + tileentity.x = this.locX * 16 + i; + tileentity.y = j; + tileentity.z = this.locZ * 16 + k; + if (this.getType(i, j, k) instanceof IContainer) { + if (this.tileEntities.containsKey(chunkposition)) { + ((TileEntity) this.tileEntities.get(chunkposition)).s(); + } + + tileentity.t(); + this.tileEntities.put(chunkposition, tileentity); + // Spigot start - The tile entity has a world, now hoppers can be born ticking. + if (this.world.spigotConfig.altHopperTicking) { + this.world.triggerHoppersList.add(tileentity); + } + // Spigot end + // PaperSpigot start - Remove invalid mob spawner Tile Entities + } else if (this.world.paperSpigotConfig.removeInvalidMobSpawnerTEs && tileentity instanceof TileEntityMobSpawner && + org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(getType(i, j, k)) != org.bukkit.Material.MOB_SPAWNER) { + this.tileEntities.remove(chunkposition); + // PaperSpigot end + // CraftBukkit start + } else { + System.out.println("Attempted to place a tile entity (" + tileentity + ") at " + tileentity.x + "," + tileentity.y + "," + tileentity.z + + " (" + org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(getType(i, j, k)) + ") where there was no entity tile!"); + System.out.println("Chunk coordinates: " + (this.locX * 16) + "," + (this.locZ * 16)); + new Exception().printStackTrace(); + // CraftBukkit end + } + } + + public void f(int i, int j, int k) { + ChunkPosition chunkposition = new ChunkPosition(i, j, k); + + if (this.d) { + TileEntity tileentity = (TileEntity) this.tileEntities.remove(chunkposition); + + if (tileentity != null) { + tileentity.s(); + } + } + } + + public void addEntities() { + this.d = true; + this.world.a(this.tileEntities.values()); + + for (int i = 0; i < this.entitySlices.length; ++i) { + Iterator iterator = this.entitySlices[i].iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + entity.X(); + } + + this.world.a(this.entitySlices[i]); + } + } + + public void removeEntities() { + this.d = false; + Iterator iterator = this.tileEntities.values().iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + // Spigot Start + if ( tileentity instanceof IInventory ) + { + for ( org.bukkit.entity.HumanEntity h : new ArrayList( (List) ( (IInventory) tileentity ).getViewers() ) ) + { + if ( h instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity ) + { + ( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory(); + } + } + } + // Spigot End + + this.world.a(tileentity); + } + + for (int i = 0; i < this.entitySlices.length; ++i) { + // CraftBukkit start + java.util.Iterator iter = this.entitySlices[i].iterator(); + while (iter.hasNext()) { + Entity entity = (Entity) iter.next(); + // Spigot Start + if ( entity instanceof IInventory ) + { + for ( org.bukkit.entity.HumanEntity h : new ArrayList( (List) ( (IInventory) entity ).getViewers() ) ) + { + if ( h instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity ) + { + ( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory(); + } + } + } + // Spigot End + + // Do not pass along players, as doing so can get them stuck outside of time. + // (which for example disables inventory icon updates and prevents block breaking) + if (entity instanceof EntityPlayer) { + iter.remove(); + } + } + // CraftBukkit end + + this.world.b(this.entitySlices[i]); + } + } + + public void e() { + this.n = true; + } + + public void a(Entity entity, AxisAlignedBB axisalignedbb, List list, IEntitySelector ientityselector) { + int i = MathHelper.floor((axisalignedbb.b - 2.0D) / 16.0D); + int j = MathHelper.floor((axisalignedbb.e + 2.0D) / 16.0D); + + i = MathHelper.a(i, 0, this.entitySlices.length - 1); + j = MathHelper.a(j, 0, this.entitySlices.length - 1); + + for (int k = i; k <= j; ++k) { + List list1 = this.entitySlices[k]; + + for (int l = 0; l < list1.size(); ++l) { + Entity entity1 = (Entity) list1.get(l); + + if (entity1 != entity && entity1.boundingBox.b(axisalignedbb) && (ientityselector == null || ientityselector.a(entity1))) { + list.add(entity1); + Entity[] aentity = entity1.at(); + + if (aentity != null) { + for (int i1 = 0; i1 < aentity.length; ++i1) { + entity1 = aentity[i1]; + if (entity1 != entity && entity1.boundingBox.b(axisalignedbb) && (ientityselector == null || ientityselector.a(entity1))) { + list.add(entity1); + } + } + } + } + } + } + } + + public void a(Class oclass, AxisAlignedBB axisalignedbb, List list, IEntitySelector ientityselector) { + int i = MathHelper.floor((axisalignedbb.b - 2.0D) / 16.0D); + int j = MathHelper.floor((axisalignedbb.e + 2.0D) / 16.0D); + + i = MathHelper.a(i, 0, this.entitySlices.length - 1); + j = MathHelper.a(j, 0, this.entitySlices.length - 1); + + for (int k = i; k <= j; ++k) { + List list1 = this.entitySlices[k]; + + for (int l = 0; l < list1.size(); ++l) { + Entity entity = (Entity) list1.get(l); + + if (oclass.isAssignableFrom(entity.getClass()) && entity.boundingBox.b(axisalignedbb) && (ientityselector == null || ientityselector.a(entity))) { + list.add(entity); + } + } + } + } + + public boolean a(boolean flag) { + if (flag) { + if (this.o && this.world.getTime() != this.lastSaved || this.n) { + return true; + } + // Poweruser start + } else if (this.n && this.world.getAutoSaveWorldData().getLastAutosaveTimeStamp() >= this.lastSaved) { + return true; + } else if (this.o && this.world.getTime() >= this.lastSaved + MinecraftServer.getServer().autosavePeriod) { + return true; + } else { + return false; + } + // Poweruser end + return this.n; + } + + public Random a(long i) { + return new Random(this.world.getSeed() + (long) (this.locX * this.locX * 4987142) + (long) (this.locX * 5947611) + (long) (this.locZ * this.locZ) * 4392871L + (long) (this.locZ * 389711) ^ i); + } + + public boolean isEmpty() { + return false; + } + + public void loadNearby(IChunkProvider ichunkprovider, IChunkProvider ichunkprovider1, int i, int j) { + world.timings.syncChunkLoadPostTimer.startTiming(); // Spigot + if (!this.done && ichunkprovider.isChunkLoaded(i + 1, j + 1) && ichunkprovider.isChunkLoaded(i, j + 1) && ichunkprovider.isChunkLoaded(i + 1, j)) { + ichunkprovider.getChunkAt(ichunkprovider1, i, j); + } + + if (ichunkprovider.isChunkLoaded(i - 1, j) && !ichunkprovider.getOrCreateChunk(i - 1, j).done && ichunkprovider.isChunkLoaded(i - 1, j + 1) && ichunkprovider.isChunkLoaded(i, j + 1) && ichunkprovider.isChunkLoaded(i - 1, j + 1)) { + ichunkprovider.getChunkAt(ichunkprovider1, i - 1, j); + } + + if (ichunkprovider.isChunkLoaded(i, j - 1) && !ichunkprovider.getOrCreateChunk(i, j - 1).done && ichunkprovider.isChunkLoaded(i + 1, j - 1) && ichunkprovider.isChunkLoaded(i + 1, j - 1) && ichunkprovider.isChunkLoaded(i + 1, j)) { + ichunkprovider.getChunkAt(ichunkprovider1, i, j - 1); + } + + if (ichunkprovider.isChunkLoaded(i - 1, j - 1) && !ichunkprovider.getOrCreateChunk(i - 1, j - 1).done && ichunkprovider.isChunkLoaded(i, j - 1) && ichunkprovider.isChunkLoaded(i - 1, j)) { + ichunkprovider.getChunkAt(ichunkprovider1, i - 1, j - 1); + } + world.timings.syncChunkLoadPostTimer.stopTiming(); // Spigot + } + + public int d(int i, int j) { + int k = i | j << 4; + int l = this.b[k]; + + if (l == -999) { + int i1 = this.h() + 15; + + l = -1; + + while (i1 > 0 && l == -1) { + Block block = this.getType(i, i1, j); + Material material = block.getMaterial(); + + if (!material.isSolid() && !material.isLiquid()) { + --i1; + } else { + l = i1 + 1; + } + } + + this.b[k] = l; + } + + return l; + } + + public void b(boolean flag) { + if (this.w && !this.world.worldProvider.g && !flag) { + this.c(this.world.isStatic); + } + + this.m = true; + if (!this.lit && this.done && this.world.spigotConfig.randomLightUpdates) { // Spigot - also use random light updates setting to determine if we should relight + this.p(); + } + } + + public boolean isReady() { + // Spigot Start + /* + * As of 1.7, Mojang added a check to make sure that only chunks which have been lit are sent to the client. + * Unfortunately this interferes with our modified chunk ticking algorithm, which will only tick chunks distant from the player on a very infrequent basis. + * We cannot unfortunately do this lighting stage during chunk gen as it appears to put a lot more noticeable load on the server, than when it is done at play time. + * For now at least we will simply send all chunks, in accordance with pre 1.7 behaviour. + */ + return true; + // Spigot End + } + + public ChunkCoordIntPair l() { + return new ChunkCoordIntPair(this.locX, this.locZ); + } + + public boolean c(int i, int j) { + if (i < 0) { + i = 0; + } + + if (j >= 256) { + j = 255; + } + + for (int k = i; k <= j; k += 16) { + ChunkSection chunksection = this.sections[k >> 4]; + + if (chunksection != null && !chunksection.isEmpty()) { + return false; + } + } + + return true; + } + + public void a(ChunkSection[] achunksection) { + this.sections = achunksection; + } + + public BiomeBase getBiome(int i, int j, WorldChunkManager worldchunkmanager) { + int k = this.v[j << 4 | i] & 255; + + if (k == 255) { + BiomeBase biomebase = worldchunkmanager.getBiome((this.locX << 4) + i, (this.locZ << 4) + j); + + k = biomebase.id; + this.v[j << 4 | i] = (byte) (k & 255); + } + + return BiomeBase.getBiome(k) == null ? BiomeBase.PLAINS : BiomeBase.getBiome(k); + } + + public byte[] m() { + return this.v; + } + + public void a(byte[] abyte) { + this.v = abyte; + } + + public void n() { + this.x = 0; + } + + public void o() { + for (int i = 0; i < 8; ++i) { + if (this.x >= 4096) { + return; + } + + int j = this.x % 16; + int k = this.x / 16 % 16; + int l = this.x / 256; + + ++this.x; + int i1 = (this.locX << 4) + k; + int j1 = (this.locZ << 4) + l; + + for (int k1 = 0; k1 < 16; ++k1) { + int l1 = (j << 4) + k1; + + if (this.sections[j] == null && (k1 == 0 || k1 == 15 || k == 0 || k == 15 || l == 0 || l == 15) || this.sections[j] != null && this.sections[j].getTypeId(k, k1, l).getMaterial() == Material.AIR) { + if (this.world.getType(i1, l1 - 1, j1).m() > 0) { + this.world.t(i1, l1 - 1, j1); + } + + if (this.world.getType(i1, l1 + 1, j1).m() > 0) { + this.world.t(i1, l1 + 1, j1); + } + + if (this.world.getType(i1 - 1, l1, j1).m() > 0) { + this.world.t(i1 - 1, l1, j1); + } + + if (this.world.getType(i1 + 1, l1, j1).m() > 0) { + this.world.t(i1 + 1, l1, j1); + } + + if (this.world.getType(i1, l1, j1 - 1).m() > 0) { + this.world.t(i1, l1, j1 - 1); + } + + if (this.world.getType(i1, l1, j1 + 1).m() > 0) { + this.world.t(i1, l1, j1 + 1); + } + + this.world.t(i1, l1, j1); + } + } + } + } + + public void p() { + this.done = true; + this.lit = true; + if (!this.world.worldProvider.g) { + if (this.world.b(this.locX * 16 - 1, 0, this.locZ * 16 - 1, this.locX * 16 + 1, 63, this.locZ * 16 + 1)) { + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 16; ++j) { + if (!this.f(i, j)) { + this.lit = false; + break; + } + } + } + + if (this.lit) { + Chunk chunk = this.world.getChunkAtWorldCoords(this.locX * 16 - 1, this.locZ * 16); + + chunk.a(3); + chunk = this.world.getChunkAtWorldCoords(this.locX * 16 + 16, this.locZ * 16); + chunk.a(1); + chunk = this.world.getChunkAtWorldCoords(this.locX * 16, this.locZ * 16 - 1); + chunk.a(0); + chunk = this.world.getChunkAtWorldCoords(this.locX * 16, this.locZ * 16 + 16); + chunk.a(2); + } + } else { + this.lit = false; + } + } + } + + private void a(int i) { + if (this.done) { + int j; + + if (i == 3) { + for (j = 0; j < 16; ++j) { + this.f(15, j); + } + } else if (i == 1) { + for (j = 0; j < 16; ++j) { + this.f(0, j); + } + } else if (i == 0) { + for (j = 0; j < 16; ++j) { + this.f(j, 15); + } + } else if (i == 2) { + for (j = 0; j < 16; ++j) { + this.f(j, 0); + } + } + } + } + + private boolean f(int i, int j) { + int k = this.h(); + boolean flag = false; + boolean flag1 = false; + + int l; + + for (l = k + 16 - 1; l > 63 || l > 0 && !flag1; --l) { + int i1 = this.b(i, l, j); + + if (i1 == 255 && l < 63) { + flag1 = true; + } + + if (!flag && i1 > 0) { + flag = true; + } else if (flag && i1 == 0 && !this.world.t(this.locX * 16 + i, l, this.locZ * 16 + j)) { + return false; + } + } + + for (; l > 0; --l) { + if (this.getType(i, l, j).m() > 0) { + this.world.t(this.locX * 16 + i, l, this.locZ * 16 + j); + } + } + + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkCoordIntPair.java new file mode 100644 index 0000000..1594f40 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkCoordIntPair.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +public class ChunkCoordIntPair { + + public final int x; + public final int z; + + public ChunkCoordIntPair(int i, int j) { + this.x = i; + this.z = j; + } + + public static long a(int i, int j) { + return (long) i & 4294967295L | ((long) j & 4294967295L) << 32; + } + + public int hashCode() { + int i = 1664525 * this.x + 1013904223; + int j = 1664525 * (this.z ^ -559038737) + 1013904223; + + return i ^ j; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (!(object instanceof ChunkCoordIntPair)) { + return false; + } else { + ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) object; + + return this.x == chunkcoordintpair.x && this.z == chunkcoordintpair.z; + } + } + + public int a() { + return (this.x << 4) + 8; + } + + public int b() { + return (this.z << 4) + 8; + } + + public ChunkPosition a(int i) { + return new ChunkPosition(this.a(), i, this.b()); + } + + public String toString() { + return "[" + this.x + ", " + this.z + "]"; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkMap.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkMap.java new file mode 100644 index 0000000..374983a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkMap.java @@ -0,0 +1,10 @@ +package net.minecraft.server; + +public class ChunkMap { + + public byte[] a; + public int b; + public int c; + + public ChunkMap() {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderFlat.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderFlat.java new file mode 100644 index 0000000..a0bf8f2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderFlat.java @@ -0,0 +1,238 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class ChunkProviderFlat implements IChunkProvider { + + private World a; + private Random b; + private final Block[] c = new Block[256]; + private final byte[] d = new byte[256]; + private final WorldGenFlatInfo e; + private final List f = new ArrayList(); + private final boolean g; + private final boolean h; + private WorldGenLakes i; + private WorldGenLakes j; + + public ChunkProviderFlat(World world, long i, boolean flag, String s) { + this.a = world; + this.b = new Random(i); + this.e = WorldGenFlatInfo.a(s); + if (flag) { + Map map = this.e.b(); + + if (world.paperSpigotConfig.generateVillage && map.containsKey("village")) { // PaperSpigot + Map map1 = (Map) map.get("village"); + + if (!map1.containsKey("size")) { + map1.put("size", "1"); + } + + this.f.add(new WorldGenVillage(map1)); + } + + if (world.paperSpigotConfig.generateTemple && map.containsKey("biome_1")) { // PaperSpigot + this.f.add(new WorldGenLargeFeature((Map) map.get("biome_1"))); + } + + if (world.paperSpigotConfig.generateMineshaft && map.containsKey("mineshaft")) { // PaperSpigot + this.f.add(new WorldGenMineshaft((Map) map.get("mineshaft"))); + } + + if (world.paperSpigotConfig.generateStronghold && map.containsKey("stronghold")) { // PaperSpigot + this.f.add(new WorldGenStronghold((Map) map.get("stronghold"))); + } + } + + this.g = this.e.b().containsKey("decoration"); + if (this.e.b().containsKey("lake")) { + this.i = new WorldGenLakes(Blocks.STATIONARY_WATER); + } + + if (this.e.b().containsKey("lava_lake")) { + this.j = new WorldGenLakes(Blocks.STATIONARY_LAVA); + } + + this.h = world.paperSpigotConfig.generateDungeon && this.e.b().containsKey("dungeon"); + Iterator iterator = this.e.c().iterator(); + + while (iterator.hasNext()) { + WorldGenFlatLayerInfo worldgenflatlayerinfo = (WorldGenFlatLayerInfo) iterator.next(); + + for (int j = worldgenflatlayerinfo.d(); j < worldgenflatlayerinfo.d() + worldgenflatlayerinfo.a(); ++j) { + this.c[j] = worldgenflatlayerinfo.b(); + this.d[j] = (byte) worldgenflatlayerinfo.c(); + } + } + } + + public Chunk getChunkAt(int i, int j) { + return this.getOrCreateChunk(i, j); + } + + public Chunk getOrCreateChunk(int i, int j) { + Chunk chunk = new Chunk(this.a, i, j); + + int k; + + for (int l = 0; l < this.c.length; ++l) { + Block block = this.c[l]; + + if (block != null) { + k = l >> 4; + ChunkSection chunksection = chunk.getSections()[k]; + + if (chunksection == null) { + chunksection = new ChunkSection(l, !this.a.worldProvider.g); + chunk.getSections()[k] = chunksection; + } + + for (int i1 = 0; i1 < 16; ++i1) { + for (int j1 = 0; j1 < 16; ++j1) { + chunksection.setTypeId(i1, l & 15, j1, block); + chunksection.setData(i1, l & 15, j1, this.d[l]); + } + } + } + } + + chunk.initLighting(); + BiomeBase[] abiomebase = this.a.getWorldChunkManager().getBiomeBlock((BiomeBase[]) null, i * 16, j * 16, 16, 16); + byte[] abyte = chunk.m(); + + for (k = 0; k < abyte.length; ++k) { + abyte[k] = (byte) abiomebase[k].id; + } + + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + WorldGenBase worldgenbase = (WorldGenBase) iterator.next(); + + worldgenbase.a(this, this.a, i, j, (Block[]) null); + } + + chunk.initLighting(); + return chunk; + } + + public boolean isChunkLoaded(int i, int j) { + return true; + } + + public void getChunkAt(IChunkProvider ichunkprovider, int i, int j) { + int k = i * 16; + int l = j * 16; + BiomeBase biomebase = this.a.getBiome(k + 16, l + 16); + boolean flag = false; + + this.b.setSeed(this.a.getSeed()); + long i1 = this.b.nextLong() / 2L * 2L + 1L; + long j1 = this.b.nextLong() / 2L * 2L + 1L; + + this.b.setSeed((long) i * i1 + (long) j * j1 ^ this.a.getSeed()); + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + StructureGenerator structuregenerator = (StructureGenerator) iterator.next(); + boolean flag1 = structuregenerator.a(this.a, this.b, i, j); + + if (structuregenerator instanceof WorldGenVillage) { + flag |= flag1; + } + } + + int k1; + int l1; + int i2; + + if (this.i != null && !flag && this.b.nextInt(4) == 0) { + l1 = k + this.b.nextInt(16) + 8; + k1 = this.b.nextInt(256); + i2 = l + this.b.nextInt(16) + 8; + this.i.generate(this.a, this.b, l1, k1, i2); + } + + if (this.j != null && !flag && this.b.nextInt(8) == 0) { + l1 = k + this.b.nextInt(16) + 8; + k1 = this.b.nextInt(this.b.nextInt(248) + 8); + i2 = l + this.b.nextInt(16) + 8; + if (k1 < 63 || this.b.nextInt(10) == 0) { + this.j.generate(this.a, this.b, l1, k1, i2); + } + } + + if (this.h) { + for (l1 = 0; l1 < 8; ++l1) { + k1 = k + this.b.nextInt(16) + 8; + i2 = this.b.nextInt(256); + int j2 = l + this.b.nextInt(16) + 8; + + (new WorldGenDungeons()).generate(this.a, this.b, k1, i2, j2); + } + } + + if (this.g) { + biomebase.a(this.a, this.b, k, l); + } + } + + public boolean saveChunks(boolean flag, IProgressUpdate iprogressupdate) { + return true; + } + + public void c() {} + + public boolean unloadChunks() { + return false; + } + + public boolean canSave() { + return true; + } + + public String getName() { + return "FlatLevelSource"; + } + + public List getMobsFor(EnumCreatureType enumcreaturetype, int i, int j, int k) { + BiomeBase biomebase = this.a.getBiome(i, k); + + return biomebase.getMobs(enumcreaturetype); + } + + public ChunkPosition findNearestMapFeature(World world, String s, int i, int j, int k) { + if ("Stronghold".equals(s)) { + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + StructureGenerator structuregenerator = (StructureGenerator) iterator.next(); + + if (structuregenerator instanceof WorldGenStronghold) { + return structuregenerator.getNearestGeneratedFeature(world, i, j, k); + } + } + } + + return null; + } + + public int getLoadedChunks() { + return 0; + } + + public void recreateStructures(int i, int j) { + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + StructureGenerator structuregenerator = (StructureGenerator) iterator.next(); + + structuregenerator.a(this, this.a, i, j, (Block[]) null); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderGenerate.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderGenerate.java new file mode 100644 index 0000000..6c41753 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderGenerate.java @@ -0,0 +1,404 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +public class ChunkProviderGenerate implements IChunkProvider { + + private Random i; + private NoiseGeneratorOctaves j; + private NoiseGeneratorOctaves k; + private NoiseGeneratorOctaves l; + private NoiseGenerator3 m; + public NoiseGeneratorOctaves a; + public NoiseGeneratorOctaves b; + public NoiseGeneratorOctaves c; + private World n; + private final boolean o; + private WorldType p; + private final double[] q; + private final float[] r; + private double[] s = new double[256]; + private WorldGenBase t = new WorldGenCaves(); + private WorldGenStronghold u = new WorldGenStronghold(); + private WorldGenVillage v = new WorldGenVillage(); + private WorldGenMineshaft w = new WorldGenMineshaft(); + private WorldGenLargeFeature x = new WorldGenLargeFeature(); + private WorldGenBase y = new WorldGenCanyon(); + private BiomeBase[] z; + double[] d; + double[] e; + double[] f; + double[] g; + int[][] h = new int[32][32]; + + public ChunkProviderGenerate(World world, long i, boolean flag) { + this.n = world; + this.o = flag; + this.p = world.getWorldData().getType(); + this.i = new Random(i); + this.j = new NoiseGeneratorOctaves(this.i, 16); + this.k = new NoiseGeneratorOctaves(this.i, 16); + this.l = new NoiseGeneratorOctaves(this.i, 8); + this.m = new NoiseGenerator3(this.i, 4); + this.a = new NoiseGeneratorOctaves(this.i, 10); + this.b = new NoiseGeneratorOctaves(this.i, 16); + this.c = new NoiseGeneratorOctaves(this.i, 8); + this.q = new double[825]; + this.r = new float[25]; + + for (int j = -2; j <= 2; ++j) { + for (int k = -2; k <= 2; ++k) { + float f = 10.0F / MathHelper.c((float) (j * j + k * k) + 0.2F); + + this.r[j + 2 + (k + 2) * 5] = f; + } + } + } + + public void a(int i, int j, Block[] ablock) { + byte b0 = 63; + + this.z = this.n.getWorldChunkManager().getBiomes(this.z, i * 4 - 2, j * 4 - 2, 10, 10); + this.a(i * 4, 0, j * 4); + + for (int k = 0; k < 4; ++k) { + int l = k * 5; + int i1 = (k + 1) * 5; + + for (int j1 = 0; j1 < 4; ++j1) { + int k1 = (l + j1) * 33; + int l1 = (l + j1 + 1) * 33; + int i2 = (i1 + j1) * 33; + int j2 = (i1 + j1 + 1) * 33; + + for (int k2 = 0; k2 < 32; ++k2) { + double d0 = 0.125D; + double d1 = this.q[k1 + k2]; + double d2 = this.q[l1 + k2]; + double d3 = this.q[i2 + k2]; + double d4 = this.q[j2 + k2]; + double d5 = (this.q[k1 + k2 + 1] - d1) * d0; + double d6 = (this.q[l1 + k2 + 1] - d2) * d0; + double d7 = (this.q[i2 + k2 + 1] - d3) * d0; + double d8 = (this.q[j2 + k2 + 1] - d4) * d0; + + for (int l2 = 0; l2 < 8; ++l2) { + double d9 = 0.25D; + double d10 = d1; + double d11 = d2; + double d12 = (d3 - d1) * d9; + double d13 = (d4 - d2) * d9; + + for (int i3 = 0; i3 < 4; ++i3) { + int j3 = i3 + k * 4 << 12 | 0 + j1 * 4 << 8 | k2 * 8 + l2; + short short1 = 256; + + j3 -= short1; + double d14 = 0.25D; + double d15 = (d11 - d10) * d14; + double d16 = d10 - d15; + + for (int k3 = 0; k3 < 4; ++k3) { + if ((d16 += d15) > 0.0D) { + ablock[j3 += short1] = Blocks.STONE; + } else if (k2 * 8 + l2 < b0) { + ablock[j3 += short1] = Blocks.STATIONARY_WATER; + } else { + ablock[j3 += short1] = null; + } + } + + d10 += d12; + d11 += d13; + } + + d1 += d5; + d2 += d6; + d3 += d7; + d4 += d8; + } + } + } + } + } + + public void a(int i, int j, Block[] ablock, byte[] abyte, BiomeBase[] abiomebase) { + double d0 = 0.03125D; + + this.s = this.m.a(this.s, (double) (i * 16), (double) (j * 16), 16, 16, d0 * 2.0D, d0 * 2.0D, 1.0D); + + for (int k = 0; k < 16; ++k) { + for (int l = 0; l < 16; ++l) { + BiomeBase biomebase = abiomebase[l + k * 16]; + + biomebase.a(this.n, this.i, ablock, abyte, i * 16 + k, j * 16 + l, this.s[l + k * 16]); + } + } + } + + public Chunk getChunkAt(int i, int j) { + return this.getOrCreateChunk(i, j); + } + + public Chunk getOrCreateChunk(int i, int j) { + this.i.setSeed((long) i * 341873128712L + (long) j * 132897987541L); + Block[] ablock = new Block[65536]; + byte[] abyte = new byte[65536]; + + this.a(i, j, ablock); + this.z = this.n.getWorldChunkManager().getBiomeBlock(this.z, i * 16, j * 16, 16, 16); + this.a(i, j, ablock, abyte, this.z); + // PaperSpigot start + if (this.n.paperSpigotConfig.generateCaves && this.n.generatorConfig.cavesMultiplier > 0) this.t.a(this, this.n, i, j, ablock); + if (this.n.paperSpigotConfig.generateCanyon) this.y.a(this, this.n, i, j, ablock); + // PaperSpigot end + if (this.o) { + // PaperSpigot start + if (this.n.paperSpigotConfig.generateMineshaft) this.w.a(this, this.n, i, j, ablock); + if (this.n.paperSpigotConfig.generateVillage) this.v.a(this, this.n, i, j, ablock); + if (this.n.paperSpigotConfig.generateStronghold) this.u.a(this, this.n, i, j, ablock); + if (this.n.paperSpigotConfig.generateTemple) this.x.a(this, this.n, i, j, ablock); + // PaperSpigot end + } + + Chunk chunk = new Chunk(this.n, ablock, abyte, i, j); + byte[] abyte1 = chunk.m(); + + for (int k = 0; k < abyte1.length; ++k) { + abyte1[k] = (byte) this.z[k].id; + } + + chunk.initLighting(); + return chunk; + } + + private void a(int i, int j, int k) { + double d0 = 684.412D; + double d1 = 684.412D; + double d2 = 512.0D; + double d3 = 512.0D; + + this.g = this.b.a(this.g, i, k, 5, 5, 200.0D, 200.0D, 0.5D); + this.d = this.l.a(this.d, i, j, k, 5, 33, 5, 8.555150000000001D, 4.277575000000001D, 8.555150000000001D); + this.e = this.j.a(this.e, i, j, k, 5, 33, 5, 684.412D, 684.412D, 684.412D); + this.f = this.k.a(this.f, i, j, k, 5, 33, 5, 684.412D, 684.412D, 684.412D); + boolean flag = false; + boolean flag1 = false; + int l = 0; + int i1 = 0; + double d4 = 8.5D; + + for (int j1 = 0; j1 < 5; ++j1) { + for (int k1 = 0; k1 < 5; ++k1) { + float f = 0.0F; + float f1 = 0.0F; + float f2 = 0.0F; + byte b0 = 2; + BiomeBase biomebase = this.z[j1 + 2 + (k1 + 2) * 10]; + + for (int l1 = -b0; l1 <= b0; ++l1) { + for (int i2 = -b0; i2 <= b0; ++i2) { + BiomeBase biomebase1 = this.z[j1 + l1 + 2 + (k1 + i2 + 2) * 10]; + float f3 = biomebase1.am; + float f4 = biomebase1.an; + + if (this.p == WorldType.AMPLIFIED && f3 > 0.0F) { + f3 = 1.0F + f3 * 2.0F; + f4 = 1.0F + f4 * 4.0F; + } + + float f5 = this.r[l1 + 2 + (i2 + 2) * 5] / (f3 + 2.0F); + + if (biomebase1.am > biomebase.am) { + f5 /= 2.0F; + } + + f += f4 * f5; + f1 += f3 * f5; + f2 += f5; + } + } + + f /= f2; + f1 /= f2; + f = f * 0.9F + 0.1F; + f1 = (f1 * 4.0F - 1.0F) / 8.0F; + double d5 = this.g[i1] / 8000.0D; + + if (d5 < 0.0D) { + d5 = -d5 * 0.3D; + } + + d5 = d5 * 3.0D - 2.0D; + if (d5 < 0.0D) { + d5 /= 2.0D; + if (d5 < -1.0D) { + d5 = -1.0D; + } + + d5 /= 1.4D; + d5 /= 2.0D; + } else { + if (d5 > 1.0D) { + d5 = 1.0D; + } + + d5 /= 8.0D; + } + + ++i1; + double d6 = (double) f1; + double d7 = (double) f; + + d6 += d5 * 0.2D; + d6 = d6 * 8.5D / 8.0D; + double d8 = 8.5D + d6 * 4.0D; + + for (int j2 = 0; j2 < 33; ++j2) { + double d9 = ((double) j2 - d8) * 12.0D * 128.0D / 256.0D / d7; + + if (d9 < 0.0D) { + d9 *= 4.0D; + } + + double d10 = this.e[l] / 512.0D; + double d11 = this.f[l] / 512.0D; + double d12 = (this.d[l] / 10.0D + 1.0D) / 2.0D; + double d13 = MathHelper.b(d10, d11, d12) - d9; + + if (j2 > 29) { + double d14 = (double) ((float) (j2 - 29) / 3.0F); + + d13 = d13 * (1.0D - d14) + -10.0D * d14; + } + + this.q[l] = d13; + ++l; + } + } + } + } + + public boolean isChunkLoaded(int i, int j) { + return true; + } + + public void getChunkAt(IChunkProvider ichunkprovider, int i, int j) { + BlockFalling.instaFall = true; + int k = i * 16; + int l = j * 16; + BiomeBase biomebase = this.n.getBiome(k + 16, l + 16); + + this.i.setSeed(this.n.getSeed()); + long i1 = this.i.nextLong() / 2L * 2L + 1L; + long j1 = this.i.nextLong() / 2L * 2L + 1L; + + this.i.setSeed((long) i * i1 + (long) j * j1 ^ this.n.getSeed()); + boolean flag = false; + + if (this.o) { + // PaperSpigot start + if (this.n.paperSpigotConfig.generateMineshaft) this.w.a(this.n, this.i, i, j); + if (this.n.paperSpigotConfig.generateVillage) flag = this.v.a(this.n, this.i, i, j); + if (this.n.paperSpigotConfig.generateStronghold) this.u.a(this.n, this.i, i, j); + if (this.n.paperSpigotConfig.generateTemple) this.x.a(this.n, this.i, i, j); + // PaperSpigot end + } + + int k1; + int l1; + int i2; + + if (biomebase != BiomeBase.DESERT && biomebase != BiomeBase.DESERT_HILLS && !flag && this.i.nextInt(4) == 0) { + k1 = k + this.i.nextInt(16) + 8; + l1 = this.i.nextInt(256); + i2 = l + this.i.nextInt(16) + 8; + (new WorldGenLakes(Blocks.STATIONARY_WATER)).generate(this.n, this.i, k1, l1, i2); + } + + if (!flag && this.i.nextInt(8) == 0) { + k1 = k + this.i.nextInt(16) + 8; + l1 = this.i.nextInt(this.i.nextInt(248) + 8); + i2 = l + this.i.nextInt(16) + 8; + if (l1 < 63 || this.i.nextInt(10) == 0) { + (new WorldGenLakes(Blocks.STATIONARY_LAVA)).generate(this.n, this.i, k1, l1, i2); + } + } + + // PaperSpigot start + if (this.n.paperSpigotConfig.generateDungeon) { + for (k1 = 0; k1 < 8; ++k1) { + l1 = k + this.i.nextInt(16) + 8; + i2 = this.i.nextInt(256); + int j2 = l + this.i.nextInt(16) + 8; + + (new WorldGenDungeons()).generate(this.n, this.i, l1, i2, j2); + } + } + // PaperSpigot end + + biomebase.a(this.n, this.i, k, l); + SpawnerCreature.a(this.n, biomebase, k + 8, l + 8, 16, 16, this.i); + k += 8; + l += 8; + + for (k1 = 0; k1 < 16; ++k1) { + for (l1 = 0; l1 < 16; ++l1) { + i2 = this.n.h(k + k1, l + l1); + if (this.n.r(k1 + k, i2 - 1, l1 + l)) { + this.n.setTypeAndData(k1 + k, i2 - 1, l1 + l, Blocks.ICE, 0, 2); + } + + if (this.n.e(k1 + k, i2, l1 + l, true)) { + this.n.setTypeAndData(k1 + k, i2, l1 + l, Blocks.SNOW, 0, 2); + } + } + } + + BlockFalling.instaFall = false; + } + + public boolean saveChunks(boolean flag, IProgressUpdate iprogressupdate) { + return true; + } + + public void c() {} + + public boolean unloadChunks() { + return false; + } + + public boolean canSave() { + return true; + } + + public String getName() { + return "RandomLevelSource"; + } + + public List getMobsFor(EnumCreatureType enumcreaturetype, int i, int j, int k) { + BiomeBase biomebase = this.n.getBiome(i, k); + + return enumcreaturetype == EnumCreatureType.MONSTER && this.x.a(i, j, k) ? this.x.b() : biomebase.getMobs(enumcreaturetype); + } + + public ChunkPosition findNearestMapFeature(World world, String s, int i, int j, int k) { + return "Stronghold".equals(s) && this.u != null ? this.u.getNearestGeneratedFeature(world, i, j, k) : null; + } + + public int getLoadedChunks() { + return 0; + } + + public void recreateStructures(int i, int j) { + if (this.o) { + // PaperSpigot start + if (this.n.paperSpigotConfig.generateMineshaft) this.w.a(this, this.n, i, j, (Block[]) null); + if (this.n.paperSpigotConfig.generateVillage) this.v.a(this, this.n, i, j, (Block[]) null); + if (this.n.paperSpigotConfig.generateStronghold) this.u.a(this, this.n, i, j, (Block[]) null); + if (this.n.paperSpigotConfig.generateTemple) this.x.a(this, this.n, i, j, (Block[]) null); + // PaperSpigot end + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderHell.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderHell.java new file mode 100644 index 0000000..b851249 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderHell.java @@ -0,0 +1,450 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +public class ChunkProviderHell implements IChunkProvider { + + private Random i; + private NoiseGeneratorOctaves j; + private NoiseGeneratorOctaves k; + private NoiseGeneratorOctaves l; + private NoiseGeneratorOctaves m; + private NoiseGeneratorOctaves n; + public NoiseGeneratorOctaves a; + public NoiseGeneratorOctaves b; + private World o; + private double[] p; + public WorldGenNether c = new WorldGenNether(); + private double[] q = new double[256]; + private double[] r = new double[256]; + private double[] s = new double[256]; + private WorldGenBase t = new WorldGenCavesHell(); + double[] d; + double[] e; + double[] f; + double[] g; + double[] h; + + public ChunkProviderHell(World world, long i) { + this.o = world; + this.i = new Random(i); + this.j = new NoiseGeneratorOctaves(this.i, 16); + this.k = new NoiseGeneratorOctaves(this.i, 16); + this.l = new NoiseGeneratorOctaves(this.i, 8); + this.m = new NoiseGeneratorOctaves(this.i, 4); + this.n = new NoiseGeneratorOctaves(this.i, 4); + this.a = new NoiseGeneratorOctaves(this.i, 10); + this.b = new NoiseGeneratorOctaves(this.i, 16); + } + + public void a(int i, int j, Block[] ablock) { + byte b0 = 4; + byte b1 = 32; + int k = b0 + 1; + byte b2 = 17; + int l = b0 + 1; + + this.p = this.a(this.p, i * b0, 0, j * b0, k, b2, l); + + for (int i1 = 0; i1 < b0; ++i1) { + for (int j1 = 0; j1 < b0; ++j1) { + for (int k1 = 0; k1 < 16; ++k1) { + double d0 = 0.125D; + double d1 = this.p[((i1 + 0) * l + j1 + 0) * b2 + k1 + 0]; + double d2 = this.p[((i1 + 0) * l + j1 + 1) * b2 + k1 + 0]; + double d3 = this.p[((i1 + 1) * l + j1 + 0) * b2 + k1 + 0]; + double d4 = this.p[((i1 + 1) * l + j1 + 1) * b2 + k1 + 0]; + double d5 = (this.p[((i1 + 0) * l + j1 + 0) * b2 + k1 + 1] - d1) * d0; + double d6 = (this.p[((i1 + 0) * l + j1 + 1) * b2 + k1 + 1] - d2) * d0; + double d7 = (this.p[((i1 + 1) * l + j1 + 0) * b2 + k1 + 1] - d3) * d0; + double d8 = (this.p[((i1 + 1) * l + j1 + 1) * b2 + k1 + 1] - d4) * d0; + + for (int l1 = 0; l1 < 8; ++l1) { + double d9 = 0.25D; + double d10 = d1; + double d11 = d2; + double d12 = (d3 - d1) * d9; + double d13 = (d4 - d2) * d9; + + for (int i2 = 0; i2 < 4; ++i2) { + int j2 = i2 + i1 * 4 << 11 | 0 + j1 * 4 << 7 | k1 * 8 + l1; + short short1 = 128; + double d14 = 0.25D; + double d15 = d10; + double d16 = (d11 - d10) * d14; + + for (int k2 = 0; k2 < 4; ++k2) { + Block block = null; + + if (k1 * 8 + l1 < b1) { + block = Blocks.STATIONARY_LAVA; + } + + if (d15 > 0.0D) { + block = Blocks.NETHERRACK; + } + + ablock[j2] = block; + j2 += short1; + d15 += d16; + } + + d10 += d12; + d11 += d13; + } + + d1 += d5; + d2 += d6; + d3 += d7; + d4 += d8; + } + } + } + } + } + + public void b(int i, int j, Block[] ablock) { + byte b0 = 64; + double d0 = 0.03125D; + + this.q = this.m.a(this.q, i * 16, j * 16, 0, 16, 16, 1, d0, d0, 1.0D); + this.r = this.m.a(this.r, i * 16, 109, j * 16, 16, 1, 16, d0, 1.0D, d0); + this.s = this.n.a(this.s, i * 16, j * 16, 0, 16, 16, 1, d0 * 2.0D, d0 * 2.0D, d0 * 2.0D); + + for (int k = 0; k < 16; ++k) { + for (int l = 0; l < 16; ++l) { + boolean flag = this.q[k + l * 16] + this.i.nextDouble() * 0.2D > 0.0D; + boolean flag1 = this.r[k + l * 16] + this.i.nextDouble() * 0.2D > 0.0D; + int i1 = (int) (this.s[k + l * 16] / 3.0D + 3.0D + this.i.nextDouble() * 0.25D); + int j1 = -1; + Block block = Blocks.NETHERRACK; + Block block1 = Blocks.NETHERRACK; + + for (int k1 = 127; k1 >= 0; --k1) { + int l1 = (l * 16 + k) * 128 + k1; + + // PaperSpigot start - Configurable flat bedrock worldgen + if (k1 < 127 - (o.paperSpigotConfig.generateFlatBedrock ? 0 : this.i.nextInt(5)) && + k1 > (o.paperSpigotConfig.generateFlatBedrock ? 0 : this.i.nextInt(5))) { + // PaperSpigot end + Block block2 = ablock[l1]; + + if (block2 != null && block2.getMaterial() != Material.AIR) { + if (block2 == Blocks.NETHERRACK) { + if (j1 == -1) { + if (i1 <= 0) { + block = null; + block1 = Blocks.NETHERRACK; + } else if (k1 >= b0 - 4 && k1 <= b0 + 1) { + block = Blocks.NETHERRACK; + block1 = Blocks.NETHERRACK; + if (flag1) { + block = Blocks.GRAVEL; + block1 = Blocks.NETHERRACK; + } + + if (flag) { + block = Blocks.SOUL_SAND; + block1 = Blocks.SOUL_SAND; + } + } + + if (k1 < b0 && (block == null || block.getMaterial() == Material.AIR)) { + block = Blocks.STATIONARY_LAVA; + } + + j1 = i1; + if (k1 >= b0 - 1) { + ablock[l1] = block; + } else { + ablock[l1] = block1; + } + } else if (j1 > 0) { + --j1; + ablock[l1] = block1; + } + } + } else { + j1 = -1; + } + } else { + ablock[l1] = Blocks.BEDROCK; + } + } + } + } + } + + public Chunk getChunkAt(int i, int j) { + return this.getOrCreateChunk(i, j); + } + + public Chunk getOrCreateChunk(int i, int j) { + this.i.setSeed((long) i * 341873128712L + (long) j * 132897987541L); + Block[] ablock = new Block['\u8000']; + + this.a(i, j, ablock); + this.b(i, j, ablock); + // PaperSpigot start + if (this.o.paperSpigotConfig.generateCaves) this.t.a(this, this.o, i, j, ablock); + if (this.o.paperSpigotConfig.generateFortress) this.c.a(this, this.o, i, j, ablock); + // PaperSpigot end + Chunk chunk = new Chunk(this.o, ablock, i, j); + BiomeBase[] abiomebase = this.o.getWorldChunkManager().getBiomeBlock((BiomeBase[]) null, i * 16, j * 16, 16, 16); + byte[] abyte = chunk.m(); + + for (int k = 0; k < abyte.length; ++k) { + abyte[k] = (byte) abiomebase[k].id; + } + + chunk.n(); + return chunk; + } + + private double[] a(double[] adouble, int i, int j, int k, int l, int i1, int j1) { + if (adouble == null) { + adouble = new double[l * i1 * j1]; + } + + double d0 = 684.412D; + double d1 = 2053.236D; + + this.g = this.a.a(this.g, i, j, k, l, 1, j1, 1.0D, 0.0D, 1.0D); + this.h = this.b.a(this.h, i, j, k, l, 1, j1, 100.0D, 0.0D, 100.0D); + this.d = this.l.a(this.d, i, j, k, l, i1, j1, d0 / 80.0D, d1 / 60.0D, d0 / 80.0D); + this.e = this.j.a(this.e, i, j, k, l, i1, j1, d0, d1, d0); + this.f = this.k.a(this.f, i, j, k, l, i1, j1, d0, d1, d0); + int k1 = 0; + int l1 = 0; + double[] adouble1 = new double[i1]; + + int i2; + + for (i2 = 0; i2 < i1; ++i2) { + adouble1[i2] = Math.cos((double) i2 * 3.141592653589793D * 6.0D / (double) i1) * 2.0D; + double d2 = (double) i2; + + if (i2 > i1 / 2) { + d2 = (double) (i1 - 1 - i2); + } + + if (d2 < 4.0D) { + d2 = 4.0D - d2; + adouble1[i2] -= d2 * d2 * d2 * 10.0D; + } + } + + for (i2 = 0; i2 < l; ++i2) { + for (int j2 = 0; j2 < j1; ++j2) { + double d3 = (this.g[l1] + 256.0D) / 512.0D; + + if (d3 > 1.0D) { + d3 = 1.0D; + } + + double d4 = 0.0D; + double d5 = this.h[l1] / 8000.0D; + + if (d5 < 0.0D) { + d5 = -d5; + } + + d5 = d5 * 3.0D - 3.0D; + if (d5 < 0.0D) { + d5 /= 2.0D; + if (d5 < -1.0D) { + d5 = -1.0D; + } + + d5 /= 1.4D; + d5 /= 2.0D; + d3 = 0.0D; + } else { + if (d5 > 1.0D) { + d5 = 1.0D; + } + + d5 /= 6.0D; + } + + d3 += 0.5D; + d5 = d5 * (double) i1 / 16.0D; + ++l1; + + for (int k2 = 0; k2 < i1; ++k2) { + double d6 = 0.0D; + double d7 = adouble1[k2]; + double d8 = this.e[k1] / 512.0D; + double d9 = this.f[k1] / 512.0D; + double d10 = (this.d[k1] / 10.0D + 1.0D) / 2.0D; + + if (d10 < 0.0D) { + d6 = d8; + } else if (d10 > 1.0D) { + d6 = d9; + } else { + d6 = d8 + (d9 - d8) * d10; + } + + d6 -= d7; + double d11; + + if (k2 > i1 - 4) { + d11 = (double) ((float) (k2 - (i1 - 4)) / 3.0F); + d6 = d6 * (1.0D - d11) + -10.0D * d11; + } + + if ((double) k2 < d4) { + d11 = (d4 - (double) k2) / 4.0D; + if (d11 < 0.0D) { + d11 = 0.0D; + } + + if (d11 > 1.0D) { + d11 = 1.0D; + } + + d6 = d6 * (1.0D - d11) + -10.0D * d11; + } + + adouble[k1] = d6; + ++k1; + } + } + } + + return adouble; + } + + public boolean isChunkLoaded(int i, int j) { + return true; + } + + public void getChunkAt(IChunkProvider ichunkprovider, int i, int j) { + BlockFalling.instaFall = true; + int k = i * 16; + int l = j * 16; + + if (this.o.paperSpigotConfig.generateFortress) this.c.a(this.o, this.i, i, j); // PaperSpigot + + int i1; + int j1; + int k1; + int l1; + + for (i1 = 0; i1 < 8; ++i1) { + j1 = k + this.i.nextInt(16) + 8; + k1 = this.i.nextInt(120) + 4; + l1 = l + this.i.nextInt(16) + 8; + (new WorldGenHellLava(Blocks.LAVA, false)).generate(this.o, this.i, j1, k1, l1); + } + + i1 = this.i.nextInt(this.i.nextInt(10) + 1) + 1; + + int i2; + + for (j1 = 0; j1 < i1; ++j1) { + k1 = k + this.i.nextInt(16) + 8; + l1 = this.i.nextInt(120) + 4; + i2 = l + this.i.nextInt(16) + 8; + (new WorldGenFire()).generate(this.o, this.i, k1, l1, i2); + } + + i1 = this.i.nextInt(this.i.nextInt(10) + 1); + + for (j1 = 0; j1 < i1; ++j1) { + k1 = k + this.i.nextInt(16) + 8; + l1 = this.i.nextInt(120) + 4; + i2 = l + this.i.nextInt(16) + 8; + (new WorldGenLightStone1()).generate(this.o, this.i, k1, l1, i2); + } + + for (j1 = 0; j1 < 10; ++j1) { + k1 = k + this.i.nextInt(16) + 8; + l1 = this.i.nextInt(128); + i2 = l + this.i.nextInt(16) + 8; + (new WorldGenLightStone2()).generate(this.o, this.i, k1, l1, i2); + } + + if (this.i.nextInt(1) == 0) { + j1 = k + this.i.nextInt(16) + 8; + k1 = this.i.nextInt(128); + l1 = l + this.i.nextInt(16) + 8; + (new WorldGenFlowers(Blocks.BROWN_MUSHROOM)).generate(this.o, this.i, j1, k1, l1); + } + + if (this.i.nextInt(1) == 0) { + j1 = k + this.i.nextInt(16) + 8; + k1 = this.i.nextInt(128); + l1 = l + this.i.nextInt(16) + 8; + (new WorldGenFlowers(Blocks.RED_MUSHROOM)).generate(this.o, this.i, j1, k1, l1); + } + + WorldGenMinable worldgenminable = new WorldGenMinable(Blocks.QUARTZ_ORE, 13, Blocks.NETHERRACK); + + int j2; + + for (k1 = 0; k1 < 16; ++k1) { + l1 = k + this.i.nextInt(16); + i2 = this.i.nextInt(108) + 10; + j2 = l + this.i.nextInt(16); + worldgenminable.generate(this.o, this.i, l1, i2, j2); + } + + for (k1 = 0; k1 < 16; ++k1) { + l1 = k + this.i.nextInt(16); + i2 = this.i.nextInt(108) + 10; + j2 = l + this.i.nextInt(16); + (new WorldGenHellLava(Blocks.LAVA, true)).generate(this.o, this.i, l1, i2, j2); + } + + BlockFalling.instaFall = false; + } + + public boolean saveChunks(boolean flag, IProgressUpdate iprogressupdate) { + return true; + } + + public void c() {} + + public boolean unloadChunks() { + return false; + } + + public boolean canSave() { + return true; + } + + public String getName() { + return "HellRandomLevelSource"; + } + + public List getMobsFor(EnumCreatureType enumcreaturetype, int i, int j, int k) { + if (enumcreaturetype == EnumCreatureType.MONSTER) { + if (this.c.b(i, j, k)) { + return this.c.b(); + } + + if (this.c.d(i, j, k) && this.o.getType(i, j - 1, k) == Blocks.NETHER_BRICK) { + return this.c.b(); + } + } + + BiomeBase biomebase = this.o.getBiome(i, k); + + return biomebase.getMobs(enumcreaturetype); + } + + public ChunkPosition findNearestMapFeature(World world, String s, int i, int j, int k) { + return null; + } + + public int getLoadedChunks() { + return 0; + } + + public void recreateStructures(int i, int j) { + if (this.o.paperSpigotConfig.generateFortress) this.c.a(this, this.o, i, j, (Block[]) null); // PaperSpigot + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderServer.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderServer.java new file mode 100644 index 0000000..88e4be8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +1,486 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +// CraftBukkit start +import java.util.Random; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Server; +import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; +import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.craftbukkit.util.LongHashSet; +import org.bukkit.event.world.ChunkUnloadEvent; +// CraftBukkit end + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + +public class ChunkProviderServer implements IChunkProvider { + + private static final Logger b = LogManager.getLogger(); + // CraftBukkit start - private -> public + public LongHashSet unloadQueue = new LongHashSet(); // LongHashSet + public Chunk emptyChunk; + public IChunkProvider chunkProvider; + private IChunkLoader f; + public boolean forceChunkLoad = false; // true -> false + //public LongObjectHashMap chunks = new LongObjectHashMap(); + public Long2ObjectOpenHashMap chunks = new Long2ObjectOpenHashMap<>(); // MineHQ + public WorldServer world; + // CraftBukkit end + + public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, IChunkProvider ichunkprovider) { + this.emptyChunk = new EmptyChunk(worldserver, Integer.MIN_VALUE, Integer.MIN_VALUE); // Poweruser + this.world = worldserver; + this.f = ichunkloader; + this.chunkProvider = ichunkprovider; + } + + public boolean chunkExists(int i, int j) { + return ((ChunkRegionLoader) this.f).chunkExists(this.world, i, j); + } + + public boolean isChunkLoaded(int i, int j) { + return this.chunks.containsKey(LongHash.toLong(i, j)); // CraftBukkit // MineHQ + } + + // CraftBukkit start - Change return type to Collection and return the values of our chunk map + public java.util.Collection a() { + // return this.chunkList; + return this.chunks.values(); + // CraftBukkit end + } + + // MineHQ start + public void queueUnload(int x, int z) { + queueUnload(x, z, false); + } + + public void queueUnload(int i, int j, boolean checked) { + if (!checked && this.world.getPlayerChunkMap().isChunkInUse(i, j)) return; + // MineHQ end + // PaperSpigot start - Asynchronous lighting updates + Chunk chunk = this.chunks.get(LongHash.toLong(i, j)); // MineHQ + if (chunk != null && chunk.world.paperSpigotConfig.useAsyncLighting && (chunk.pendingLightUpdates.get() > 0 || chunk.world.getTime() - chunk.lightUpdateTime < 20)) { + return; + } + // PaperSpigot end + // PaperSpigot start - Don't unload chunk if it contains an entity that loads chunks + if (chunk != null) { + for (List entities : chunk.entitySlices) { + for (Entity entity : entities) { + if (entity.loadChunks) { + return; + } + } + } + } + // PaperSpigot end + if (this.world.worldProvider.e()) { + ChunkCoordinates chunkcoordinates = this.world.getSpawn(); + int k = i * 16 + 8 - chunkcoordinates.x; + int l = j * 16 + 8 - chunkcoordinates.z; + short short1 = 128; + + // CraftBukkit start + if (k < -short1 || k > short1 || l < -short1 || l > short1 || !(this.world.keepSpawnInMemory)) { // Added 'this.world.keepSpawnInMemory' + this.unloadQueue.add(i, j); + + // MineHQ start - don't lookup twice + if (chunk != null) { + chunk.mustSave = true; + } + // MineHQ end + } + // CraftBukkit end + } else { + // CraftBukkit start + this.unloadQueue.add(i, j); + + // MineHQ start - don't lookup twice + if (chunk != null) { + chunk.mustSave = true; + } + // MineHQ end + // CraftBukkit end + } + } + + public void b() { + Iterator iterator = this.chunks.values().iterator(); // CraftBukkit + + while (iterator.hasNext()) { + Chunk chunk = (Chunk) iterator.next(); + + this.queueUnload(chunk.locX, chunk.locZ); + } + } + + // CraftBukkit start - Add async variant, provide compatibility + public Chunk getChunkIfLoaded(int x, int z) { + return this.chunks.get(LongHash.toLong(x, z)); // MineHQ + } + + public Chunk getChunkAt(int i, int j) { + return getChunkAt(i, j, null); + } + + public Chunk getChunkAt(int i, int j, Runnable runnable) { + this.unloadQueue.remove(i, j); + Chunk chunk = this.chunks.get(LongHash.toLong(i, j)); // MineHQ + ChunkRegionLoader loader = null; + + if (this.f instanceof ChunkRegionLoader) { + loader = (ChunkRegionLoader) this.f; + } + + // We can only use the queue for already generated chunks + if (chunk == null && loader != null && loader.chunkExists(this.world, i, j)) { + if (runnable != null) { + ChunkIOExecutor.queueChunkLoad(this.world, loader, this, i, j, runnable); + return null; + } else { + chunk = ChunkIOExecutor.syncChunkLoad(this.world, loader, this, i, j); + } + } else if (chunk == null) { + chunk = this.originalGetChunkAt(i, j); + } + + // If we didn't load the chunk async and have a callback run it now + if (runnable != null) { + runnable.run(); + } + + return chunk; + } + + public Chunk originalGetChunkAt(int i, int j) { + this.unloadQueue.remove(i, j); + Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); // MineHQ + boolean newChunk = false; + + if (chunk == null) { + world.timings.syncChunkLoadTimer.startTiming(); // Spigot + chunk = this.loadChunk(i, j); + if (chunk == null) { + if (this.chunkProvider == null) { + chunk = this.emptyChunk; + } else { + try { + chunk = this.chunkProvider.getOrCreateChunk(i, j); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated"); + + crashreportsystemdetails.a("Location", String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)})); + crashreportsystemdetails.a("Position hash", Long.valueOf(LongHash.toLong(i, j))); // CraftBukkit - Use LongHash + crashreportsystemdetails.a("Generator", this.chunkProvider.getName()); + throw new ReportedException(crashreport); + } + } + newChunk = true; // CraftBukkit + } + + this.chunks.put(LongHash.toLong(i, j), chunk); // CraftBukkit // MineHQ + chunk.addEntities(); + + // CraftBukkit start + Server server = this.world.getServer(); + if (server != null) { + /* + * If it's a new world, the first few chunks are generated inside + * the World constructor. We can't reliably alter that, so we have + * no way of creating a CraftWorld/CraftServer at that point. + */ + server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, newChunk)); + } + + // Update neighbor counts + for (int x = -2; x < 3; x++) { + for (int z = -2; z < 3; z++) { + if (x == 0 && z == 0) { + continue; + } + + Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z); + if (neighbor != null) { + neighbor.setNeighborLoaded(-x, -z); + chunk.setNeighborLoaded(x, z); + } + } + } + // CraftBukkit end + chunk.loadNearby(this, this, i, j); + world.timings.syncChunkLoadTimer.stopTiming(); // Spigot + } + + return chunk; + } + + public Chunk getOrCreateChunk(int i, int j) { + // CraftBukkit start + Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); // MineHQ + + chunk = chunk == null ? (!this.world.isLoading && !this.forceChunkLoad ? this.emptyChunk : this.getChunkAt(i, j)) : chunk; + if (chunk == this.emptyChunk) return chunk; + if (i != chunk.locX || j != chunk.locZ) { + b.error("Chunk (" + chunk.locX + ", " + chunk.locZ + ") stored at (" + i + ", " + j + ") in world '" + world.getWorld().getName() + "'"); + b.error(chunk.getClass().getName()); + Throwable ex = new Throwable(); + ex.fillInStackTrace(); + ex.printStackTrace(); + } + return chunk; + // CraftBukkit end + } + + public Chunk loadChunk(int i, int j) { // CraftBukkit - private -> public + if (this.f == null) { + return null; + } else { + try { + Chunk chunk = this.f.a(this.world, i, j); + + if (chunk != null) { + chunk.lastSaved = this.world.getTime(); + if (this.chunkProvider != null) { + world.timings.syncChunkLoadStructuresTimer.startTiming(); // Spigot + this.chunkProvider.recreateStructures(i, j); + world.timings.syncChunkLoadStructuresTimer.stopTiming(); // Spigot + } + } + + return chunk; + } catch (Exception exception) { + b.error("Couldn\'t load chunk", exception); + return null; + } + } + } + + public void saveChunkNOP(Chunk chunk) { // CraftBukkit - private -> public + if (this.f != null) { + try { + this.f.b(this.world, chunk); + } catch (Exception exception) { + b.error("Couldn\'t save entities", exception); + } + } + } + + public void saveChunk(Chunk chunk) { // CraftBukkit - private -> public + if (this.f != null) { + try { + chunk.lastSaved = this.world.getTime(); + this.f.a(this.world, chunk); + // CraftBukkit start - IOException to Exception + } catch (Exception ioexception) { + b.error("Couldn\'t save chunk", ioexception); + /* Remove extra exception + } catch (ExceptionWorldConflict exceptionworldconflict) { + b.error("Couldn\'t save chunk; already in use by another instance of Minecraft?", exceptionworldconflict); + // CraftBukkit end */ + } + } + } + + public void getChunkAt(IChunkProvider ichunkprovider, int i, int j) { + Chunk chunk = this.getOrCreateChunk(i, j); + + if (!chunk.done) { + chunk.p(); + if (this.chunkProvider != null) { + this.chunkProvider.getChunkAt(ichunkprovider, i, j); + + // CraftBukkit start + BlockSand.instaFall = true; + Random random = new Random(); + random.setSeed(world.getSeed()); + long xRand = random.nextLong() / 2L * 2L + 1L; + long zRand = random.nextLong() / 2L * 2L + 1L; + random.setSeed((long) i * xRand + (long) j * zRand ^ world.getSeed()); + + org.bukkit.World world = this.world.getWorld(); + if (world != null) { + this.world.populating = true; + try { + for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { + populator.populate(world, random, chunk.bukkitChunk); + } + } finally { + this.world.populating = false; + } + } + BlockSand.instaFall = false; + this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(chunk.bukkitChunk)); + // CraftBukkit end + + chunk.e(); + } + } + } + + public boolean saveChunks(boolean flag, IProgressUpdate iprogressupdate) { + int i = 0; + // CraftBukkit start + Iterator iterator = this.chunks.values().iterator(); + + while (iterator.hasNext()) { + Chunk chunk = (Chunk) iterator.next(); + // CraftBukkit end + + if (flag) { + this.saveChunkNOP(chunk); + } + + if (chunk.a(flag)) { + this.saveChunk(chunk); + chunk.n = false; + ++i; + // Poweruser start + if (i >= org.spigotmc.SpigotConfig.autoSaveChunksPerTick && !flag) { + this.world.getAutoSaveWorldData().addAutoSaveChunkCount(i); + // Poweruser end + return false; + } + } + } + // Poweruser start + if(!flag) { + this.world.getAutoSaveWorldData().addAutoSaveChunkCount(i); + } + // Poweruser end + + return true; + } + + public void c() { + if (this.f != null) { + this.f.b(); + } + } + + + public boolean unloadChunks() { + if (!this.world.savingDisabled) { + int chunksSize = this.chunks.size(); + int unloadSize = this.unloadQueue.size(); + int unloaded = 0; + long start = System.currentTimeMillis(); + long nanoStart = System.nanoTime(); + long unloadQueuePopTotal = 0, chunksGet = 0, callEvent = 0, removeEntities = 0, saveChunk = 0, saveChunkNOP = 0, chunkRemove = 0, updateNeighbourCounts = 0; + // CraftBukkit start + Server server = this.world.getServer(); + for (int i = 0; i < 100 && !this.unloadQueue.isEmpty() && (System.currentTimeMillis() - start) < 150; i++) { + nanoStart = System.nanoTime(); + long chunkcoordinates = this.unloadQueue.popFirst(); + unloadQueuePopTotal += System.nanoTime() - nanoStart; + // MineHQ start + int locX = LongHash.msw(chunkcoordinates); + int locZ = LongHash.lsw(chunkcoordinates); + nanoStart = System.nanoTime(); + Chunk chunk = this.chunks.get(LongHash.toLong(locX, locZ)); + chunksGet += System.nanoTime() - nanoStart; + // MineHQ end + if (chunk == null) continue; + + ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk); + nanoStart = System.nanoTime(); + server.getPluginManager().callEvent(event); + callEvent += System.nanoTime() - nanoStart; + if (!event.isCancelled()) { + if (chunk != null) { + this.world.timings.doChunkUnloadSave.startTiming(); + nanoStart = System.nanoTime(); + chunk.removeEntities(); + removeEntities += System.nanoTime() - nanoStart; + nanoStart = System.nanoTime(); + this.saveChunk(chunk); + saveChunk += System.nanoTime() - nanoStart; + nanoStart = System.nanoTime(); + this.saveChunkNOP(chunk); + saveChunkNOP += System.nanoTime() - nanoStart; + nanoStart = System.nanoTime(); + this.chunks.remove(LongHash.toLong(locX, locZ)); // CraftBukkit // MineHQ + chunkRemove += System.nanoTime() - nanoStart; + unloaded++; + this.world.timings.doChunkUnloadSave.stopTiming(); + } + + // this.unloadQueue.remove(olong); + // this.chunks.remove(olong.longValue()); + + // Update neighbor counts + nanoStart = System.nanoTime(); + for (int x = -2; x < 3; x++) { + for (int z = -2; z < 3; z++) { + if (x == 0 && z == 0) { + continue; + } + + Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z); + if (neighbor != null) { + neighbor.setNeighborUnloaded(-x, -z); + chunk.setNeighborUnloaded(x, z); + } + } + } + updateNeighbourCounts += System.nanoTime() - nanoStart; + } + } + // CraftBukkit end + long timeTaken = System.currentTimeMillis() - start; + if (timeTaken > 75) { + MinecraftServer.getLogger().warn("ChunkProviderServer.unloadChunks took too long! " + timeTaken + "ms!"); + MinecraftServer.getLogger().warn("World name: " + this.world.worldData.getName()); + MinecraftServer.getLogger().warn("chunks.size() = " + chunksSize); + MinecraftServer.getLogger().warn("unloadQueue.size() = " + unloadSize); + MinecraftServer.getLogger().warn("chunks unloaded this run: " + unloaded); + MinecraftServer.getLogger().warn("unloadQueuePopTotal: " + unloadQueuePopTotal); + MinecraftServer.getLogger().warn("chunksGet: " + chunksGet); + MinecraftServer.getLogger().warn("callEvent: " + callEvent); + MinecraftServer.getLogger().warn("removeEntities: " + removeEntities); + MinecraftServer.getLogger().warn("saveChunk: " + saveChunk); + MinecraftServer.getLogger().warn("saveChunkNOP: " + saveChunkNOP); + MinecraftServer.getLogger().warn("chunkRemove: " + chunkRemove); + MinecraftServer.getLogger().warn("updateNeighbourCounts: " + updateNeighbourCounts); + this.world.printTimings(); + MinecraftServer.getLogger().warn("world.N.size(): " + world.N.size()); + MinecraftServer.getLogger().warn("world.V.size(): " + world.V.size()); + } + + if (this.f != null) { + this.f.a(); + } + + this.world.clearTimings(); + } + + return this.chunkProvider.unloadChunks(); + } + + public boolean canSave() { + return !this.world.savingDisabled; + } + + public String getName() { + // CraftBukkit - this.chunks.count() -> .values().size() + return "ServerChunkCache: " + this.chunks.values().size() + " Drop: " + this.unloadQueue.size(); + } + + public List getMobsFor(EnumCreatureType enumcreaturetype, int i, int j, int k) { + return this.chunkProvider.getMobsFor(enumcreaturetype, i, j, k); + } + + public ChunkPosition findNearestMapFeature(World world, String s, int i, int j, int k) { + return this.chunkProvider.findNearestMapFeature(world, s, i, j, k); + } + + public int getLoadedChunks() { + // CraftBukkit - this.chunks.count() -> this.chunks.size() + return this.chunks.size(); + } + + public void recreateStructures(int i, int j) {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkRegionLoader.java new file mode 100644 index 0000000..5a1ed2c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +1,435 @@ +package net.minecraft.server; + +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + + private java.util.LinkedHashMap pendingSaves = new java.util.LinkedHashMap(); // Spigot + private static final Logger a = LogManager.getLogger(); + private List b = new ArrayList(); + private Set c = new HashSet(); + private Object d = new Object(); + private final File e; + + public ChunkRegionLoader(File file1) { + this.e = file1; + } + + // CraftBukkit start + public boolean chunkExists(World world, int i, int j) { + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); + + synchronized (this.d) { + // Spigot start + if (pendingSaves.containsKey(chunkcoordintpair)) { + return true; + } + // Spigot end + } + + final RegionFile region = RegionFileCache.a(this.e, i, j, false); // PaperSpigot + return region != null && region.chunkExists(i & 31, j & 31); // PaperSpigot + } + // CraftBukkit end + + // CraftBukkit start - Add async variant, provide compatibility + public Chunk a(World world, int i, int j) { + world.timings.syncChunkLoadDataTimer.startTiming(); // Spigot + Object[] data = this.loadChunk(world, i, j); + world.timings.syncChunkLoadDataTimer.stopTiming(); // Spigot + if (data != null) { + Chunk chunk = (Chunk) data[0]; + NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; + this.loadEntities(chunk, nbttagcompound.getCompound("Level"), world); + return chunk; + } + + return null; + } + + public Object[] loadChunk(World world, int i, int j) { + // CraftBukkit end + NBTTagCompound nbttagcompound = null; + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); + Object object = this.d; + + synchronized (this.d) { + // Spigot start + PendingChunkToSave pendingchunktosave = pendingSaves.get(chunkcoordintpair); + if (pendingchunktosave != null) { + nbttagcompound = pendingchunktosave.b; + } + // Spigot end + } + + if (nbttagcompound == null) { + DataInputStream datainputstream = RegionFileCache.c(this.e, i, j); + + if (datainputstream == null) { + return null; + } + + nbttagcompound = NBTCompressedStreamTools.a(datainputstream); + } + + return this.a(world, i, j, nbttagcompound); + } + + protected Object[] a(World world, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[] + if (!nbttagcompound.hasKeyOfType("Level", 10)) { + a.error("Chunk file at " + i + "," + j + " is missing level data, skipping"); + return null; + } else if (!nbttagcompound.getCompound("Level").hasKeyOfType("Sections", 9)) { + a.error("Chunk file at " + i + "," + j + " is missing block data, skipping"); + return null; + } else { + Chunk chunk = this.a(world, nbttagcompound.getCompound("Level")); + + if (!chunk.a(i, j)) { + a.error("Chunk file at " + i + "," + j + " is in the wrong location; relocating. (Expected " + i + ", " + j + ", got " + chunk.locX + ", " + chunk.locZ + ")"); + nbttagcompound.getCompound("Level").setInt("xPos", i); // CraftBukkit - .getCompound("Level") + nbttagcompound.getCompound("Level").setInt("zPos", j); // CraftBukkit - .getCompound("Level") + + // CraftBukkit start - Have to move tile entities since we don't load them at this stage + NBTTagList tileEntities = nbttagcompound.getCompound("Level").getList("TileEntities", 10); + if (tileEntities != null) { + for (int te = 0; te < tileEntities.size(); te++) { + NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.get(te); + int x = tileEntity.getInt("x") - chunk.locX * 16; + int z = tileEntity.getInt("z") - chunk.locZ * 16; + tileEntity.setInt("x", i * 16 + x); + tileEntity.setInt("z", j * 16 + z); + } + } + // CraftBukkit end + chunk = this.a(world, nbttagcompound.getCompound("Level")); + } + + // CraftBukkit start + Object[] data = new Object[2]; + data[0] = chunk; + data[1] = nbttagcompound; + return data; + // CraftBukkit end + } + } + + public void a(World world, Chunk chunk) { + this.worldInQuestion = world; + // CraftBukkit start - "handle" exception + try { + world.G(); + } catch (ExceptionWorldConflict ex) { + ex.printStackTrace(); + } + // CraftBukkit end + + try { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound.set("Level", nbttagcompound1); + this.a(chunk, world, nbttagcompound1); + this.a(chunk.l(), nbttagcompound); + } catch (Exception exception) { + exception.printStackTrace(); + } + this.worldInQuestion = null; + } + + private World worldInQuestion; + protected void a(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) { + Object object = this.d; + this.start = System.nanoTime(); + synchronized (this.d) { + worldInQuestion.obtainLock += differenceAndReset(); + // Spigot start + if (this.pendingSaves.put(chunkcoordintpair, new PendingChunkToSave(chunkcoordintpair, nbttagcompound)) != null) { + worldInQuestion.pendingSavesPut += differenceAndReset(); + return; + } + worldInQuestion.pendingSavesPut += differenceAndReset(); + // Spigot end + FileIOThread.a.a(this); + worldInQuestion.fileIOThreadAddition += differenceAndReset(); + } + } + + public boolean c() { + PendingChunkToSave pendingchunktosave = null; + Object object = this.d; + + synchronized (this.d) { + // Spigot start + if (this.pendingSaves.isEmpty()) { + return false; + } + + pendingchunktosave = this.pendingSaves.values().iterator().next(); + this.pendingSaves.remove(pendingchunktosave.a); + // Spigot end + } + + if (pendingchunktosave != null) { + try { + this.a(pendingchunktosave); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + return true; + } + + public void a(PendingChunkToSave pendingchunktosave) throws java.io.IOException { // CraftBukkit - public -> private, added throws + DataOutputStream dataoutputstream = RegionFileCache.d(this.e, pendingchunktosave.a.x, pendingchunktosave.a.z); + + NBTCompressedStreamTools.a(pendingchunktosave.b, (DataOutput) dataoutputstream); + dataoutputstream.close(); + } + + public void b(World world, Chunk chunk) {} + + public void a() {} + + public void b() { + while (this.c()) { + ; + } + } + + private long start; + private long differenceAndReset() { + long difference = System.nanoTime() - start; + start = System.nanoTime(); + return difference; + } + + private void a(Chunk chunk, World world, NBTTagCompound nbttagcompound) { + start = System.nanoTime(); + nbttagcompound.setByte("V", (byte) 1); + nbttagcompound.setInt("xPos", chunk.locX); + nbttagcompound.setInt("zPos", chunk.locZ); + nbttagcompound.setLong("LastUpdate", world.getTime()); + nbttagcompound.setIntArray("HeightMap", chunk.heightMap); + nbttagcompound.setBoolean("TerrainPopulated", chunk.done); + nbttagcompound.setBoolean("LightPopulated", chunk.lit); + nbttagcompound.setLong("InhabitedTime", chunk.s); + world.writeStartNBT += differenceAndReset(); + ChunkSection[] achunksection = chunk.getSections(); + NBTTagList nbttaglist = new NBTTagList(); + boolean flag = !world.worldProvider.g; + ChunkSection[] achunksection1 = achunksection; + int i = achunksection.length; + + NBTTagCompound nbttagcompound1; + + for (int j = 0; j < i; ++j) { + ChunkSection chunksection = achunksection1[j]; + + if (chunksection != null) { + nbttagcompound1 = new NBTTagCompound(); + nbttagcompound1.setByte("Y", (byte) (chunksection.getYPosition() >> 4 & 255)); + nbttagcompound1.setByteArray("Blocks", chunksection.getIdArray()); + // MineHQ start - 1.7 has no extended block IDs + /* + if (chunksection.getExtendedIdArray() != null) { + nbttagcompound1.setByteArray("Add", chunksection.getExtendedIdArray().a); + } + */ + // MineHQ end + nbttagcompound1.setByteArray("Data", chunksection.getDataArray().a); + nbttagcompound1.setByteArray("BlockLight", chunksection.getEmittedLightArray().a); + if (flag) { + nbttagcompound1.setByteArray("SkyLight", chunksection.getSkyLightArray().a); + } else { + nbttagcompound1.setByteArray("SkyLight", new byte[chunksection.getEmittedLightArray().a.length]); + } + + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Sections", nbttaglist); + world.writeSections += differenceAndReset(); + nbttagcompound.setByteArray("Biomes", chunk.m()); + world.writeBiomes += differenceAndReset(); + chunk.o = false; + NBTTagList nbttaglist1 = new NBTTagList(); + + Iterator iterator; + + for (i = 0; i < chunk.entitySlices.length; ++i) { + iterator = chunk.entitySlices[i].iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + nbttagcompound1 = new NBTTagCompound(); + if (entity.d(nbttagcompound1)) { + chunk.o = true; + nbttaglist1.add(nbttagcompound1); + } + } + } + + nbttagcompound.set("Entities", nbttaglist1); + world.writeEntities += differenceAndReset(); + NBTTagList nbttaglist2 = new NBTTagList(); + + iterator = chunk.tileEntities.values().iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + + nbttagcompound1 = new NBTTagCompound(); + tileentity.b(nbttagcompound1); + nbttaglist2.add(nbttagcompound1); + } + + nbttagcompound.set("TileEntities", nbttaglist2); + world.writeTileEntities += differenceAndReset(); + List list = world.a(chunk, false); + + if (list != null) { + long k = world.getTime(); + NBTTagList nbttaglist3 = new NBTTagList(); + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + NextTickListEntry nextticklistentry = (NextTickListEntry) iterator1.next(); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + + nbttagcompound2.setInt("i", Block.getId(nextticklistentry.a())); + nbttagcompound2.setInt("x", nextticklistentry.a); + nbttagcompound2.setInt("y", nextticklistentry.b); + nbttagcompound2.setInt("z", nextticklistentry.c); + nbttagcompound2.setInt("t", (int) (nextticklistentry.d - k)); + nbttagcompound2.setInt("p", nextticklistentry.e); + nbttaglist3.add(nbttagcompound2); + } + + nbttagcompound.set("TileTicks", nbttaglist3); + } + world.writeTileTicks += differenceAndReset(); + } + + private Chunk a(World world, NBTTagCompound nbttagcompound) { + int i = nbttagcompound.getInt("xPos"); + int j = nbttagcompound.getInt("zPos"); + Chunk chunk = new Chunk(world, i, j); + + chunk.heightMap = nbttagcompound.getIntArray("HeightMap"); + chunk.done = nbttagcompound.getBoolean("TerrainPopulated"); + chunk.lit = nbttagcompound.getBoolean("LightPopulated"); + chunk.s = nbttagcompound.getLong("InhabitedTime"); + NBTTagList nbttaglist = nbttagcompound.getList("Sections", 10); + byte b0 = 16; + ChunkSection[] achunksection = new ChunkSection[b0]; + boolean flag = !world.worldProvider.g; + + for (int k = 0; k < nbttaglist.size(); ++k) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(k); + byte b1 = nbttagcompound1.getByte("Y"); + ChunkSection chunksection = new ChunkSection(b1 << 4, flag); + + chunksection.setIdArray(nbttagcompound1.getByteArray("Blocks")); + // MineHQ start - 1.7 has no extended block IDs + /* + if (nbttagcompound1.hasKeyOfType("Add", 7)) { + chunksection.setExtendedIdArray(new NibbleArray(nbttagcompound1.getByteArray("Add"), 4)); + } + */ + // MineHQ end + + chunksection.setDataArray(new NibbleArray(nbttagcompound1.getByteArray("Data"), 4)); + chunksection.setEmittedLightArray(new NibbleArray(nbttagcompound1.getByteArray("BlockLight"), 4)); + if (flag) { + chunksection.setSkyLightArray(new NibbleArray(nbttagcompound1.getByteArray("SkyLight"), 4)); + } + + chunksection.recalcBlockCounts(); + achunksection[b1] = chunksection; + } + + chunk.a(achunksection); + if (nbttagcompound.hasKeyOfType("Biomes", 7)) { + chunk.a(nbttagcompound.getByteArray("Biomes")); + } + + // CraftBukkit start - End this method here and split off entity loading to another method + return chunk; + } + + public void loadEntities(Chunk chunk, NBTTagCompound nbttagcompound, World world) { + // CraftBukkit end + world.timings.syncChunkLoadEntitiesTimer.startTiming(); // Spigot + NBTTagList nbttaglist1 = nbttagcompound.getList("Entities", 10); + + if (nbttaglist1 != null) { + for (int l = 0; l < nbttaglist1.size(); ++l) { + NBTTagCompound nbttagcompound2 = nbttaglist1.get(l); + Entity entity = EntityTypes.a(nbttagcompound2, world); + + chunk.o = true; + if (entity != null) { + chunk.a(entity); + Entity entity1 = entity; + + for (NBTTagCompound nbttagcompound3 = nbttagcompound2; nbttagcompound3.hasKeyOfType("Riding", 10); nbttagcompound3 = nbttagcompound3.getCompound("Riding")) { + Entity entity2 = EntityTypes.a(nbttagcompound3.getCompound("Riding"), world); + + if (entity2 != null) { + chunk.a(entity2); + entity1.mount(entity2); + } + + entity1 = entity2; + } + } + } + } + world.timings.syncChunkLoadEntitiesTimer.stopTiming(); // Spigot + world.timings.syncChunkLoadTileEntitiesTimer.startTiming(); // Spigot + NBTTagList nbttaglist2 = nbttagcompound.getList("TileEntities", 10); + + if (nbttaglist2 != null) { + for (int i1 = 0; i1 < nbttaglist2.size(); ++i1) { + NBTTagCompound nbttagcompound4 = nbttaglist2.get(i1); + TileEntity tileentity = TileEntity.c(nbttagcompound4); + + if (tileentity != null) { + chunk.a(tileentity); + } + } + } + world.timings.syncChunkLoadTileEntitiesTimer.stopTiming(); // Spigot + world.timings.syncChunkLoadTileTicksTimer.startTiming(); // Spigot + + if (nbttagcompound.hasKeyOfType("TileTicks", 9)) { + NBTTagList nbttaglist3 = nbttagcompound.getList("TileTicks", 10); + + if (nbttaglist3 != null) { + for (int j1 = 0; j1 < nbttaglist3.size(); ++j1) { + NBTTagCompound nbttagcompound5 = nbttaglist3.get(j1); + + world.b(nbttagcompound5.getInt("x"), nbttagcompound5.getInt("y"), nbttagcompound5.getInt("z"), Block.getById(nbttagcompound5.getInt("i")), nbttagcompound5.getInt("t"), nbttagcompound5.getInt("p")); + } + } + } + world.timings.syncChunkLoadTileTicksTimer.stopTiming(); // Spigot + + // return chunk; // CraftBukkit + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ChunkSection.java b/vspigot-server/src/main/java/net/minecraft/server/ChunkSection.java new file mode 100644 index 0000000..ea28631 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ChunkSection.java @@ -0,0 +1,493 @@ +package net.minecraft.server; + +import net.valorhcf.ChunkSectionSnapshot; + +import java.util.Arrays; // CraftBukkit + +public class ChunkSection { + + private int yPos; + private int nonEmptyBlockCount; + private int tickingBlockCount; + private byte[] blockIds; + // private NibbleArray extBlockIds; // MineHQ - 1.7 has no extended block IDs + private NibbleArray blockData; + private NibbleArray emittedLight; + private NibbleArray skyLight; + // CraftBukkit start - Compact storage + private int compactId; + // private byte compactExtId; // MineHQ + private byte compactData; + private byte compactEmitted; + private byte compactSky; + public boolean isDirty = false; + + // Pre-generated (read-only!) NibbleArrays for every possible value, used for chunk saving + private static NibbleArray[] compactPregen = new NibbleArray[16]; + static { + for (int i = 0; i < 16; i++) { + compactPregen[i] = expandCompactNibble((byte) i); + } + } + + private static NibbleArray expandCompactNibble(byte value) { + byte[] data = new byte[2048]; + Arrays.fill(data, (byte) (value | (value << 4))); + return new NibbleArray(data, 4); + } + + private boolean canBeCompact(byte[] array) { + byte value = array[0]; + for (int i = 1; i < array.length; i++) { + if (value != array[i]) { + return false; + } + } + + return true; + } + // CraftBukkit end + + public ChunkSection(int i, boolean flag) { + this.yPos = i; + /* CraftBukkit - Start as null, using compact storage + this.blockIds = new byte[4096]; + this.blockData = new NibbleArray(this.blockIds.length, 4); + this.emittedLight = new NibbleArray(this.blockIds.length, 4); + if (flag) { + this.skyLight = new NibbleArray(this.blockIds.length, 4); + } + */ + if (!flag) { + this.compactSky = -1; + } + // CraftBukkit end + } + + // CraftBukkit start + public ChunkSection(int y, boolean flag, byte[] blkIds, byte[] extBlkIds) { + this.yPos = y; + this.setIdArray(blkIds); + // MineHQ start - 1.7 has no extended block IDs + /* + if (extBlkIds != null) { + this.setExtendedIdArray(new NibbleArray(extBlkIds, 4)); + } + */ + // MineHQ end + if (!flag) { + this.compactSky = -1; + } + this.recalcBlockCounts(); + } + // CraftBukkit end + + public Block getTypeId(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.blockIds == null) { + // MineHQ start - 1.7 has no extended block IDs + /* + int id = this.compactId; + if (this.extBlockIds == null) { + id |= this.compactExtId << 8; + } else { + id |= this.extBlockIds.a(i, j, k) << 8; + } + + return Block.getById(id); + */ + return Block.getById(this.compactId); + // MineHQ end + } + // CraftBukkit end + + int l = this.blockIds[j << 8 | k << 4 | i] & 255; + + // MineHQ start + /* + if (this.extBlockIds != null) { + l |= this.extBlockIds.a(i, j, k) << 8; + } + */ + // MineHQ end + return Block.getById(l); + } + + public void setTypeId(int i, int j, int k, Block block) { + // CraftBukkit start - Compact storage + Block block1 = this.getTypeId(i, j, k); + if (block == block1) { + return; + } + // CraftBukkit end + + if (block1 != Blocks.AIR) { + --this.nonEmptyBlockCount; + if (block1.isTicking()) { + --this.tickingBlockCount; + } + } + + if (block != Blocks.AIR) { + ++this.nonEmptyBlockCount; + if (block.isTicking()) { + ++this.tickingBlockCount; + } + } + + int i1 = Block.getId(block); + + // CraftBukkit start - Compact storage + if (this.blockIds == null) { + this.blockIds = new byte[4096]; + Arrays.fill(this.blockIds, (byte) (this.compactId & 255)); + } + // CraftBukkit end + + this.blockIds[j << 8 | k << 4 | i] = (byte) (i1 & 255); + // MineHQ start + isDirty = true; + /* + if (i1 > 255) { + if (this.extBlockIds == null) { + this.extBlockIds = expandCompactNibble(this.compactExtId); // CraftBukkit - Compact storage + } + + this.extBlockIds.a(i, j, k, (i1 & 3840) >> 8); + } else if (this.extBlockIds != null) { + this.extBlockIds.a(i, j, k, 0); + } + */ + // MineHQ end + } + + public int getData(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.blockData == null) { + return this.compactData; + } + // CraftBukkit end + return this.blockData.a(i, j, k); + } + + public void setData(int i, int j, int k, int l) { + // CraftBukkit start - Compact storage + if (this.blockData == null) { + if (this.compactData == l) { + return; + } + this.blockData = expandCompactNibble(this.compactData); + } + // CraftBukkit end + isDirty = true; // MineHQ + this.blockData.a(i, j, k, l); + } + + public boolean isEmpty() { + return this.nonEmptyBlockCount == 0; + } + + public boolean shouldTick() { + return this.tickingBlockCount > 0; + } + + public int getYPosition() { + return this.yPos; + } + + public void setSkyLight(int i, int j, int k, int l) { + // CraftBukkit start - Compact storage + if (this.skyLight == null) { + if (this.compactSky == l) { + return; + } + this.skyLight = expandCompactNibble(this.compactSky); + } + // CraftBukkit end + isDirty = true; // MineHQ + this.skyLight.a(i, j, k, l); + } + + public int getSkyLight(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.skyLight == null) { + return this.compactSky; + } + // CraftBukkit end + return this.skyLight.a(i, j, k); + } + + public void setEmittedLight(int i, int j, int k, int l) { + // CraftBukkit start - Compact storage + if (this.emittedLight == null) { + if (this.compactEmitted == l) { + return; + } + this.emittedLight = expandCompactNibble(this.compactEmitted); + } + // CraftBukkit end + isDirty = true; // MineHQ + this.emittedLight.a(i, j, k, l); + } + + public int getEmittedLight(int i, int j, int k) { + // CraftBukkit start - Compact storage + if (this.emittedLight == null) { + return this.compactEmitted; + } + // CraftBukkit end + return this.emittedLight.a(i, j, k); + } + + public void recalcBlockCounts() { + // CraftBukkit start - Optimize for speed + int cntNonEmpty = 0; + int cntTicking = 0; + + // MineHQ start - 1.7 has no extended block IDs + if (this.blockIds == null) { + int id = this.compactId; + if (id > 0) { + Block block = Block.getById(id); + if (block == null) { + this.compactId = 0; + } else { + cntNonEmpty = 4096; + if (block.isTicking()) { + cntTicking = 4096; + } + } + } + } else { + byte[] blkIds = this.blockIds; + for (int off = 0; off < blkIds.length; off++) { + int l = blkIds[off] & 0xFF; + if (l > 0) { + if (Block.getById(l) == null) { + blkIds[off] = 0; + } else { + ++cntNonEmpty; + if (Block.getById(l).isTicking()) { + ++cntTicking; + } + } + } + } + } + // MineHQ end + this.nonEmptyBlockCount = cntNonEmpty; + this.tickingBlockCount = cntTicking; + } + + public void old_recalcBlockCounts() { + // CraftBukkit end + this.nonEmptyBlockCount = 0; + this.tickingBlockCount = 0; + + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 16; ++j) { + for (int k = 0; k < 16; ++k) { + Block block = this.getTypeId(i, j, k); + + if (block != Blocks.AIR) { + ++this.nonEmptyBlockCount; + if (block.isTicking()) { + ++this.tickingBlockCount; + } + } + } + } + } + } + + public byte[] getIdArray() { + // CraftBukkit start - Compact storage + if (this.blockIds == null) { + byte[] ids = new byte[4096]; + Arrays.fill(ids, (byte) (this.compactId & 255)); + return ids; + } + // CraftBukkit end + return this.blockIds; + } + + // MineHQ start - 1.7 has no extended block IDs + /* + public NibbleArray getExtendedIdArray() { + // CraftBukkit start - Compact storage + if (this.extBlockIds == null && this.compactExtId != 0) { + return compactPregen[this.compactExtId]; + } + // CraftBukkit end + return this.extBlockIds; + } + */ + // MineHQ end + + public NibbleArray getDataArray() { + // CraftBukkit start - Compact storage + if (this.blockData == null) { + return compactPregen[this.compactData]; + } + // CraftBukkit end + return this.blockData; + } + + public NibbleArray getEmittedLightArray() { + // CraftBukkit start - Compact storage + if (this.emittedLight == null) { + return compactPregen[this.compactEmitted]; + } + // CraftBukkit end + return this.emittedLight; + } + + public NibbleArray getSkyLightArray() { + // CraftBukkit start - Compact storage + if (this.skyLight == null && this.compactSky != -1) { + return compactPregen[this.compactSky]; + } + // CraftBukkit end + return this.skyLight; + } + + public void setIdArray(byte[] abyte) { + // CraftBukkit start - Compact storage + if (abyte == null) { + this.compactId = 0; + this.blockIds = null; + return; + } else if (canBeCompact(abyte)) { + this.compactId = abyte[0] & 255; + return; + } + // CraftBukkit end + this.blockIds = this.validateByteArray(abyte); // CraftBukkit - Validate data + } + + // MineHQ start - 1.7 has no extended block IDs + /* + public void setExtendedIdArray(NibbleArray nibblearray) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactExtId = 0; + this.extBlockIds = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactExtId = (byte) (nibblearray.a(0, 0, 0) & 0xF); + return; + } + // CraftBukkit end + this.extBlockIds = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data + } + */ + // MineHQ end + + public void setDataArray(NibbleArray nibblearray) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactData = 0; + this.blockData = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactData = (byte) (nibblearray.a(0, 0, 0) & 0xF); + return; + } + // CraftBukkit end + this.blockData = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data + } + + public void setEmittedLightArray(NibbleArray nibblearray) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactEmitted = 0; + this.emittedLight = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactEmitted = (byte) (nibblearray.a(0, 0, 0) & 0xF); + return; + } + // CraftBukkit end + this.emittedLight = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data + } + + public void setSkyLightArray(NibbleArray nibblearray) { + // CraftBukkit start - Compact storage + if (nibblearray == null) { + this.compactSky = -1; + this.skyLight = null; + return; + } else if (canBeCompact(nibblearray.a)) { + this.compactSky = (byte) (nibblearray.a(0, 0, 0) & 0xF); + return; + } + // CraftBukkit end + this.skyLight = this.validateNibbleArray(nibblearray); // CraftBukkit - Validate data + } + + // CraftBukkit start - Validate array lengths + private NibbleArray validateNibbleArray(NibbleArray nibbleArray) { + if (nibbleArray != null && nibbleArray.a.length < 2048) { + byte[] newArray = new byte[2048]; + System.arraycopy(nibbleArray.a, 0, newArray, 0, nibbleArray.a.length); + nibbleArray = new NibbleArray(newArray, 4); + } + + return nibbleArray; + } + + private byte[] validateByteArray(byte[] byteArray) { + if (byteArray != null && byteArray.length < 4096) { + byte[] newArray = new byte[4096]; + System.arraycopy(byteArray, 0, newArray, 0, byteArray.length); + byteArray = newArray; + } + + return byteArray; + } + // CraftBukkit end + + // MineHQ start - chunk snapshot api + public ChunkSectionSnapshot createSnapshot() { + return new ChunkSectionSnapshot( + nonEmptyBlockCount, + tickingBlockCount, + clone(blockIds), + clone(blockData), + clone(emittedLight), + clone(skyLight), + compactId, + compactData, + compactEmitted, + compactSky); + } + + public void restoreSnapshot(ChunkSectionSnapshot snap) { + nonEmptyBlockCount = snap.getNonEmptyBlockCount(); + tickingBlockCount = snap.getTickingBlockCount(); + blockIds = clone(snap.getBlockIds()); + blockData = clone(snap.getBlockData()); + emittedLight = clone(snap.getEmittedLight()); + skyLight = clone(snap.getSkyLight()); + compactId = snap.getCompactId(); + compactData = snap.getCompactData(); + compactEmitted = snap.getCompactEmitted(); + compactSky = snap.getCompactSky(); + isDirty = true; + } + + private static byte[] clone(byte[] array) { + if (array != null) { + return array.clone(); + } + return null; + } + + private static NibbleArray clone(NibbleArray array) { + if (array != null) { + return array.clone(); + } + return null; + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ClientCommandOrdinalWrapper.java b/vspigot-server/src/main/java/net/minecraft/server/ClientCommandOrdinalWrapper.java new file mode 100644 index 0000000..04eacb8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ClientCommandOrdinalWrapper.java @@ -0,0 +1,27 @@ +package net.minecraft.server; + +// CraftBukkit - import package private class +class ClientCommandOrdinalWrapper { + + static final int[] a = new int[EnumClientCommand.values().length]; + + static { + try { + a[EnumClientCommand.PERFORM_RESPAWN.ordinal()] = 1; + } catch (NoSuchFieldError nosuchfielderror) { + ; + } + + try { + a[EnumClientCommand.REQUEST_STATS.ordinal()] = 2; + } catch (NoSuchFieldError nosuchfielderror1) { + ; + } + + try { + a[EnumClientCommand.OPEN_INVENTORY_ACHIEVEMENT.ordinal()] = 3; + } catch (NoSuchFieldError nosuchfielderror2) { + ; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java new file mode 100644 index 0000000..278832c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java @@ -0,0 +1,238 @@ +package net.minecraft.server; + +import java.text.SimpleDateFormat; +import java.util.Date; + +// CraftBukkit start +import java.util.ArrayList; +import org.apache.logging.log4j.Level; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import com.google.common.base.Joiner; +// CraftBukkit end + +public abstract class CommandBlockListenerAbstract implements ICommandListener { + + private static final SimpleDateFormat a = new SimpleDateFormat("HH:mm:ss"); + private int b; + private boolean c = true; + private IChatBaseComponent d = null; + public String e = ""; // CraftBukkit - private -> public + private String f = "@"; + protected org.bukkit.command.CommandSender sender; // CraftBukkit - add sender; + + public CommandBlockListenerAbstract() {} + + public int g() { + return this.b; + } + + public IChatBaseComponent h() { + return this.d; + } + + public void a(NBTTagCompound nbttagcompound) { + nbttagcompound.setString("Command", this.e); + nbttagcompound.setInt("SuccessCount", this.b); + nbttagcompound.setString("CustomName", this.f); + if (this.d != null) { + nbttagcompound.setString("LastOutput", ChatSerializer.a(this.d)); + } + + nbttagcompound.setBoolean("TrackOutput", this.c); + } + + public void b(NBTTagCompound nbttagcompound) { + this.e = nbttagcompound.getString("Command"); + this.b = nbttagcompound.getInt("SuccessCount"); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.f = nbttagcompound.getString("CustomName"); + } + + if (nbttagcompound.hasKeyOfType("LastOutput", 8)) { + this.d = ChatSerializer.a(nbttagcompound.getString("LastOutput")); + } + + if (nbttagcompound.hasKeyOfType("TrackOutput", 1)) { + this.c = nbttagcompound.getBoolean("TrackOutput"); + } + } + + public boolean a(int i, String s) { + return i <= 2; + } + + public void setCommand(String s) { + this.e = s; + } + + public String getCommand() { + return this.e; + } + + public void a(World world) { + if (world.isStatic) { + this.b = 0; + } + + MinecraftServer minecraftserver = MinecraftServer.getServer(); + + if (minecraftserver != null && minecraftserver.getEnableCommandBlock()) { + // CraftBukkit start - Handle command block commands using Bukkit dispatcher + org.bukkit.command.SimpleCommandMap commandMap = minecraftserver.server.getCommandMap(); + Joiner joiner = Joiner.on(" "); + String command = this.e; + if (this.e.startsWith("/")) { + command = this.e.substring(1); + } + String[] args = command.split(" "); + ArrayList commands = new ArrayList(); + + // Block disallowed commands + if (args[0].equalsIgnoreCase("stop") || args[0].equalsIgnoreCase("kick") || args[0].equalsIgnoreCase("op") || + args[0].equalsIgnoreCase("deop") || args[0].equalsIgnoreCase("ban") || args[0].equalsIgnoreCase("ban-ip") || + args[0].equalsIgnoreCase("pardon") || args[0].equalsIgnoreCase("pardon-ip") || args[0].equalsIgnoreCase("reload")) { + this.b = 0; + return; + } + + // If the world has no players don't run + if (this.getWorld().players.isEmpty()) { + this.b = 0; + return; + } + + // Handle vanilla commands; + if (minecraftserver.server.getCommandBlockOverride(args[0])) { + org.bukkit.command.Command commandBlockCommand = commandMap.getCommand("minecraft:" + args[0]); + if (commandBlockCommand instanceof VanillaCommandWrapper) { + this.b = ((VanillaCommandWrapper) commandBlockCommand).dispatchVanillaCommandBlock(this, this.e); + return; + } + } + + // Spigot start - check for manually prefixed command or commands that don't need a prefix + org.bukkit.command.Command commandBlockCommand = commandMap.getCommand(args[0]); + if (commandBlockCommand instanceof VanillaCommandWrapper) { + this.b = ((VanillaCommandWrapper) commandBlockCommand).dispatchVanillaCommandBlock(this, this.e); + return; + } + // Spigot end + + // Make sure this is a valid command + if (commandMap.getCommand(args[0]) == null) { + this.b = 0; + return; + } + + // testfor command requires special handling + if (args[0].equalsIgnoreCase("testfor")) { + if (args.length < 2) { + this.b = 0; + return; + } + + EntityPlayer[] players = PlayerSelector.getPlayers(this, args[1]); + + if (players != null && players.length > 0) { + this.b = players.length; + return; + } else { + EntityPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(args[1]); + if (player == null) { + this.b = 0; + return; + } else { + this.b = 1; + return; + } + } + } + + commands.add(args); + + // Find positions of command block syntax, if any + ArrayList newCommands = new ArrayList(); + for (int i = 0; i < args.length; i++) { + if (PlayerSelector.isPattern(args[i])) { + for (int j = 0; j < commands.size(); j++) { + newCommands.addAll(this.buildCommands(commands.get(j), i)); + } + ArrayList temp = commands; + commands = newCommands; + newCommands = temp; + newCommands.clear(); + } + } + + int completed = 0; + + // Now dispatch all of the commands we ended up with + for (int i = 0; i < commands.size(); i++) { + try { + if (commandMap.dispatch(sender, joiner.join(java.util.Arrays.asList(commands.get(i))))) { + completed++; + } + } catch (Throwable exception) { + if(this instanceof TileEntityCommandListener) { + TileEntityCommandListener listener = (TileEntityCommandListener) this; + MinecraftServer.getLogger().log(Level.WARN, String.format("CommandBlock at (%d,%d,%d) failed to handle command", listener.getChunkCoordinates().x, listener.getChunkCoordinates().y, listener.getChunkCoordinates().z), exception); + } else if (this instanceof EntityMinecartCommandBlockListener) { + EntityMinecartCommandBlockListener listener = (EntityMinecartCommandBlockListener) this; + MinecraftServer.getLogger().log(Level.WARN, String.format("MinecartCommandBlock at (%d,%d,%d) failed to handle command", listener.getChunkCoordinates().x, listener.getChunkCoordinates().y, listener.getChunkCoordinates().z), exception); + } else { + MinecraftServer.getLogger().log(Level.WARN, String.format("Unknown CommandBlock failed to handle command"), exception); + } + } + } + + this.b = completed; + // CraftBukkit end + } else { + this.b = 0; + } + } + + // CraftBukkit start + private ArrayList buildCommands(String[] args, int pos) { + ArrayList commands = new ArrayList(); + EntityPlayer[] players = PlayerSelector.getPlayers(this, args[pos]); + if (players != null) { + for (EntityPlayer player : players) { + if (player.world != this.getWorld()) { + continue; + } + String[] command = args.clone(); + command[pos] = player.getName(); + commands.add(command); + } + } + + return commands; + } + // CraftBukkit end + + public String getName() { + return this.f; + } + + public IChatBaseComponent getScoreboardDisplayName() { + return new ChatComponentText(this.getName()); + } + + public void setName(String s) { + this.f = s; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + if (this.c && this.getWorld() != null && !this.getWorld().isStatic) { + this.d = (new ChatComponentText("[" + a.format(new Date()) + "] ")).addSibling(ichatbasecomponent); + this.e(); + } + } + + public abstract void e(); + + public void b(IChatBaseComponent ichatbasecomponent) { + this.d = ichatbasecomponent; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/CommandDispatcher.java b/vspigot-server/src/main/java/net/minecraft/server/CommandDispatcher.java new file mode 100644 index 0000000..84bcca1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/CommandDispatcher.java @@ -0,0 +1,94 @@ +package net.minecraft.server; + +import java.util.Iterator; + +public class CommandDispatcher extends CommandHandler implements ICommandDispatcher { + + public CommandDispatcher() { + this.a(new CommandTime()); + this.a(new CommandGamemode()); + this.a(new CommandDifficulty()); + this.a(new CommandGamemodeDefault()); + this.a(new CommandKill()); + this.a(new CommandToggleDownfall()); + this.a(new CommandWeather()); + this.a(new CommandXp()); + this.a(new CommandTp()); + this.a(new CommandGive()); + this.a(new CommandEffect()); + this.a(new CommandEnchant()); + this.a(new CommandMe()); + this.a(new CommandSeed()); + this.a(new CommandHelp()); + this.a(new CommandDebug()); + this.a(new CommandTell()); + this.a(new CommandSay()); + this.a(new CommandSpawnpoint()); + this.a(new CommandSetWorldSpawn()); + this.a(new CommandGamerule()); + this.a(new CommandClear()); + this.a(new CommandTestFor()); + this.a(new CommandSpreadPlayers()); + this.a(new CommandPlaySound()); + this.a(new CommandScoreboard()); + this.a(new CommandAchievement()); + this.a(new CommandSummon()); + this.a(new CommandSetBlock()); + this.a(new CommandTestForBlock()); + this.a(new CommandTellRaw()); + if (MinecraftServer.getServer().X()) { + this.a(new CommandOp()); + this.a(new CommandDeop()); + this.a(new CommandStop()); + this.a(new CommandSaveAll()); + this.a(new CommandSaveOff()); + this.a(new CommandSaveOn()); + this.a(new CommandBanIp()); + this.a(new CommandPardonIP()); + this.a(new CommandBan()); + this.a(new CommandBanList()); + this.a(new CommandPardon()); + this.a(new CommandKick()); + this.a(new CommandList()); + this.a(new CommandWhitelist()); + this.a(new CommandIdleTimeout()); + this.a(new CommandNetstat()); + } else { + this.a(new CommandPublish()); + } + + CommandAbstract.a((ICommandDispatcher) this); + } + + public void a(ICommandListener icommandlistener, ICommand icommand, int i, String s, Object... aobject) { + boolean flag = true; + + if (icommandlistener instanceof CommandBlockListenerAbstract && !MinecraftServer.getServer().worldServer[0].getGameRules().getBoolean("commandBlockOutput")) { + flag = false; + } + + ChatMessage chatmessage = new ChatMessage("chat.type.admin", new Object[] { icommandlistener.getName(), new ChatMessage(s, aobject)}); + + chatmessage.getChatModifier().setColor(EnumChatFormat.GRAY); + chatmessage.getChatModifier().setItalic(Boolean.valueOf(true)); + if (flag) { + Iterator iterator = MinecraftServer.getServer().getPlayerList().players.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (entityhuman != icommandlistener && MinecraftServer.getServer().getPlayerList().isOp(entityhuman.getProfile()) && icommand.canUse(entityhuman) && (!(icommandlistener instanceof RemoteControlCommandListener) || MinecraftServer.getServer().m())) { + entityhuman.sendMessage(chatmessage); + } + } + } + + if (icommandlistener != MinecraftServer.getServer() && !org.spigotmc.SpigotConfig.silentCommandBlocks) { // Spigot + MinecraftServer.getServer().sendMessage(chatmessage); + } + + if ((i & 1) != 1) { + icommandlistener.sendMessage(new ChatMessage(s, aobject)); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Container.java b/vspigot-server/src/main/java/net/minecraft/server/Container.java new file mode 100644 index 0000000..da16a48 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Container.java @@ -0,0 +1,618 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +// CraftBukkit start +import java.util.HashMap; +import java.util.Map; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.Event.Result; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.InventoryView; +// CraftBukkit end + +public abstract class Container { + + public List b = new ArrayList(); + public List c = new ArrayList(); + public int windowId; + private int dragType = -1; + public int g; // CraftBukkit - private -> public + private final Set h = new HashSet(); + protected List listeners = new ArrayList(); + private Set i = new HashSet(); + private int tickCount; // Spigot + + // CraftBukkit start + public boolean checkReachable = true; + public abstract InventoryView getBukkitView(); + public void transferTo(Container other, org.bukkit.craftbukkit.entity.CraftHumanEntity player) { + InventoryView source = this.getBukkitView(), destination = other.getBukkitView(); + ((CraftInventory) source.getTopInventory()).getInventory().onClose(player); + ((CraftInventory) source.getBottomInventory()).getInventory().onClose(player); + ((CraftInventory) destination.getTopInventory()).getInventory().onOpen(player); + ((CraftInventory) destination.getBottomInventory()).getInventory().onOpen(player); + } + // CraftBukkit end + + public Container() {} + + protected Slot a(Slot slot) { + slot.rawSlotIndex = this.c.size(); + this.c.add(slot); + this.b.add(null); + return slot; + } + + public void addSlotListener(ICrafting icrafting) { + if (this.listeners.contains(icrafting)) { + throw new IllegalArgumentException("Listener already listening"); + } else { + this.listeners.add(icrafting); + icrafting.a(this, this.a()); + this.b(); + } + } + + public List a() { + ArrayList arraylist = new ArrayList(this.c.size()); // Velt + + for (int i = 0; i < this.c.size(); ++i) { + arraylist.add(((Slot) this.c.get(i)).getItem()); + } + + return arraylist; + } + + public void b() { + for (int i = 0; i < this.c.size(); ++i) { + ItemStack itemstack = ((Slot) this.c.get(i)).getItem(); + ItemStack itemstack1 = (ItemStack) this.b.get(i); + + if (!ItemStack.fastMatches(itemstack1, itemstack) || (tickCount % 20 == 0 && !ItemStack.matches(itemstack1, itemstack))) { // Spigot + itemstack1 = itemstack == null ? null : itemstack.cloneItemStack(); + this.b.set(i, itemstack1); + + for (int j = 0; j < this.listeners.size(); ++j) { + ((ICrafting) this.listeners.get(j)).a(this, i, itemstack1); + } + } + } + + tickCount++; // Spigot + } + + public boolean a(EntityHuman entityhuman, int i) { + return false; + } + + public Slot getSlot(IInventory iinventory, int i) { + for (int j = 0; j < this.c.size(); ++j) { + Slot slot = (Slot) this.c.get(j); + + if (slot.a(iinventory, i)) { + return slot; + } + } + + return null; + } + + public Slot getSlot(int i) { + return (Slot) this.c.get(i); + } + + public ItemStack b(EntityHuman entityhuman, int i) { + Slot slot = (Slot) this.c.get(i); + + return slot != null ? slot.getItem() : null; + } + + public ItemStack clickItem(int i, int j, int k, EntityHuman entityhuman) { + ItemStack itemstack = null; + PlayerInventory playerinventory = entityhuman.inventory; + int l; + ItemStack itemstack1; + + if (k == 5) { + int i1 = this.g; + + this.g = c(j); + if ((i1 != 1 || this.g != 2) && i1 != this.g) { + this.d(); + } else if (playerinventory.getCarried() == null) { + this.d(); + } else if (this.g == 0) { + this.dragType = b(j); + if (d(this.dragType)) { + this.g = 1; + this.h.clear(); + } else { + this.d(); + } + } else if (this.g == 1) { + Slot slot = (Slot) this.c.get(i); + + if (slot != null && a(slot, playerinventory.getCarried(), true) && slot.isAllowed(playerinventory.getCarried()) && playerinventory.getCarried().count > this.h.size() && this.b(slot)) { + this.h.add(slot); + } + } else if (this.g == 2) { + if (!this.h.isEmpty()) { + itemstack1 = playerinventory.getCarried().cloneItemStack(); + l = playerinventory.getCarried().count; + Iterator iterator = this.h.iterator(); + + Map draggedSlots = new HashMap(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) + while (iterator.hasNext()) { + Slot slot1 = (Slot) iterator.next(); + + if (slot1 != null && a(slot1, playerinventory.getCarried(), true) && slot1.isAllowed(playerinventory.getCarried()) && playerinventory.getCarried().count >= this.h.size() && this.b(slot1)) { + ItemStack itemstack2 = itemstack1.cloneItemStack(); + int j1 = slot1.hasItem() ? slot1.getItem().count : 0; + + a(this.h, this.dragType, itemstack2, j1); + if (itemstack2.count > itemstack2.getMaxStackSize()) { + itemstack2.count = itemstack2.getMaxStackSize(); + } + + if (itemstack2.count > slot1.getMaxStackSize()) { + itemstack2.count = slot1.getMaxStackSize(); + } + + l -= itemstack2.count - j1; + draggedSlots.put(slot1.rawSlotIndex, itemstack2); // CraftBukkit - Put in map instead of setting + } + } + + // CraftBukkit start - InventoryDragEvent + InventoryView view = getBukkitView(); + org.bukkit.inventory.ItemStack newcursor = CraftItemStack.asCraftMirror(itemstack1); + newcursor.setAmount(l); + Map eventmap = new HashMap(); + for (Map.Entry ditem : draggedSlots.entrySet()) { + eventmap.put(ditem.getKey(), CraftItemStack.asBukkitCopy(ditem.getValue())); + } + + // It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory. + ItemStack oldCursor = playerinventory.getCarried(); + playerinventory.setCarried(CraftItemStack.asNMSCopy(newcursor)); + + InventoryDragEvent event = new InventoryDragEvent(view, (newcursor.getType() != org.bukkit.Material.AIR ? newcursor : null), CraftItemStack.asBukkitCopy(oldCursor), this.dragType == 1, eventmap); + entityhuman.world.getServer().getPluginManager().callEvent(event); + + // Whether or not a change was made to the inventory that requires an update. + boolean needsUpdate = event.getResult() != Result.DEFAULT; + + if (event.getResult() != Result.DENY) { + for (Map.Entry dslot : draggedSlots.entrySet()) { + view.setItem(dslot.getKey(), CraftItemStack.asBukkitCopy(dslot.getValue())); + } + // The only time the carried item will be set to null is if the inventory is closed by the server. + // If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early. + if (playerinventory.getCarried() != null) { + playerinventory.setCarried(CraftItemStack.asNMSCopy(event.getCursor())); + needsUpdate = true; + + } + } else { + playerinventory.setCarried(oldCursor); + } + + if (needsUpdate && entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).updateInventory(this); + } + // CraftBukkit end + } + + this.d(); + } else { + this.d(); + } + } else if (this.g != 0) { + this.d(); + } else { + Slot slot2; + int k1; + ItemStack itemstack3; + + if ((k == 0 || k == 1) && (j == 0 || j == 1)) { + if (i == -999) { + if (playerinventory.getCarried() != null && i == -999) { + if (j == 0) { + entityhuman.drop(playerinventory.getCarried(), true); + playerinventory.setCarried((ItemStack) null); + } + + if (j == 1) { + // CraftBukkit start - Store a reference + ItemStack itemstack4 = playerinventory.getCarried(); + if (itemstack4.count > 0) { + entityhuman.drop(itemstack4.a(1), true); + } + + if (itemstack4.count == 0) { + // CraftBukkit end + playerinventory.setCarried((ItemStack) null); + } + } + } + } else if (k == 1) { + if (i < 0) { + return null; + } + + slot2 = (Slot) this.c.get(i); + if (slot2 != null && slot2.isAllowed(entityhuman)) { + itemstack1 = this.b(entityhuman, i); + if (itemstack1 != null) { + Item item = itemstack1.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (slot2.getItem() != null && slot2.getItem().getItem() == item) { + this.a(i, j, true, entityhuman); + } + } + } + } else { + if (i < 0) { + return null; + } + + slot2 = (Slot) this.c.get(i); + if (slot2 != null) { + itemstack1 = slot2.getItem(); + ItemStack itemstack4 = playerinventory.getCarried(); + + if (itemstack1 != null) { + itemstack = itemstack1.cloneItemStack(); + } + + if (itemstack1 == null) { + if (itemstack4 != null && slot2.isAllowed(itemstack4)) { + k1 = j == 0 ? itemstack4.count : 1; + if (k1 > slot2.getMaxStackSize()) { + k1 = slot2.getMaxStackSize(); + } + + if (itemstack4.count >= k1) { + slot2.set(itemstack4.a(k1)); + } + + if (itemstack4.count == 0) { + playerinventory.setCarried((ItemStack) null); + // CraftBukkit start - Update client cursor if we didn't empty it + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, entityhuman.inventory.getCarried())); + } + // CraftBukkit end + } + } else if (slot2.isAllowed(entityhuman)) { + if (itemstack4 == null) { + k1 = j == 0 ? itemstack1.count : (itemstack1.count + 1) / 2; + itemstack3 = slot2.a(k1); + playerinventory.setCarried(itemstack3); + if (itemstack1.count == 0) { + slot2.set((ItemStack) null); + } + + slot2.a(entityhuman, playerinventory.getCarried()); + } else if (slot2.isAllowed(itemstack4)) { + if (itemstack1.getItem() == itemstack4.getItem() && itemstack1.getData() == itemstack4.getData() && ItemStack.equals(itemstack1, itemstack4)) { + k1 = j == 0 ? itemstack4.count : 1; + if (k1 > slot2.getMaxStackSize() - itemstack1.count) { + k1 = slot2.getMaxStackSize() - itemstack1.count; + } + + if (k1 > itemstack4.getMaxStackSize() - itemstack1.count) { + k1 = itemstack4.getMaxStackSize() - itemstack1.count; + } + + itemstack4.a(k1); + if (itemstack4.count == 0) { + playerinventory.setCarried((ItemStack) null); + // CraftBukkit start - Update client cursor if we didn't empty it + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, entityhuman.inventory.getCarried())); + } + // CraftBukkit end + + itemstack1.count += k1; + } else if (itemstack4.count <= slot2.getMaxStackSize()) { + slot2.set(itemstack4); + playerinventory.setCarried(itemstack1); + } + } else if (itemstack1.getItem() == itemstack4.getItem() && itemstack4.getMaxStackSize() > 1 && (!itemstack1.usesData() || itemstack1.getData() == itemstack4.getData()) && ItemStack.equals(itemstack1, itemstack4)) { + k1 = itemstack1.count; + // CraftBukkit start - itemstack4.getMaxStackSize() -> maxStack + int maxStack = Math.min(itemstack4.getMaxStackSize(), slot2.getMaxStackSize()); + if (k1 > 0 && k1 + itemstack4.count <= maxStack) { + // CraftBukkit end + itemstack4.count += k1; + itemstack1 = slot2.a(k1); + if (itemstack1.count == 0) { + slot2.set((ItemStack) null); + } + + slot2.a(entityhuman, playerinventory.getCarried()); + // CraftBukkit start - Update client cursor if we didn't empty it + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, entityhuman.inventory.getCarried())); + } + // CraftBukkit end + } + } + + slot2.f(); + // CraftBukkit start - Make sure the client has the right slot contents + if (entityhuman instanceof EntityPlayer && slot2.getMaxStackSize() != 64) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, slot2.rawSlotIndex, slot2.getItem())); + // Updating a crafting inventory makes the client reset the result slot, have to send it again + if (this.getBukkitView().getType() == InventoryType.WORKBENCH || this.getBukkitView().getType() == InventoryType.CRAFTING) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, 0, this.getSlot(0).getItem())); + } + } + // CraftBukkit end + } + } + } else if (k == 2 && j >= 0 && j < 9) { + slot2 = (Slot) this.c.get(i); + if (slot2.isAllowed(entityhuman)) { + itemstack1 = playerinventory.getItem(j); + boolean flag = itemstack1 == null || slot2.inventory == playerinventory && slot2.isAllowed(itemstack1); + + k1 = -1; + if (!flag) { + k1 = playerinventory.getFirstEmptySlotIndex(); + flag |= k1 > -1; + } + + if (slot2.hasItem() && flag) { + itemstack3 = slot2.getItem(); + playerinventory.setItem(j, itemstack3.cloneItemStack()); + if ((slot2.inventory != playerinventory || !slot2.isAllowed(itemstack1)) && itemstack1 != null) { + if (k1 > -1) { + playerinventory.pickup(itemstack1); + slot2.a(itemstack3.count); + slot2.set((ItemStack) null); + slot2.a(entityhuman, itemstack3); + } + } else { + slot2.a(itemstack3.count); + slot2.set(itemstack1); + slot2.a(entityhuman, itemstack3); + } + } else if (!slot2.hasItem() && itemstack1 != null && slot2.isAllowed(itemstack1)) { + playerinventory.setItem(j, (ItemStack) null); + slot2.set(itemstack1); + } + } + } else if (k == 3 && entityhuman.abilities.canInstantlyBuild && playerinventory.getCarried() == null && i >= 0) { + slot2 = (Slot) this.c.get(i); + if (slot2 != null && slot2.hasItem()) { + itemstack1 = slot2.getItem().cloneItemStack(); + itemstack1.count = itemstack1.getMaxStackSize(); + playerinventory.setCarried(itemstack1); + } + } else if (k == 4 && playerinventory.getCarried() == null && i >= 0) { + slot2 = (Slot) this.c.get(i); + if (slot2 != null && slot2.hasItem() && slot2.isAllowed(entityhuman)) { + itemstack1 = slot2.a(j == 0 ? 1 : slot2.getItem().count); + slot2.a(entityhuman, itemstack1); + entityhuman.drop(itemstack1, true); + } + } else if (k == 6 && i >= 0) { + slot2 = (Slot) this.c.get(i); + itemstack1 = playerinventory.getCarried(); + if (itemstack1 != null && (slot2 == null || !slot2.hasItem() || !slot2.isAllowed(entityhuman))) { + l = j == 0 ? 0 : this.c.size() - 1; + k1 = j == 0 ? 1 : -1; + + for (int l1 = 0; l1 < 2; ++l1) { + for (int i2 = l; i2 >= 0 && i2 < this.c.size() && itemstack1.count < itemstack1.getMaxStackSize(); i2 += k1) { + Slot slot3 = (Slot) this.c.get(i2); + + if (slot3.hasItem() && a(slot3, itemstack1, true) && slot3.isAllowed(entityhuman) && this.a(itemstack1, slot3) && (l1 != 0 || slot3.getItem().count != slot3.getItem().getMaxStackSize())) { + int j2 = Math.min(itemstack1.getMaxStackSize() - itemstack1.count, slot3.getItem().count); + ItemStack itemstack5 = slot3.a(j2); + + itemstack1.count += j2; + if (itemstack5.count <= 0) { + slot3.set((ItemStack) null); + } + + slot3.a(entityhuman, itemstack5); + } + } + } + } + + this.b(); + } + } + + return itemstack; + } + + public boolean a(ItemStack itemstack, Slot slot) { + return true; + } + + protected void a(int i, int j, boolean flag, EntityHuman entityhuman) { + this.clickItem(i, j, 1, entityhuman); + } + + public void b(EntityHuman entityhuman) { + PlayerInventory playerinventory = entityhuman.inventory; + + if (playerinventory.getCarried() != null) { + entityhuman.drop(playerinventory.getCarried(), false); + playerinventory.setCarried((ItemStack) null); + } + } + + public void a(IInventory iinventory) { + this.b(); + } + + public void setItem(int i, ItemStack itemstack) { + this.getSlot(i).set(itemstack); + } + + public boolean c(EntityHuman entityhuman) { + return !this.i.contains(entityhuman); + } + + public void a(EntityHuman entityhuman, boolean flag) { + if (flag) { + this.i.remove(entityhuman); + } else { + this.i.add(entityhuman); + } + } + + public abstract boolean a(EntityHuman entityhuman); + + protected boolean a(ItemStack itemstack, int i, int j, boolean flag) { + boolean flag1 = false; + int k = i; + + if (flag) { + k = j - 1; + } + + Slot slot; + ItemStack itemstack1; + + if (itemstack.isStackable()) { + while (itemstack.count > 0 && (!flag && k < j || flag && k >= i)) { + slot = (Slot) this.c.get(k); + itemstack1 = slot.getItem(); + if (itemstack1 != null && itemstack1.getItem() == itemstack.getItem() && (!itemstack.usesData() || itemstack.getData() == itemstack1.getData()) && ItemStack.equals(itemstack, itemstack1)) { + int l = itemstack1.count + itemstack.count; + + // CraftBukkit start - itemstack.getMaxStackSize() -> maxStack + int maxStack = Math.min(itemstack.getMaxStackSize(), slot.getMaxStackSize()); + if (l <= maxStack) { + itemstack.count = 0; + itemstack1.count = l; + slot.f(); + flag1 = true; + } else if (itemstack1.count < maxStack) { + itemstack.count -= maxStack - itemstack1.count; + itemstack1.count = maxStack; + slot.f(); + flag1 = true; + } + // CraftBukkit end + } + + if (flag) { + --k; + } else { + ++k; + } + } + } + + if (itemstack.count > 0) { + if (flag) { + k = j - 1; + } else { + k = i; + } + + while (!flag && k < j || flag && k >= i) { + slot = (Slot) this.c.get(k); + itemstack1 = slot.getItem(); + if (itemstack1 == null) { + slot.set(itemstack.cloneItemStack()); + slot.f(); + itemstack.count = 0; + flag1 = true; + break; + } + + if (flag) { + --k; + } else { + ++k; + } + } + } + + return flag1; + } + + public static int b(int i) { + return i >> 2 & 3; + } + + public static int c(int i) { + return i & 3; + } + + public static boolean d(int i) { + return i == 0 || i == 1; + } + + protected void d() { + this.g = 0; + this.h.clear(); + } + + public static boolean a(Slot slot, ItemStack itemstack, boolean flag) { + boolean flag1 = slot == null || !slot.hasItem(); + + if (slot != null && slot.hasItem() && itemstack != null && itemstack.doMaterialsMatch(slot.getItem()) && ItemStack.equals(slot.getItem(), itemstack)) { + int i = flag ? 0 : itemstack.count; + + flag1 |= slot.getItem().count + i <= itemstack.getMaxStackSize(); + } + + return flag1; + } + + public static void a(Set set, int i, ItemStack itemstack, int j) { + switch (i) { + case 0: + itemstack.count = MathHelper.d((float) itemstack.count / (float) set.size()); + break; + + case 1: + itemstack.count = 1; + } + + itemstack.count += j; + } + + public boolean b(Slot slot) { + return true; + } + + public static int b(IInventory iinventory) { + if (iinventory == null) { + return 0; + } else { + int i = 0; + float f = 0.0F; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack = iinventory.getItem(j); + + if (itemstack != null) { + f += (float) itemstack.count / (float) Math.min(iinventory.getMaxStackSize(), itemstack.getMaxStackSize()); + ++i; + } + } + + f /= (float) iinventory.getSize(); + return MathHelper.d(f * 14.0F) + (i > 0 ? 1 : 0); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerAnvil.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerAnvil.java new file mode 100644 index 0000000..18078f3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerAnvil.java @@ -0,0 +1,420 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Map; + +import net.minecraft.util.org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.inventory.PrepareAnvilRepairEvent; + +public class ContainerAnvil extends Container { + + private static final Logger f = LogManager.getLogger(); + private IInventory g = new InventoryCraftResult(); + private IInventory h = new ContainerAnvilInventory(this, "Repair", true, 2); + private World i; + private int j; + private int k; + private int l; + public int a; + private int m; + private String n; + private final EntityHuman o; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerAnvil(PlayerInventory playerinventory, World world, int i, int j, int k, EntityHuman entityhuman) { + this.player = playerinventory; // CraftBukkit + this.i = world; + this.j = i; + this.k = j; + this.l = k; + this.o = entityhuman; + this.a(new Slot(this.h, 0, 27, 47)); + this.a(new Slot(this.h, 1, 76, 47)); + this.a((Slot) (new SlotAnvilResult(this, this.g, 2, 134, 47, world, i, j, k))); + + int l; + + for (l = 0; l < 3; ++l) { + for (int i1 = 0; i1 < 9; ++i1) { + this.a(new Slot(playerinventory, i1 + l * 9 + 9, 8 + i1 * 18, 84 + l * 18)); + } + } + + for (l = 0; l < 9; ++l) { + this.a(new Slot(playerinventory, l, 8 + l * 18, 142)); + } + } + + public void a(IInventory iinventory) { + super.a(iinventory); + if (iinventory == this.h) { + this.e(); + } + } + + public void e() { + ItemStack itemstack = this.h.getItem(0); + + this.a = 0; + int i = 0; + byte b0 = 0; + int j = 0; + + if (itemstack == null) { + this.g.setItem(0, (ItemStack) null); + this.a = 0; + } else { + ItemStack itemstack1 = itemstack.cloneItemStack(); + ItemStack itemstack2 = this.h.getItem(1); + Map map = EnchantmentManager.a(itemstack1); + boolean flag = false; + int k = b0 + itemstack.getRepairCost() + (itemstack2 == null ? 0 : itemstack2.getRepairCost()); + + this.m = 0; + int l; + int i1; + int j1; + int k1; + int l1; + Iterator iterator; + Enchantment enchantment; + + if (itemstack2 != null) { + flag = itemstack2.getItem() == Items.ENCHANTED_BOOK && Items.ENCHANTED_BOOK.g(itemstack2).size() > 0; + if (itemstack1.g() && itemstack1.getItem().a(itemstack, itemstack2)) { + l = Math.min(itemstack1.j(), itemstack1.l() / 4); + if (l <= 0) { + this.g.setItem(0, (ItemStack) null); + this.a = 0; + return; + } + + for (i1 = 0; l > 0 && i1 < itemstack2.count; ++i1) { + j1 = itemstack1.j() - l; + itemstack1.setData(j1); + i += Math.max(1, l / 100) + map.size(); + l = Math.min(itemstack1.j(), itemstack1.l() / 4); + } + + this.m = i1; + } else { + if (!flag && (itemstack1.getItem() != itemstack2.getItem() || !itemstack1.g())) { + this.g.setItem(0, (ItemStack) null); + this.a = 0; + return; + } + + if (itemstack1.g() && !flag) { + l = itemstack.l() - itemstack.j(); + i1 = itemstack2.l() - itemstack2.j(); + j1 = i1 + itemstack1.l() * 12 / 100; + int i2 = l + j1; + + k1 = itemstack1.l() - i2; + if (k1 < 0) { + k1 = 0; + } + + if (k1 < itemstack1.getData()) { + itemstack1.setData(k1); + i += Math.max(1, j1 / 100); + } + } + + Map map1 = EnchantmentManager.a(itemstack2); + + iterator = map1.keySet().iterator(); + + while (iterator.hasNext()) { + j1 = ((Integer) iterator.next()).intValue(); + enchantment = Enchantment.byId[j1]; + k1 = map.containsKey(Integer.valueOf(j1)) ? ((Integer) map.get(Integer.valueOf(j1))).intValue() : 0; + l1 = ((Integer) map1.get(Integer.valueOf(j1))).intValue(); + int j2; + + if (k1 == l1) { + ++l1; + j2 = l1; + } else { + j2 = Math.max(l1, k1); + } + + l1 = j2; + int k2 = l1 - k1; + boolean flag1 = enchantment.canEnchant(itemstack); + + if (this.o.abilities.canInstantlyBuild || itemstack.getItem() == Items.ENCHANTED_BOOK) { + flag1 = true; + } + + Iterator iterator1 = map.keySet().iterator(); + + while (iterator1.hasNext()) { + int l2 = ((Integer) iterator1.next()).intValue(); + + if (l2 != j1 && !enchantment.a(Enchantment.byId[l2])) { + flag1 = false; + i += k2; + } + } + + if (flag1) { + if (l1 > enchantment.getMaxLevel()) { + l1 = enchantment.getMaxLevel(); + } + + map.put(Integer.valueOf(j1), Integer.valueOf(l1)); + int i3 = 0; + + switch (enchantment.getRandomWeight()) { + case 1: + i3 = 8; + break; + + case 2: + i3 = 4; + + case 3: + case 4: + case 6: + case 7: + case 8: + case 9: + default: + break; + + case 5: + i3 = 2; + break; + + case 10: + i3 = 1; + } + + if (flag) { + i3 = Math.max(1, i3 / 2); + } + + i += i3 * k2; + } + } + } + } + + if (StringUtils.isBlank(this.n)) { + if (itemstack.hasName()) { + j = itemstack.g() ? 7 : itemstack.count * 5; + i += j; + itemstack1.t(); + } + } else if (!this.n.equals(itemstack.getName())) { + j = itemstack.g() ? 7 : itemstack.count * 5; + i += j; + if (itemstack.hasName()) { + k += j / 2; + } + + itemstack1.c(this.n); + } + + l = 0; + + for (iterator = map.keySet().iterator(); iterator.hasNext(); k += l + k1 * l1) { + j1 = ((Integer) iterator.next()).intValue(); + enchantment = Enchantment.byId[j1]; + k1 = ((Integer) map.get(Integer.valueOf(j1))).intValue(); + l1 = 0; + ++l; + switch (enchantment.getRandomWeight()) { + case 1: + l1 = 8; + break; + + case 2: + l1 = 4; + + case 3: + case 4: + case 6: + case 7: + case 8: + case 9: + default: + break; + + case 5: + l1 = 2; + break; + + case 10: + l1 = 1; + } + + if (flag) { + l1 = Math.max(1, l1 / 2); + } + } + + if (flag) { + k = Math.max(1, k / 2); + } + + this.a = k + i; + if (i <= 0) { + itemstack1 = null; + } + + if (j == i && j > 0 && this.a >= 40) { + this.a = 39; + } + + if (this.a >= 40 && !this.o.abilities.canInstantlyBuild) { + itemstack1 = null; + } + + if (itemstack1 != null) { + i1 = itemstack1.getRepairCost(); + if (itemstack2 != null && i1 < itemstack2.getRepairCost()) { + i1 = itemstack2.getRepairCost(); + } + + if (itemstack1.hasName()) { + i1 -= 9; + } + + if (i1 < 0) { + i1 = 0; + } + + i1 += 2; + itemstack1.setRepairCost(i1); + EnchantmentManager.a(map, itemstack1); + if (itemstack2 != null) { + PrepareAnvilRepairEvent event = new PrepareAnvilRepairEvent( + this.o.getBukkitEntity(), + getBukkitView(), + this.o.world.getWorld().getBlockAt(this.j, this.k, this.l), + CraftItemStack.asBukkitCopy(itemstack), + CraftItemStack.asBukkitCopy(itemstack2), + CraftItemStack.asBukkitCopy(itemstack1)); + + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled() || event.getResult().getType() == Material.AIR) { + return; + } + itemstack1.c(itemstack.getName()); + itemstack1 = CraftItemStack.asNMSCopy(event.getResult()); + } + itemstack1.setRepairCost(i1); + } + this.g.setItem(0, itemstack1); + this.b(); + } + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, 0, this.a); + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + if (!this.i.isStatic) { + for (int i = 0; i < this.h.getSize(); ++i) { + ItemStack itemstack = this.h.splitWithoutUpdate(i); + + if (itemstack != null) { + entityhuman.drop(itemstack, false); + } + } + } + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.i.getType(this.j, this.k, this.l) != Blocks.ANVIL ? false : entityhuman.e((double) this.j + 0.5D, (double) this.k + 0.5D, (double) this.l + 0.5D) <= 64.0D; + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 2) { + if (!this.a(itemstack1, 3, 39, true)) { + return null; + } + + slot.a(itemstack1, itemstack); + } else if (i != 0 && i != 1) { + if (i >= 3 && i < 39 && !this.a(itemstack1, 0, 2, false)) { + return null; + } + } else if (!this.a(itemstack1, 3, 39, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + public void a(String s) { + this.n = s; + if (this.getSlot(2).hasItem()) { + ItemStack itemstack = this.getSlot(2).getItem(); + + if (StringUtils.isBlank(s)) { + itemstack.t(); + } else { + itemstack.c(this.n); + } + } + + this.e(); + } + + static IInventory a(ContainerAnvil containeranvil) { + return containeranvil.h; + } + + static int b(ContainerAnvil containeranvil) { + return containeranvil.m; + } + + // CraftBukkit start + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryAnvil(this.h, this.g); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerAnvilInventory.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerAnvilInventory.java new file mode 100644 index 0000000..6cc0d92 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerAnvilInventory.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class ContainerAnvilInventory extends InventorySubcontainer { // CraftBukkit - public + + final ContainerAnvil a; + + // CraftBukkit start + public List transaction = new java.util.ArrayList(); + public org.bukkit.entity.Player player; + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return this.player; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + ContainerAnvilInventory(ContainerAnvil containeranvil, String s, boolean flag, int i) { + super(s, flag, i); + this.a = containeranvil; + } + + // CraftBukkit start - override inherited maxStack from InventorySubcontainer + public int getMaxStackSize() { + return maxStack; + } + // CraftBukkit end + + public void update() { + super.update(); + this.a.a((IInventory) this); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerBeacon.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerBeacon.java new file mode 100644 index 0000000..b7fa7b4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerBeacon.java @@ -0,0 +1,114 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit + +public class ContainerBeacon extends Container { + + private TileEntityBeacon a; + private final SlotBeacon f; + private int g; + private int h; + private int i; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerBeacon(PlayerInventory playerinventory, TileEntityBeacon tileentitybeacon) { + player = playerinventory; // CraftBukkit + this.a = tileentitybeacon; + this.a(this.f = new SlotBeacon(this, tileentitybeacon, 0, 136, 110)); + byte b0 = 36; + short short1 = 137; + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, b0 + j * 18, short1 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, b0 + i * 18, 58 + short1)); + } + + this.g = tileentitybeacon.l(); + this.h = tileentitybeacon.j(); + this.i = tileentitybeacon.k(); + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, 0, this.g); + icrafting.setContainerData(this, 1, this.h); + icrafting.setContainerData(this, 2, this.i); + } + + public TileEntityBeacon e() { + return this.a; + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.a.a(entityhuman); + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 0) { + if (!this.a(itemstack1, 1, 37, true)) { + return null; + } + + slot.a(itemstack1, itemstack); + } else if (!this.f.hasItem() && this.f.isAllowed(itemstack1) && itemstack1.count == 1) { + if (!this.a(itemstack1, 0, 1, false)) { + return null; + } + } else if (i >= 1 && i < 28) { + if (!this.a(itemstack1, 28, 37, false)) { + return null; + } + } else if (i >= 28 && i < 37) { + if (!this.a(itemstack1, 1, 28, false)) { + return null; + } + } else if (!this.a(itemstack1, 1, 37, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + // CraftBukkit start + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryBeacon(this.a); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerBrewingStand.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerBrewingStand.java new file mode 100644 index 0000000..903489a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerBrewingStand.java @@ -0,0 +1,126 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryBrewer; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerBrewingStand extends Container { + + private TileEntityBrewingStand brewingStand; + private final Slot f; + private int g; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerBrewingStand(PlayerInventory playerinventory, TileEntityBrewingStand tileentitybrewingstand) { + player = playerinventory; // CraftBukkit + this.brewingStand = tileentitybrewingstand; + this.a(new SlotPotionBottle(playerinventory.player, tileentitybrewingstand, 0, 56, 46)); + this.a(new SlotPotionBottle(playerinventory.player, tileentitybrewingstand, 1, 79, 53)); + this.a(new SlotPotionBottle(playerinventory.player, tileentitybrewingstand, 2, 102, 46)); + this.f = this.a(new SlotBrewing(this, tileentitybrewingstand, 3, 79, 17)); + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, 0, this.brewingStand.i()); + } + + public void b() { + super.b(); + + for (int i = 0; i < this.listeners.size(); ++i) { + ICrafting icrafting = (ICrafting) this.listeners.get(i); + + if (this.g != this.brewingStand.i()) { + icrafting.setContainerData(this, 0, this.brewingStand.i()); + } + } + + this.g = this.brewingStand.i(); + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.brewingStand.a(entityhuman); + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if ((i < 0 || i > 2) && i != 3) { + if (!this.f.hasItem() && this.f.isAllowed(itemstack1)) { + if (!this.a(itemstack1, 3, 4, false)) { + return null; + } + } else if (SlotPotionBottle.b_(itemstack)) { + if (!this.a(itemstack1, 0, 3, false)) { + return null; + } + } else if (i >= 4 && i < 31) { + if (!this.a(itemstack1, 31, 40, false)) { + return null; + } + } else if (i >= 31 && i < 40) { + if (!this.a(itemstack1, 4, 31, false)) { + return null; + } + } else if (!this.a(itemstack1, 4, 40, false)) { + return null; + } + } else { + if (!this.a(itemstack1, 4, 40, true)) { + return null; + } + + slot.a(itemstack1, itemstack); + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + // CraftBukkit start + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryBrewer inventory = new CraftInventoryBrewer(this.brewingStand); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerChest.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerChest.java new file mode 100644 index 0000000..c2c4fb5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerChest.java @@ -0,0 +1,104 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerChest extends Container { + + public IInventory container; // CraftBukkit - private->public + private int f; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventory inventory; + if (this.container instanceof PlayerInventory) { + inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryPlayer((PlayerInventory) this.container); + } else if (this.container instanceof InventoryLargeChest) { + inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) this.container); + } else { + inventory = new CraftInventory(this.container); + } + + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end + + public ContainerChest(IInventory iinventory, IInventory iinventory1) { + this.container = iinventory1; + this.f = iinventory1.getSize() / 9; + iinventory1.startOpen(); + int i = (this.f - 4) * 18; + // CraftBukkit start - Save player + // TODO: Should we check to make sure it really is an InventoryPlayer? + this.player = (PlayerInventory) iinventory; + // CraftBukkit end + + int j; + int k; + + for (j = 0; j < this.f; ++j) { + for (k = 0; k < 9; ++k) { + this.a(new Slot(iinventory1, k + j * 9, 8 + k * 18, 18 + j * 18)); + } + } + + for (j = 0; j < 3; ++j) { + for (k = 0; k < 9; ++k) { + this.a(new Slot(iinventory, k + j * 9 + 9, 8 + k * 18, 103 + j * 18 + i)); + } + } + + for (j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j, 8 + j * 18, 161 + i)); + } + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.container.a(entityhuman); + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.f * 9) { + if (!this.a(itemstack1, this.f * 9, this.c.size(), true)) { + return null; + } + } else if (!this.a(itemstack1, 0, this.f * 9, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.container.closeContainer(); + } + + public IInventory e() { + return this.container; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerDispenser.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerDispenser.java new file mode 100644 index 0000000..32187ec --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerDispenser.java @@ -0,0 +1,91 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerDispenser extends Container { + + public TileEntityDispenser items; // CraftBukkit - private -> public + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerDispenser(IInventory iinventory, TileEntityDispenser tileentitydispenser) { + this.items = tileentitydispenser; + // CraftBukkit start - Save player + // TODO: Should we check to make sure it really is an InventoryPlayer? + this.player = (PlayerInventory)iinventory; + // CraftBukkit end + + int i; + int j; + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 3; ++j) { + this.a(new Slot(tileentitydispenser, j + i * 3, 62 + j * 18, 17 + i * 18)); + } + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(iinventory, i, 8 + i * 18, 142)); + } + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.items.a(entityhuman); + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < 9) { + if (!this.a(itemstack1, 9, 45, true)) { + return null; + } + } else if (!this.a(itemstack1, 0, 9, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + // CraftBukkit start + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventory inventory = new CraftInventory(this.items); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerEnchantTable.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerEnchantTable.java new file mode 100644 index 0000000..5e0dee6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerEnchantTable.java @@ -0,0 +1,298 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +// CraftBukkit start +import java.util.Map; + +import org.bukkit.craftbukkit.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.enchantment.EnchantItemEvent; +import org.bukkit.event.enchantment.PrepareItemEnchantEvent; +import org.bukkit.entity.Player; +// CraftBukkit end + +public class ContainerEnchantTable extends Container { + + // CraftBukkit - make type specific (changed from IInventory) + public ContainerEnchantTableInventory enchantSlots = new ContainerEnchantTableInventory(this, "Enchant", true, 1); + private World world; + private int x; + private int y; + private int z; + private Random l = new Random(); + public long f; + public int[] costs = new int[3]; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private Player player; + // CraftBukkit end + + public ContainerEnchantTable(PlayerInventory playerinventory, World world, int i, int j, int k) { + this.world = world; + this.x = i; + this.y = j; + this.z = k; + this.a((Slot) (new SlotEnchant(this, this.enchantSlots, 0, 25, 47))); + + int l; + + for (l = 0; l < 3; ++l) { + for (int i1 = 0; i1 < 9; ++i1) { + this.a(new Slot(playerinventory, i1 + l * 9 + 9, 8 + i1 * 18, 84 + l * 18)); + } + } + + for (l = 0; l < 9; ++l) { + this.a(new Slot(playerinventory, l, 8 + l * 18, 142)); + } + + // CraftBukkit start + player = (Player) playerinventory.player.getBukkitEntity(); + enchantSlots.player = player; + // CraftBukkit end + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, 0, this.costs[0]); + icrafting.setContainerData(this, 1, this.costs[1]); + icrafting.setContainerData(this, 2, this.costs[2]); + } + + public void b() { + super.b(); + + for (int i = 0; i < this.listeners.size(); ++i) { + ICrafting icrafting = (ICrafting) this.listeners.get(i); + + icrafting.setContainerData(this, 0, this.costs[0]); + icrafting.setContainerData(this, 1, this.costs[1]); + icrafting.setContainerData(this, 2, this.costs[2]); + } + } + + public void a(IInventory iinventory) { + if (iinventory == this.enchantSlots) { + ItemStack itemstack = iinventory.getItem(0); + int i; + + if (itemstack != null) { // CraftBukkit - relax condition + this.f = this.l.nextLong(); + if (!this.world.isStatic) { + i = 0; + + int j; + + for (j = -1; j <= 1; ++j) { + for (int k = -1; k <= 1; ++k) { + if ((j != 0 || k != 0) && this.world.isEmpty(this.x + k, this.y, this.z + j) && this.world.isEmpty(this.x + k, this.y + 1, this.z + j)) { + if (this.world.getType(this.x + k * 2, this.y, this.z + j * 2) == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.x + k * 2, this.y + 1, this.z + j * 2) == Blocks.BOOKSHELF) { + ++i; + } + + if (k != 0 && j != 0) { + if (this.world.getType(this.x + k * 2, this.y, this.z + j) == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.x + k * 2, this.y + 1, this.z + j) == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.x + k, this.y, this.z + j * 2) == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.x + k, this.y + 1, this.z + j * 2) == Blocks.BOOKSHELF) { + ++i; + } + } + } + } + } + + for (j = 0; j < 3; ++j) { + this.costs[j] = EnchantmentManager.a(this.l, j, i, itemstack); + } + + // CraftBukkit start + CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); + PrepareItemEnchantEvent event = new PrepareItemEnchantEvent(player, this.getBukkitView(), this.world.getWorld().getBlockAt(this.x, this.y, this.z), item, this.costs, i); + event.setCancelled(!itemstack.x()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + for (i = 0; i < 3; ++i) { + this.costs[i] = 0; + } + return; + } + // CraftBukkit end + + this.b(); + } + } else { + for (i = 0; i < 3; ++i) { + this.costs[i] = 0; + } + } + } + } + + public boolean a(EntityHuman entityhuman, int i) { + ItemStack itemstack = this.enchantSlots.getItem(0); + + if (this.costs[i] > 0 && itemstack != null && (entityhuman.expLevel >= this.costs[i] || entityhuman.abilities.canInstantlyBuild)) { + if (!this.world.isStatic) { + List list = EnchantmentManager.b(this.l, itemstack, this.costs[i]); + // CraftBukkit start - Provide an empty enchantment list + if (list == null) { + list = new java.util.ArrayList(); + } + // CraftBukkit end + + boolean flag = itemstack.getItem() == Items.BOOK; + + if (list != null) { + // CraftBukkit start + Map enchants = new java.util.HashMap(); + for (Object obj : list) { + EnchantmentInstance instance = (EnchantmentInstance) obj; + enchants.put(org.bukkit.enchantments.Enchantment.getById(instance.enchantment.id), instance.level); + } + CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); + + EnchantItemEvent event = new EnchantItemEvent((Player) entityhuman.getBukkitEntity(), this.getBukkitView(), this.world.getWorld().getBlockAt(this.x, this.y, this.z), item, this.costs[i], enchants, i); + this.world.getServer().getPluginManager().callEvent(event); + + int level = event.getExpLevelCost(); + if (event.isCancelled() || (level > entityhuman.expLevel && !entityhuman.abilities.canInstantlyBuild) || event.getEnchantsToAdd().isEmpty()) { + return false; + } + + if (flag) { + itemstack.setItem(Items.ENCHANTED_BOOK); + } + + boolean added = false; + for (Map.Entry entry : event.getEnchantsToAdd().entrySet()) { + if (entry.getKey().getMaxLevel() == 0) + continue; + try { + if (flag) { + int enchantId = entry.getKey().getId(); + Enchantment enchant = Enchantment.byId[enchantId]; + if (enchant == null) { + continue; + } + + EnchantmentInstance enchantment = new EnchantmentInstance(enchantId, entry.getValue()); + Items.ENCHANTED_BOOK.a(itemstack, enchantment); + added = true; + } else { + item.addUnsafeEnchantment(entry.getKey(), entry.getValue()); + added = true; + } + } catch (IllegalArgumentException e) { + /* Just swallow invalid enchantments */ + } + } + + if (!added) { + return false; + } + + entityhuman.levelDown(-level); + // CraftBukkit end + + this.a(this.enchantSlots); + } + } + + return true; + } else { + return false; + } + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + if (!this.world.isStatic) { + ItemStack itemstack = this.enchantSlots.splitWithoutUpdate(0); + + if (itemstack != null) { + entityhuman.drop(itemstack, false); + } + } + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.world.getType(this.x, this.y, this.z) != Blocks.ENCHANTMENT_TABLE ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 0) { + if (!this.a(itemstack1, 1, 37, true)) { + return null; + } + } else { + if (((Slot) this.c.get(0)).hasItem() || !((Slot) this.c.get(0)).isAllowed(itemstack1)) { + return null; + } + + if (itemstack1.hasTag() && itemstack1.count == 1) { + ((Slot) this.c.get(0)).set(itemstack1.cloneItemStack()); + itemstack1.count = 0; + } else if (itemstack1.count >= 1) { + // Spigot start + ItemStack clone = itemstack1.cloneItemStack(); + clone.count = 1; + ((Slot) this.c.get(0)).set(clone); + // Spigot end + --itemstack1.count; + } + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + // CraftBukkit start + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.enchantSlots); + bukkitEntity = new CraftInventoryView(this.player, inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerEnchantTableInventory.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerEnchantTableInventory.java new file mode 100644 index 0000000..b9dbf60 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerEnchantTableInventory.java @@ -0,0 +1,57 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class ContainerEnchantTableInventory extends InventorySubcontainer { // CraftBukkit -> public + + final ContainerEnchantTable enchantTable; + + // CraftBukkit start + public List transaction = new java.util.ArrayList(); + public org.bukkit.entity.Player player; + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return this.player; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + ContainerEnchantTableInventory(ContainerEnchantTable containerenchanttable, String s, boolean flag, int i) { + super(s, flag, i); + this.enchantTable = containerenchanttable; + this.setMaxStackSize(1); // CraftBukkit + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void update() { + super.update(); + this.enchantTable.a((IInventory) this); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerFurnace.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerFurnace.java new file mode 100644 index 0000000..2438528 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerFurnace.java @@ -0,0 +1,135 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryFurnace; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerFurnace extends Container { + + private TileEntityFurnace furnace; + private int f; + private int g; + private int h; + + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryFurnace inventory = new CraftInventoryFurnace(this.furnace); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end + + public ContainerFurnace(PlayerInventory playerinventory, TileEntityFurnace tileentityfurnace) { + this.furnace = tileentityfurnace; + this.a(new Slot(tileentityfurnace, 0, 56, 17)); + this.a(new Slot(tileentityfurnace, 1, 56, 53)); + this.a(new SlotFurnaceResult(playerinventory.player, tileentityfurnace, 2, 116, 35)); + this.player = playerinventory; // CraftBukkit - save player + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, 0, this.furnace.cookTime); + icrafting.setContainerData(this, 1, this.furnace.burnTime); + icrafting.setContainerData(this, 2, this.furnace.ticksForCurrentFuel); + } + + public void b() { + super.b(); + + for (int i = 0; i < this.listeners.size(); ++i) { + ICrafting icrafting = (ICrafting) this.listeners.get(i); + + if (this.f != this.furnace.cookTime) { + icrafting.setContainerData(this, 0, this.furnace.cookTime); + } + + if (this.g != this.furnace.burnTime) { + icrafting.setContainerData(this, 1, this.furnace.burnTime); + } + + if (this.h != this.furnace.ticksForCurrentFuel) { + icrafting.setContainerData(this, 2, this.furnace.ticksForCurrentFuel); + } + } + + this.f = this.furnace.cookTime; + this.g = this.furnace.burnTime; + this.h = this.furnace.ticksForCurrentFuel; + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.furnace.a(entityhuman); + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 2) { + if (!this.a(itemstack1, 3, 39, true)) { + return null; + } + + slot.a(itemstack1, itemstack); + } else if (i != 1 && i != 0) { + if (RecipesFurnace.getInstance().getResult(itemstack1) != null) { + if (!this.a(itemstack1, 0, 1, false)) { + return null; + } + } else if (TileEntityFurnace.isFuel(itemstack1)) { + if (!this.a(itemstack1, 1, 2, false)) { + return null; + } + } else if (i >= 3 && i < 30) { + if (!this.a(itemstack1, 30, 39, false)) { + return null; + } + } else if (i >= 30 && i < 39 && !this.a(itemstack1, 3, 30, false)) { + return null; + } + } else if (!this.a(itemstack1, 3, 39, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerHopper.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerHopper.java new file mode 100644 index 0000000..e2c789b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerHopper.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerHopper extends Container { + + private final IInventory hopper; + + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventory inventory = new CraftInventory(this.hopper); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end + + public ContainerHopper(PlayerInventory playerinventory, IInventory iinventory) { + this.hopper = iinventory; + this.player = playerinventory; // CraftBukkit - save player + iinventory.startOpen(); + byte b0 = 51; + + int i; + + for (i = 0; i < iinventory.getSize(); ++i) { + this.a(new Slot(iinventory, i, 44 + i * 18, 20)); + } + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, i * 18 + b0)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 58 + b0)); + } + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.hopper.a(entityhuman); + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.hopper.getSize()) { + if (!this.a(itemstack1, this.hopper.getSize(), this.c.size(), true)) { + return null; + } + } else if (!this.a(itemstack1, 0, this.hopper.getSize(), false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.hopper.closeContainer(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerHorse.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerHorse.java new file mode 100644 index 0000000..79aa182 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerHorse.java @@ -0,0 +1,104 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.inventory.InventoryView; +// CraftBukkit end + +public class ContainerHorse extends Container { + + private IInventory a; + private EntityHorse f; + + // CraftBukkit start + org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity; + PlayerInventory player; + + @Override + public InventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryHorse(this.a); + return bukkitEntity = new CraftInventoryView(player.player.getBukkitEntity(), inventory, this); + } + + public ContainerHorse(IInventory iinventory, IInventory iinventory1, EntityHorse entityhorse) { + player = (PlayerInventory) iinventory; + // CraftBukkit end + this.a = iinventory1; + this.f = entityhorse; + byte b0 = 3; + + iinventory1.startOpen(); + int i = (b0 - 4) * 18; + + this.a(new SlotHorseSaddle(this, iinventory1, 0, 8, 18)); + this.a(new SlotHorseArmor(this, iinventory1, 1, 8, 36, entityhorse)); + int j; + int k; + + if (entityhorse.hasChest()) { + for (j = 0; j < b0; ++j) { + for (k = 0; k < 5; ++k) { + this.a(new Slot(iinventory1, 2 + k + j * 5, 80 + k * 18, 18 + j * 18)); + } + } + } + + for (j = 0; j < 3; ++j) { + for (k = 0; k < 9; ++k) { + this.a(new Slot(iinventory, k + j * 9 + 9, 8 + k * 18, 102 + j * 18 + i)); + } + } + + for (j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j, 8 + j * 18, 160 + i)); + } + } + + public boolean a(EntityHuman entityhuman) { + return this.a.a(entityhuman) && this.f.isAlive() && this.f.e(entityhuman) < 8.0F; + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.a.getSize()) { + if (!this.a(itemstack1, this.a.getSize(), this.c.size(), true)) { + return null; + } + } else if (this.getSlot(1).isAllowed(itemstack1) && !this.getSlot(1).hasItem()) { + if (!this.a(itemstack1, 1, 2, false)) { + return null; + } + } else if (this.getSlot(0).isAllowed(itemstack1)) { + if (!this.a(itemstack1, 0, 1, false)) { + return null; + } + } else if (this.a.getSize() <= 2 || !this.a(itemstack1, 2, this.a.getSize(), false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.a.closeContainer(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerMerchant.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerMerchant.java new file mode 100644 index 0000000..97f97f3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerMerchant.java @@ -0,0 +1,131 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit + +public class ContainerMerchant extends Container { + + private IMerchant merchant; + private InventoryMerchant f; + private final World g; + + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), new org.bukkit.craftbukkit.inventory.CraftInventoryMerchant(this.getMerchantInventory()), this); + } + return bukkitEntity; + } + // CraftBukkit end + + + public ContainerMerchant(PlayerInventory playerinventory, IMerchant imerchant, World world) { + this.merchant = imerchant; + this.g = world; + this.f = new InventoryMerchant(playerinventory.player, imerchant); + this.a(new Slot(this.f, 0, 36, 53)); + this.a(new Slot(this.f, 1, 62, 53)); + this.a((Slot) (new SlotMerchantResult(playerinventory.player, imerchant, this.f, 2, 120, 53))); + this.player = playerinventory; // CraftBukkit - save player + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + } + + public InventoryMerchant getMerchantInventory() { + return this.f; + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + } + + public void b() { + super.b(); + } + + public void a(IInventory iinventory) { + this.f.h(); + super.a(iinventory); + } + + public void e(int i) { + this.f.c(i); + } + + public boolean a(EntityHuman entityhuman) { + return this.merchant.b() == entityhuman; + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 2) { + if (!this.a(itemstack1, 3, 39, true)) { + return null; + } + + slot.a(itemstack1, itemstack); + } else if (i != 0 && i != 1) { + if (i >= 3 && i < 30) { + if (!this.a(itemstack1, 30, 39, false)) { + return null; + } + } else if (i >= 30 && i < 39 && !this.a(itemstack1, 3, 30, false)) { + return null; + } + } else if (!this.a(itemstack1, 3, 39, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.merchant.a_((EntityHuman) null); + super.b(entityhuman); + if (!this.g.isStatic) { + ItemStack itemstack = this.f.splitWithoutUpdate(0); + + if (itemstack != null) { + entityhuman.drop(itemstack, false); + } + + itemstack = this.f.splitWithoutUpdate(1); + if (itemstack != null) { + entityhuman.drop(itemstack, false); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerPlayer.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerPlayer.java new file mode 100644 index 0000000..587ee26 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerPlayer.java @@ -0,0 +1,157 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerPlayer extends Container { + + public InventoryCrafting craftInventory = new InventoryCrafting(this, 2, 2); + public IInventory resultInventory = new InventoryCraftResult(); + public boolean g; + private final EntityHuman h; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerPlayer(PlayerInventory playerinventory, boolean flag, EntityHuman entityhuman) { + this.g = flag; + this.h = entityhuman; + this.resultInventory = new InventoryCraftResult(); // CraftBukkit - moved to before InventoryCrafting construction + this.craftInventory = new InventoryCrafting(this, 2, 2, playerinventory.player); // CraftBukkit - pass player + this.craftInventory.resultInventory = this.resultInventory; // CraftBukkit - let InventoryCrafting know about its result slot + this.player = playerinventory; // CraftBukkit - save player + this.a((Slot) (new SlotResult(playerinventory.player, this.craftInventory, this.resultInventory, 0, 144, 36))); + + int i; + int j; + + for (i = 0; i < 2; ++i) { + for (j = 0; j < 2; ++j) { + this.a(new Slot(this.craftInventory, j + i * 2, 88 + j * 18, 26 + i * 18)); + } + } + + for (i = 0; i < 4; ++i) { + this.a((Slot) (new SlotArmor(this, playerinventory, playerinventory.getSize() - 1 - i, 8, 8 + i * 18, i))); + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + (i + 1) * 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + // this.a((IInventory) this.craftInventory); // CraftBukkit - unneeded since it just sets result slot to empty + } + + public void a(IInventory iinventory) { + // CraftBukkit start (Note: the following line would cause an error if called during construction) + CraftingManager.getInstance().lastCraftView = getBukkitView(); + ItemStack craftResult = CraftingManager.getInstance().craft(this.craftInventory, this.h.world); + this.resultInventory.setItem(0, craftResult); + if (super.listeners.size() < 1) { + return; + } + + EntityPlayer player = (EntityPlayer) super.listeners.get(0); // TODO: Is this _always_ correct? Seems like it. + player.playerConnection.sendPacket(new PacketPlayOutSetSlot(player.activeContainer.windowId, 0, craftResult)); + // CraftBukkit end + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + + for (int i = 0; i < 4; ++i) { + ItemStack itemstack = this.craftInventory.splitWithoutUpdate(i); + + if (itemstack != null) { + entityhuman.drop(itemstack, false); + } + } + + this.resultInventory.setItem(0, (ItemStack) null); + } + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 0) { + if (!this.a(itemstack1, 9, 45, true)) { + return null; + } + + slot.a(itemstack1, itemstack); + } else if (i >= 1 && i < 5) { + if (!this.a(itemstack1, 9, 45, false)) { + return null; + } + } else if (i >= 5 && i < 9) { + if (!this.a(itemstack1, 9, 45, false)) { + return null; + } + } else if (itemstack.getItem() instanceof ItemArmor && !((Slot) this.c.get(5 + ((ItemArmor) itemstack.getItem()).b)).hasItem()) { + int j = 5 + ((ItemArmor) itemstack.getItem()).b; + + if (!this.a(itemstack1, j, j + 1, false)) { + return null; + } + } else if (i >= 9 && i < 36) { + if (!this.a(itemstack1, 36, 45, false)) { + return null; + } + } else if (i >= 36 && i < 45) { + if (!this.a(itemstack1, 9, 36, false)) { + return null; + } + } else if (!this.a(itemstack1, 9, 45, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + public boolean a(ItemStack itemstack, Slot slot) { + return slot.inventory != this.resultInventory && super.a(itemstack, slot); + } + + // CraftBukkit start + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftInventory, this.resultInventory); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ContainerWorkbench.java b/vspigot-server/src/main/java/net/minecraft/server/ContainerWorkbench.java new file mode 100644 index 0000000..37c6105 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ContainerWorkbench.java @@ -0,0 +1,145 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerWorkbench extends Container { + + public InventoryCrafting craftInventory; // CraftBukkit - move initialization into constructor + public IInventory resultInventory; // CraftBukkit - move initialization into constructor + private World g; + private int h; + private int i; + private int j; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerWorkbench(PlayerInventory playerinventory, World world, int i, int j, int k) { + // CraftBukkit start - Switched order of IInventory construction and stored player + this.resultInventory = new InventoryCraftResult(); + this.craftInventory = new InventoryCrafting(this, 3, 3, playerinventory.player); // CraftBukkit - pass player + this.craftInventory.resultInventory = this.resultInventory; + this.player = playerinventory; + // CraftBukkit end + this.g = world; + this.h = i; + this.i = j; + this.j = k; + this.a((Slot) (new SlotResult(playerinventory.player, this.craftInventory, this.resultInventory, 0, 124, 35))); + + int l; + int i1; + + for (l = 0; l < 3; ++l) { + for (i1 = 0; i1 < 3; ++i1) { + this.a(new Slot(this.craftInventory, i1 + l * 3, 30 + i1 * 18, 17 + l * 18)); + } + } + + for (l = 0; l < 3; ++l) { + for (i1 = 0; i1 < 9; ++i1) { + this.a(new Slot(playerinventory, i1 + l * 9 + 9, 8 + i1 * 18, 84 + l * 18)); + } + } + + for (l = 0; l < 9; ++l) { + this.a(new Slot(playerinventory, l, 8 + l * 18, 142)); + } + + this.a((IInventory) this.craftInventory); + } + + public void a(IInventory iinventory) { + // CraftBukkit start + CraftingManager.getInstance().lastCraftView = getBukkitView(); + ItemStack craftResult = CraftingManager.getInstance().craft(this.craftInventory, this.g); + this.resultInventory.setItem(0, craftResult); + if (super.listeners.size() < 1) { + return; + } + + EntityPlayer player = (EntityPlayer) super.listeners.get(0); // TODO: Is this _always_ correct? Seems like it. + player.playerConnection.sendPacket(new PacketPlayOutSetSlot(player.activeContainer.windowId, 0, craftResult)); + // CraftBukkit end + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + if (!this.g.isStatic) { + for (int i = 0; i < 9; ++i) { + ItemStack itemstack = this.craftInventory.splitWithoutUpdate(i); + + if (itemstack != null) { + entityhuman.drop(itemstack, false); + } + } + } + } + + public boolean a(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.g.getType(this.h, this.i, this.j) != Blocks.WORKBENCH ? false : entityhuman.e((double) this.h + 0.5D, (double) this.i + 0.5D, (double) this.j + 0.5D) <= 64.0D; + } + + public ItemStack b(EntityHuman entityhuman, int i) { + ItemStack itemstack = null; + Slot slot = (Slot) this.c.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 0) { + if (!this.a(itemstack1, 10, 46, true)) { + return null; + } + + slot.a(itemstack1, itemstack); + } else if (i >= 10 && i < 37) { + if (!this.a(itemstack1, 37, 46, false)) { + return null; + } + } else if (i >= 37 && i < 46) { + if (!this.a(itemstack1, 10, 37, false)) { + return null; + } + } else if (!this.a(itemstack1, 10, 46, false)) { + return null; + } + + if (itemstack1.count == 0) { + slot.set((ItemStack) null); + } else { + slot.f(); + } + + if (itemstack1.count == itemstack.count) { + return null; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + public boolean a(ItemStack itemstack, Slot slot) { + return slot.inventory != this.resultInventory && super.a(itemstack, slot); + } + + // CraftBukkit start + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftInventory, this.resultInventory); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ControllerLook.java b/vspigot-server/src/main/java/net/minecraft/server/ControllerLook.java new file mode 100644 index 0000000..f1e8b3d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ControllerLook.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.TrigMath; // CraftBukkit + +public class ControllerLook { + + private EntityInsentient a; + private float b; + private float c; + private boolean d; + private double e; + private double f; + private double g; + + public ControllerLook(EntityInsentient entityinsentient) { + this.a = entityinsentient; + } + + public void a(Entity entity, float f, float f1) { + this.e = entity.locX; + if (entity instanceof EntityLiving) { + this.f = entity.locY + (double) entity.getHeadHeight(); + } else { + this.f = (entity.boundingBox.b + entity.boundingBox.e) / 2.0D; + } + + this.g = entity.locZ; + this.b = f; + this.c = f1; + this.d = true; + } + + public void a(double d0, double d1, double d2, float f, float f1) { + this.e = d0; + this.f = d1; + this.g = d2; + this.b = f; + this.c = f1; + this.d = true; + } + + public void a() { + this.a.pitch = 0.0F; + if (this.d) { + this.d = false; + double d0 = this.e - this.a.locX; + double d1 = this.f - (this.a.locY + (double) this.a.getHeadHeight()); + double d2 = this.g - this.a.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + // CraftBukkit start - Math -> TrigMath + float f = (float) (TrigMath.atan2(d2, d0) * 180.0D / 3.1415927410125732D) - 90.0F; + float f1 = (float) (-(TrigMath.atan2(d1, d3) * 180.0D / 3.1415927410125732D)); + // CraftBukkit end + + this.a.pitch = this.a(this.a.pitch, f1, this.c); + this.a.aO = this.a(this.a.aO, f, this.b); + } else { + this.a.aO = this.a(this.a.aO, this.a.aM, 10.0F); + } + + float f2 = MathHelper.g(this.a.aO - this.a.aM); + + if (!this.a.getNavigation().g()) { + if (f2 < -75.0F) { + this.a.aO = this.a.aM - 75.0F; + } + + if (f2 > 75.0F) { + this.a.aO = this.a.aM + 75.0F; + } + } + } + + private float a(float f, float f1, float f2) { + float f3 = MathHelper.g(f1 - f); + + if (f3 > f2) { + f3 = f2; + } + + if (f3 < -f2) { + f3 = -f2; + } + + return f + f3; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ControllerMove.java b/vspigot-server/src/main/java/net/minecraft/server/ControllerMove.java new file mode 100644 index 0000000..525a4d1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ControllerMove.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +public class ControllerMove { + + private EntityInsentient a; + private double b; + private double c; + private double d; + private double e; + private boolean f; + + public ControllerMove(EntityInsentient entityinsentient) { + this.a = entityinsentient; + this.b = entityinsentient.locX; + this.c = entityinsentient.locY; + this.d = entityinsentient.locZ; + } + + public boolean a() { + return this.f; + } + + public double b() { + return this.e; + } + + public void a(double d0, double d1, double d2, double d3) { + this.b = d0; + this.c = d1; + this.d = d2; + this.e = d3; + this.f = true; + } + + public void c() { + this.a.n(0.0F); + if (this.f) { + this.f = false; + int i = MathHelper.floor(this.a.boundingBox.b + 0.5D); + double d0 = this.b - this.a.locX; + double d1 = this.d - this.a.locZ; + double d2 = this.c - (double) i; + double d3 = d0 * d0 + d2 * d2 + d1 * d1; + + if (d3 >= 2.500000277905201E-7D) { + // CraftBukkit - Math -> TrigMath + float f = (float) (org.bukkit.craftbukkit.TrigMath.atan2(d1, d0) * 180.0D / 3.1415927410125732D) - 90.0F; + + this.a.yaw = this.a(this.a.yaw, f, 30.0F); + this.a.i((float) (this.e * this.a.getAttributeInstance(GenericAttributes.d).getValue())); + if (d2 > 0.0D && d0 * d0 + d1 * d1 < 1.0D) { + this.a.getControllerJump().a(); + } + } + } + } + + private float a(float f, float f1, float f2) { + float f3 = MathHelper.g(f1 - f); + + if (f3 > f2) { + f3 = f2; + } + + if (f3 < -f2) { + f3 = -f2; + } + + return f + f3; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/CraftingManager.java b/vspigot-server/src/main/java/net/minecraft/server/CraftingManager.java new file mode 100644 index 0000000..e2fbdc4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/CraftingManager.java @@ -0,0 +1,318 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class CraftingManager { + + private static final CraftingManager a = new CraftingManager(); + // CraftBukkit start + public List recipes = new ArrayList(); // private -> public + public IRecipe lastRecipe; + public org.bukkit.inventory.InventoryView lastCraftView; + // CraftBukkit end + + public static final CraftingManager getInstance() { + return a; + } + + // CraftBukkit - private -> public + public CraftingManager() { + (new RecipesTools()).a(this); + (new RecipesWeapons()).a(this); + (new RecipeIngots()).a(this); + (new RecipesFood()).a(this); + (new RecipesCrafting()).a(this); + (new RecipesArmor()).a(this); + (new RecipesDyes()).a(this); + this.recipes.add(new RecipeArmorDye()); + this.recipes.add(new RecipeBookClone()); + this.recipes.add(new RecipeMapClone()); + this.recipes.add(new RecipeMapExtend()); + this.recipes.add(new RecipeFireworks()); + this.registerShapedRecipe(new ItemStack(Items.PAPER, 3), new Object[] { "###", Character.valueOf('#'), Items.SUGAR_CANE}); + this.registerShapelessRecipe(new ItemStack(Items.BOOK, 1), new Object[] { Items.PAPER, Items.PAPER, Items.PAPER, Items.LEATHER}); + this.registerShapelessRecipe(new ItemStack(Items.BOOK_AND_QUILL, 1), new Object[] { Items.BOOK, new ItemStack(Items.INK_SACK, 1, 0), Items.FEATHER}); + this.registerShapedRecipe(new ItemStack(Blocks.FENCE, 2), new Object[] { "###", "###", Character.valueOf('#'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Blocks.COBBLE_WALL, 6, 0), new Object[] { "###", "###", Character.valueOf('#'), Blocks.COBBLESTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.COBBLE_WALL, 6, 1), new Object[] { "###", "###", Character.valueOf('#'), Blocks.MOSSY_COBBLESTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.NETHER_FENCE, 6), new Object[] { "###", "###", Character.valueOf('#'), Blocks.NETHER_BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.FENCE_GATE, 1), new Object[] { "#W#", "#W#", Character.valueOf('#'), Items.STICK, Character.valueOf('W'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Blocks.JUKEBOX, 1), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Blocks.WOOD, Character.valueOf('X'), Items.DIAMOND}); + this.registerShapedRecipe(new ItemStack(Items.LEASH, 2), new Object[] { "~~ ", "~O ", " ~", Character.valueOf('~'), Items.STRING, Character.valueOf('O'), Items.SLIME_BALL}); + this.registerShapedRecipe(new ItemStack(Blocks.NOTE_BLOCK, 1), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Blocks.WOOD, Character.valueOf('X'), Items.REDSTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.BOOKSHELF, 1), new Object[] { "###", "XXX", "###", Character.valueOf('#'), Blocks.WOOD, Character.valueOf('X'), Items.BOOK}); + this.registerShapedRecipe(new ItemStack(Blocks.SNOW_BLOCK, 1), new Object[] { "##", "##", Character.valueOf('#'), Items.SNOW_BALL}); + this.registerShapedRecipe(new ItemStack(Blocks.SNOW, 6), new Object[] { "###", Character.valueOf('#'), Blocks.SNOW_BLOCK}); + this.registerShapedRecipe(new ItemStack(Blocks.CLAY, 1), new Object[] { "##", "##", Character.valueOf('#'), Items.CLAY_BALL}); + this.registerShapedRecipe(new ItemStack(Blocks.BRICK, 1), new Object[] { "##", "##", Character.valueOf('#'), Items.CLAY_BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.GLOWSTONE, 1), new Object[] { "##", "##", Character.valueOf('#'), Items.GLOWSTONE_DUST}); + this.registerShapedRecipe(new ItemStack(Blocks.QUARTZ_BLOCK, 1), new Object[] { "##", "##", Character.valueOf('#'), Items.QUARTZ}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOL, 1), new Object[] { "##", "##", Character.valueOf('#'), Items.STRING}); + this.registerShapedRecipe(new ItemStack(Blocks.TNT, 1), new Object[] { "X#X", "#X#", "X#X", Character.valueOf('X'), Items.SULPHUR, Character.valueOf('#'), Blocks.SAND}); + this.registerShapedRecipe(new ItemStack(Blocks.STEP, 6, 3), new Object[] { "###", Character.valueOf('#'), Blocks.COBBLESTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.STEP, 6, 0), new Object[] { "###", Character.valueOf('#'), Blocks.STONE}); + this.registerShapedRecipe(new ItemStack(Blocks.STEP, 6, 1), new Object[] { "###", Character.valueOf('#'), Blocks.SANDSTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.STEP, 6, 4), new Object[] { "###", Character.valueOf('#'), Blocks.BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.STEP, 6, 5), new Object[] { "###", Character.valueOf('#'), Blocks.SMOOTH_BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.STEP, 6, 6), new Object[] { "###", Character.valueOf('#'), Blocks.NETHER_BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.STEP, 6, 7), new Object[] { "###", Character.valueOf('#'), Blocks.QUARTZ_BLOCK}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_STEP, 6, 0), new Object[] { "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 0)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_STEP, 6, 2), new Object[] { "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 2)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_STEP, 6, 1), new Object[] { "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 1)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_STEP, 6, 3), new Object[] { "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 3)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_STEP, 6, 4), new Object[] { "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 4)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_STEP, 6, 5), new Object[] { "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 5)}); + this.registerShapedRecipe(new ItemStack(Blocks.LADDER, 3), new Object[] { "# #", "###", "# #", Character.valueOf('#'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Items.WOOD_DOOR, 1), new Object[] { "##", "##", "##", Character.valueOf('#'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Blocks.TRAP_DOOR, 2), new Object[] { "###", "###", Character.valueOf('#'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Items.IRON_DOOR, 1), new Object[] { "##", "##", "##", Character.valueOf('#'), Items.IRON_INGOT}); + this.registerShapedRecipe(new ItemStack(Items.SIGN, 3), new Object[] { "###", "###", " X ", Character.valueOf('#'), Blocks.WOOD, Character.valueOf('X'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Items.CAKE, 1), new Object[] { "AAA", "BEB", "CCC", Character.valueOf('A'), Items.MILK_BUCKET, Character.valueOf('B'), Items.SUGAR, Character.valueOf('C'), Items.WHEAT, Character.valueOf('E'), Items.EGG}); + this.registerShapedRecipe(new ItemStack(Items.SUGAR, 1), new Object[] { "#", Character.valueOf('#'), Items.SUGAR_CANE}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD, 4, 0), new Object[] { "#", Character.valueOf('#'), new ItemStack(Blocks.LOG, 1, 0)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD, 4, 1), new Object[] { "#", Character.valueOf('#'), new ItemStack(Blocks.LOG, 1, 1)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD, 4, 2), new Object[] { "#", Character.valueOf('#'), new ItemStack(Blocks.LOG, 1, 2)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD, 4, 3), new Object[] { "#", Character.valueOf('#'), new ItemStack(Blocks.LOG, 1, 3)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD, 4, 4), new Object[] { "#", Character.valueOf('#'), new ItemStack(Blocks.LOG2, 1, 0)}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD, 4, 5), new Object[] { "#", Character.valueOf('#'), new ItemStack(Blocks.LOG2, 1, 1)}); + this.registerShapedRecipe(new ItemStack(Items.STICK, 4), new Object[] { "#", "#", Character.valueOf('#'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Blocks.TORCH, 4), new Object[] { "X", "#", Character.valueOf('X'), Items.COAL, Character.valueOf('#'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Blocks.TORCH, 4), new Object[] { "X", "#", Character.valueOf('X'), new ItemStack(Items.COAL, 1, 1), Character.valueOf('#'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Items.BOWL, 4), new Object[] { "# #", " # ", Character.valueOf('#'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Items.GLASS_BOTTLE, 3), new Object[] { "# #", " # ", Character.valueOf('#'), Blocks.GLASS}); + this.registerShapedRecipe(new ItemStack(Blocks.RAILS, 16), new Object[] { "X X", "X#X", "X X", Character.valueOf('X'), Items.IRON_INGOT, Character.valueOf('#'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Blocks.GOLDEN_RAIL, 6), new Object[] { "X X", "X#X", "XRX", Character.valueOf('X'), Items.GOLD_INGOT, Character.valueOf('R'), Items.REDSTONE, Character.valueOf('#'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Blocks.ACTIVATOR_RAIL, 6), new Object[] { "XSX", "X#X", "XSX", Character.valueOf('X'), Items.IRON_INGOT, Character.valueOf('#'), Blocks.REDSTONE_TORCH_ON, Character.valueOf('S'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Blocks.DETECTOR_RAIL, 6), new Object[] { "X X", "X#X", "XRX", Character.valueOf('X'), Items.IRON_INGOT, Character.valueOf('R'), Items.REDSTONE, Character.valueOf('#'), Blocks.STONE_PLATE}); + this.registerShapedRecipe(new ItemStack(Items.MINECART, 1), new Object[] { "# #", "###", Character.valueOf('#'), Items.IRON_INGOT}); + this.registerShapedRecipe(new ItemStack(Items.CAULDRON, 1), new Object[] { "# #", "# #", "###", Character.valueOf('#'), Items.IRON_INGOT}); + this.registerShapedRecipe(new ItemStack(Items.BREWING_STAND, 1), new Object[] { " B ", "###", Character.valueOf('#'), Blocks.COBBLESTONE, Character.valueOf('B'), Items.BLAZE_ROD}); + this.registerShapedRecipe(new ItemStack(Blocks.JACK_O_LANTERN, 1), new Object[] { "A", "B", Character.valueOf('A'), Blocks.PUMPKIN, Character.valueOf('B'), Blocks.TORCH}); + this.registerShapedRecipe(new ItemStack(Items.STORAGE_MINECART, 1), new Object[] { "A", "B", Character.valueOf('A'), Blocks.CHEST, Character.valueOf('B'), Items.MINECART}); + this.registerShapedRecipe(new ItemStack(Items.POWERED_MINECART, 1), new Object[] { "A", "B", Character.valueOf('A'), Blocks.FURNACE, Character.valueOf('B'), Items.MINECART}); + this.registerShapedRecipe(new ItemStack(Items.MINECART_TNT, 1), new Object[] { "A", "B", Character.valueOf('A'), Blocks.TNT, Character.valueOf('B'), Items.MINECART}); + this.registerShapedRecipe(new ItemStack(Items.MINECART_HOPPER, 1), new Object[] { "A", "B", Character.valueOf('A'), Blocks.HOPPER, Character.valueOf('B'), Items.MINECART}); + this.registerShapedRecipe(new ItemStack(Items.BOAT, 1), new Object[] { "# #", "###", Character.valueOf('#'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Items.BUCKET, 1), new Object[] { "# #", " # ", Character.valueOf('#'), Items.IRON_INGOT}); + this.registerShapedRecipe(new ItemStack(Items.FLOWER_POT, 1), new Object[] { "# #", " # ", Character.valueOf('#'), Items.CLAY_BRICK}); + this.registerShapelessRecipe(new ItemStack(Items.FLINT_AND_STEEL, 1), new Object[] { new ItemStack(Items.IRON_INGOT, 1), new ItemStack(Items.FLINT, 1)}); + this.registerShapedRecipe(new ItemStack(Items.BREAD, 1), new Object[] { "###", Character.valueOf('#'), Items.WHEAT}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 0)}); + this.registerShapedRecipe(new ItemStack(Blocks.BIRCH_WOOD_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 2)}); + this.registerShapedRecipe(new ItemStack(Blocks.SPRUCE_WOOD_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 1)}); + this.registerShapedRecipe(new ItemStack(Blocks.JUNGLE_WOOD_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 3)}); + this.registerShapedRecipe(new ItemStack(Blocks.ACACIA_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 4)}); + this.registerShapedRecipe(new ItemStack(Blocks.DARK_OAK_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), new ItemStack(Blocks.WOOD, 1, 5)}); + this.registerShapedRecipe(new ItemStack(Items.FISHING_ROD, 1), new Object[] { " #", " #X", "# X", Character.valueOf('#'), Items.STICK, Character.valueOf('X'), Items.STRING}); + this.registerShapedRecipe(new ItemStack(Items.CARROT_STICK, 1), new Object[] { "# ", " X", Character.valueOf('#'), Items.FISHING_ROD, Character.valueOf('X'), Items.CARROT}).c(); + this.registerShapedRecipe(new ItemStack(Blocks.COBBLESTONE_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), Blocks.COBBLESTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.BRICK_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), Blocks.BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.STONE_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), Blocks.SMOOTH_BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.NETHER_BRICK_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), Blocks.NETHER_BRICK}); + this.registerShapedRecipe(new ItemStack(Blocks.SANDSTONE_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), Blocks.SANDSTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.QUARTZ_STAIRS, 4), new Object[] { "# ", "## ", "###", Character.valueOf('#'), Blocks.QUARTZ_BLOCK}); + this.registerShapedRecipe(new ItemStack(Items.PAINTING, 1), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Items.STICK, Character.valueOf('X'), Blocks.WOOL}); + this.registerShapedRecipe(new ItemStack(Items.ITEM_FRAME, 1), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Items.STICK, Character.valueOf('X'), Items.LEATHER}); + this.registerShapedRecipe(new ItemStack(Items.GOLDEN_APPLE, 1, 0), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Items.GOLD_INGOT, Character.valueOf('X'), Items.APPLE}); + this.registerShapedRecipe(new ItemStack(Items.GOLDEN_APPLE, 1, 1), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Blocks.GOLD_BLOCK, Character.valueOf('X'), Items.APPLE}); + this.registerShapedRecipe(new ItemStack(Items.CARROT_GOLDEN, 1, 0), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Items.GOLD_NUGGET, Character.valueOf('X'), Items.CARROT}); + this.registerShapedRecipe(new ItemStack(Items.SPECKLED_MELON, 1), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Items.GOLD_NUGGET, Character.valueOf('X'), Items.MELON}); + this.registerShapedRecipe(new ItemStack(Blocks.LEVER, 1), new Object[] { "X", "#", Character.valueOf('#'), Blocks.COBBLESTONE, Character.valueOf('X'), Items.STICK}); + this.registerShapedRecipe(new ItemStack(Blocks.TRIPWIRE_SOURCE, 2), new Object[] { "I", "S", "#", Character.valueOf('#'), Blocks.WOOD, Character.valueOf('S'), Items.STICK, Character.valueOf('I'), Items.IRON_INGOT}); + this.registerShapedRecipe(new ItemStack(Blocks.REDSTONE_TORCH_ON, 1), new Object[] { "X", "#", Character.valueOf('#'), Items.STICK, Character.valueOf('X'), Items.REDSTONE}); + this.registerShapedRecipe(new ItemStack(Items.DIODE, 1), new Object[] { "#X#", "III", Character.valueOf('#'), Blocks.REDSTONE_TORCH_ON, Character.valueOf('X'), Items.REDSTONE, Character.valueOf('I'), Blocks.STONE}); + this.registerShapedRecipe(new ItemStack(Items.REDSTONE_COMPARATOR, 1), new Object[] { " # ", "#X#", "III", Character.valueOf('#'), Blocks.REDSTONE_TORCH_ON, Character.valueOf('X'), Items.QUARTZ, Character.valueOf('I'), Blocks.STONE}); + this.registerShapedRecipe(new ItemStack(Items.WATCH, 1), new Object[] { " # ", "#X#", " # ", Character.valueOf('#'), Items.GOLD_INGOT, Character.valueOf('X'), Items.REDSTONE}); + this.registerShapedRecipe(new ItemStack(Items.COMPASS, 1), new Object[] { " # ", "#X#", " # ", Character.valueOf('#'), Items.IRON_INGOT, Character.valueOf('X'), Items.REDSTONE}); + this.registerShapedRecipe(new ItemStack(Items.MAP_EMPTY, 1), new Object[] { "###", "#X#", "###", Character.valueOf('#'), Items.PAPER, Character.valueOf('X'), Items.COMPASS}); + this.registerShapedRecipe(new ItemStack(Blocks.STONE_BUTTON, 1), new Object[] { "#", Character.valueOf('#'), Blocks.STONE}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_BUTTON, 1), new Object[] { "#", Character.valueOf('#'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Blocks.STONE_PLATE, 1), new Object[] { "##", Character.valueOf('#'), Blocks.STONE}); + this.registerShapedRecipe(new ItemStack(Blocks.WOOD_PLATE, 1), new Object[] { "##", Character.valueOf('#'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Blocks.IRON_PLATE, 1), new Object[] { "##", Character.valueOf('#'), Items.IRON_INGOT}); + this.registerShapedRecipe(new ItemStack(Blocks.GOLD_PLATE, 1), new Object[] { "##", Character.valueOf('#'), Items.GOLD_INGOT}); + this.registerShapedRecipe(new ItemStack(Blocks.DISPENSER, 1), new Object[] { "###", "#X#", "#R#", Character.valueOf('#'), Blocks.COBBLESTONE, Character.valueOf('X'), Items.BOW, Character.valueOf('R'), Items.REDSTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.DROPPER, 1), new Object[] { "###", "# #", "#R#", Character.valueOf('#'), Blocks.COBBLESTONE, Character.valueOf('R'), Items.REDSTONE}); + this.registerShapedRecipe(new ItemStack(Blocks.PISTON, 1), new Object[] { "TTT", "#X#", "#R#", Character.valueOf('#'), Blocks.COBBLESTONE, Character.valueOf('X'), Items.IRON_INGOT, Character.valueOf('R'), Items.REDSTONE, Character.valueOf('T'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Blocks.PISTON_STICKY, 1), new Object[] { "S", "P", Character.valueOf('S'), Items.SLIME_BALL, Character.valueOf('P'), Blocks.PISTON}); + this.registerShapedRecipe(new ItemStack(Items.BED, 1), new Object[] { "###", "XXX", Character.valueOf('#'), Blocks.WOOL, Character.valueOf('X'), Blocks.WOOD}); + this.registerShapedRecipe(new ItemStack(Blocks.ENCHANTMENT_TABLE, 1), new Object[] { " B ", "D#D", "###", Character.valueOf('#'), Blocks.OBSIDIAN, Character.valueOf('B'), Items.BOOK, Character.valueOf('D'), Items.DIAMOND}); + this.registerShapedRecipe(new ItemStack(Blocks.ANVIL, 1), new Object[] { "III", " i ", "iii", Character.valueOf('I'), Blocks.IRON_BLOCK, Character.valueOf('i'), Items.IRON_INGOT}); + this.registerShapelessRecipe(new ItemStack(Items.EYE_OF_ENDER, 1), new Object[] { Items.ENDER_PEARL, Items.BLAZE_POWDER}); + this.registerShapelessRecipe(new ItemStack(Items.FIREBALL, 3), new Object[] { Items.SULPHUR, Items.BLAZE_POWDER, Items.COAL}); + this.registerShapelessRecipe(new ItemStack(Items.FIREBALL, 3), new Object[] { Items.SULPHUR, Items.BLAZE_POWDER, new ItemStack(Items.COAL, 1, 1)}); + this.registerShapedRecipe(new ItemStack(Blocks.DAYLIGHT_DETECTOR), new Object[] { "GGG", "QQQ", "WWW", Character.valueOf('G'), Blocks.GLASS, Character.valueOf('Q'), Items.QUARTZ, Character.valueOf('W'), Blocks.WOOD_STEP}); + this.registerShapedRecipe(new ItemStack(Blocks.HOPPER), new Object[] { "I I", "ICI", " I ", Character.valueOf('I'), Items.IRON_INGOT, Character.valueOf('C'), Blocks.CHEST}); + // PaperSpigot start - Register moss stone and mossy and chiseled stone brick recipes + this.registerShapelessRecipe(new ItemStack(Blocks.MOSSY_COBBLESTONE), new Object[] { Blocks.VINE, Blocks.COBBLESTONE}); + this.registerShapelessRecipe(new ItemStack(Blocks.SMOOTH_BRICK, 1, 1), new Object[] { Blocks.VINE, Blocks.SMOOTH_BRICK}); + this.registerShapelessRecipe(new ItemStack(Blocks.SMOOTH_BRICK, 1, 3), new Object[] { new ItemStack(Blocks.STEP, 1, 5), new ItemStack(Blocks.STEP, 1, 5)}); + // PaperSpigot end + // Collections.sort(this.recipes, new RecipeSorter(this)); // CraftBukkit - moved below + this.sort(); // CraftBukkit - call new sort method + } + + // CraftBukkit start + public void sort() { + Collections.sort(this.recipes, new RecipeSorter(this)); + } + // CraftBukkit end + + // CraftBukkit - default -> public + public ShapedRecipes registerShapedRecipe(ItemStack itemstack, Object... aobject) { + String s = ""; + int i = 0; + int j = 0; + int k = 0; + + if (aobject[i] instanceof String[]) { + String[] astring = (String[]) ((String[]) aobject[i++]); + + for (int l = 0; l < astring.length; ++l) { + String s1 = astring[l]; + + ++k; + j = s1.length(); + s = s + s1; + } + } else { + while (aobject[i] instanceof String) { + String s2 = (String) aobject[i++]; + + ++k; + j = s2.length(); + s = s + s2; + } + } + + HashMap hashmap; + + for (hashmap = new HashMap(); i < aobject.length; i += 2) { + Character character = (Character) aobject[i]; + ItemStack itemstack1 = null; + + if (aobject[i + 1] instanceof Item) { + itemstack1 = new ItemStack((Item) aobject[i + 1]); + } else if (aobject[i + 1] instanceof Block) { + itemstack1 = new ItemStack((Block) aobject[i + 1], 1, 32767); + } else if (aobject[i + 1] instanceof ItemStack) { + itemstack1 = (ItemStack) aobject[i + 1]; + } + + hashmap.put(character, itemstack1); + } + + ItemStack[] aitemstack = new ItemStack[j * k]; + + for (int i1 = 0; i1 < j * k; ++i1) { + char c0 = s.charAt(i1); + + if (hashmap.containsKey(Character.valueOf(c0))) { + aitemstack[i1] = ((ItemStack) hashmap.get(Character.valueOf(c0))).cloneItemStack(); + } else { + aitemstack[i1] = null; + } + } + + ShapedRecipes shapedrecipes = new ShapedRecipes(j, k, aitemstack, itemstack); + + this.recipes.add(shapedrecipes); + return shapedrecipes; + } + + // CraftBukkit - default -> public + public void registerShapelessRecipe(ItemStack itemstack, Object... aobject) { + ArrayList arraylist = new ArrayList(); + Object[] aobject1 = aobject; + int i = aobject.length; + + for (int j = 0; j < i; ++j) { + Object object = aobject1[j]; + + if (object instanceof ItemStack) { + arraylist.add(((ItemStack) object).cloneItemStack()); + } else if (object instanceof Item) { + arraylist.add(new ItemStack((Item) object)); + } else { + if (!(object instanceof Block)) { + throw new RuntimeException("Invalid shapeless recipy!"); + } + + arraylist.add(new ItemStack((Block) object)); + } + } + + this.recipes.add(new ShapelessRecipes(itemstack, arraylist)); + } + + public ItemStack craft(InventoryCrafting inventorycrafting, World world) { + int i = 0; + ItemStack itemstack = null; + ItemStack itemstack1 = null; + + int j; + + for (j = 0; j < inventorycrafting.getSize(); ++j) { + ItemStack itemstack2 = inventorycrafting.getItem(j); + + if (itemstack2 != null) { + if (i == 0) { + itemstack = itemstack2; + } + + if (i == 1) { + itemstack1 = itemstack2; + } + + ++i; + } + } + + if (i == 2 && itemstack.getItem() == itemstack1.getItem() && itemstack.count == 1 && itemstack1.count == 1 && itemstack.getItem().usesDurability()) { + Item item = itemstack.getItem(); + int k = item.getMaxDurability() - itemstack.j(); + int l = item.getMaxDurability() - itemstack1.j(); + int i1 = k + l + item.getMaxDurability() * 5 / 100; + int j1 = item.getMaxDurability() - i1; + + if (j1 < 0) { + j1 = 0; + } + + // CraftBukkit start - Construct a dummy repair recipe + ItemStack result = new ItemStack(itemstack.getItem(), 1, j1); + List ingredients = new ArrayList(); + ingredients.add(itemstack.cloneItemStack()); + ingredients.add(itemstack1.cloneItemStack()); + ShapelessRecipes recipe = new ShapelessRecipes(result.cloneItemStack(), ingredients); + inventorycrafting.currentRecipe = recipe; + result = CraftEventFactory.callPreCraftEvent(inventorycrafting, result, lastCraftView, true); + return result; + // CraftBukkit end + } else { + for (j = 0; j < this.recipes.size(); ++j) { + IRecipe irecipe = (IRecipe) this.recipes.get(j); + + if (irecipe.a(inventorycrafting, world)) { + // CraftBukkit start - INVENTORY_PRE_CRAFT event + inventorycrafting.currentRecipe = irecipe; + ItemStack result = irecipe.a(inventorycrafting); + return CraftEventFactory.callPreCraftEvent(inventorycrafting, result, lastCraftView, false); + // CraftBukkit end + } + } + + inventorycrafting.currentRecipe = null; // CraftBukkit - Clear recipe when no recipe is found + return null; + } + } + + public List getRecipes() { + return this.recipes; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/CrashReport.java b/vspigot-server/src/main/java/net/minecraft/server/CrashReport.java new file mode 100644 index 0000000..7e06002 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/CrashReport.java @@ -0,0 +1,235 @@ +package net.minecraft.server; + +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; + +import net.minecraft.util.org.apache.commons.io.IOUtils; +import net.minecraft.util.org.apache.commons.lang3.ArrayUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class CrashReport { + + private static final Logger a = LogManager.getLogger(); + private final String b; + private final Throwable c; + private final CrashReportSystemDetails d = new CrashReportSystemDetails(this, "System Details"); + private final List e = new ArrayList(); + private File f; + private boolean g = true; + private StackTraceElement[] h = new StackTraceElement[0]; + + public CrashReport(String s, Throwable throwable) { + this.b = s; + this.c = throwable; + this.h(); + } + + private void h() { + this.d.a("Minecraft Version", (Callable) (new CrashReportVersion(this))); + this.d.a("Operating System", (Callable) (new CrashReportOperatingSystem(this))); + this.d.a("Java Version", (Callable) (new CrashReportJavaVersion(this))); + this.d.a("Java VM Version", (Callable) (new CrashReportJavaVMVersion(this))); + this.d.a("Memory", (Callable) (new CrashReportMemory(this))); + this.d.a("JVM Flags", (Callable) (new CrashReportJVMFlags(this))); + this.d.a("AABB Pool Size", (Callable) (new CrashReportAABBPoolSize(this))); + this.d.a("IntCache", (Callable) (new CrashReportIntCacheSize(this))); + this.d.a("CraftBukkit Information", (Callable) (new org.bukkit.craftbukkit.CraftCrashReport())); // CraftBukkit + } + + public String a() { + return this.b; + } + + public Throwable b() { + return this.c; + } + + public void a(StringBuilder stringbuilder) { + if ((this.h == null || this.h.length <= 0) && this.e.size() > 0) { + this.h = (StackTraceElement[]) ArrayUtils.subarray(((CrashReportSystemDetails) this.e.get(0)).a(), 0, 1); + } + + if (this.h != null && this.h.length > 0) { + stringbuilder.append("-- Head --\n"); + stringbuilder.append("Stacktrace:\n"); + StackTraceElement[] astacktraceelement = this.h; + int i = astacktraceelement.length; + + for (int j = 0; j < i; ++j) { + StackTraceElement stacktraceelement = astacktraceelement[j]; + + stringbuilder.append("\t").append("at ").append(stacktraceelement.toString()); + stringbuilder.append("\n"); + } + + stringbuilder.append("\n"); + } + + Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + CrashReportSystemDetails crashreportsystemdetails = (CrashReportSystemDetails) iterator.next(); + + crashreportsystemdetails.a(stringbuilder); + stringbuilder.append("\n\n"); + } + + this.d.a(stringbuilder); + } + + public String d() { + StringWriter stringwriter = null; + PrintWriter printwriter = null; + Object object = this.c; + + if (((Throwable) object).getMessage() == null) { + if (object instanceof NullPointerException) { + object = new NullPointerException(this.b); + } else if (object instanceof StackOverflowError) { + object = new StackOverflowError(this.b); + } else if (object instanceof OutOfMemoryError) { + object = new OutOfMemoryError(this.b); + } + + ((Throwable) object).setStackTrace(this.c.getStackTrace()); + } + + String s = ((Throwable) object).toString(); + + try { + stringwriter = new StringWriter(); + printwriter = new PrintWriter(stringwriter); + ((Throwable) object).printStackTrace(printwriter); + s = stringwriter.toString(); + } finally { + IOUtils.closeQuietly(stringwriter); + IOUtils.closeQuietly(printwriter); + } + + return s; + } + + public String e() { + StringBuilder stringbuilder = new StringBuilder(); + + stringbuilder.append("---- Minecraft Crash Report ----\n"); + stringbuilder.append("// "); + stringbuilder.append(i()); + stringbuilder.append("\n\n"); + stringbuilder.append("Time: "); + stringbuilder.append((new SimpleDateFormat()).format(new Date())); + stringbuilder.append("\n"); + stringbuilder.append("Description: "); + stringbuilder.append(this.b); + stringbuilder.append("\n\n"); + stringbuilder.append(this.d()); + stringbuilder.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n"); + + for (int i = 0; i < 87; ++i) { + stringbuilder.append("-"); + } + + stringbuilder.append("\n\n"); + this.a(stringbuilder); + return stringbuilder.toString(); + } + + public boolean a(File file1) { + if (this.f != null) { + return false; + } else { + if (file1.getParentFile() != null) { + file1.getParentFile().mkdirs(); + } + + try { + FileWriter filewriter = new FileWriter(file1); + + filewriter.write(this.e()); + filewriter.close(); + this.f = file1; + return true; + } catch (Throwable throwable) { + a.error("Could not save crash report to " + file1, throwable); + return false; + } + } + } + + public CrashReportSystemDetails g() { + return this.d; + } + + public CrashReportSystemDetails a(String s) { + return this.a(s, 1); + } + + public CrashReportSystemDetails a(String s, int i) { + CrashReportSystemDetails crashreportsystemdetails = new CrashReportSystemDetails(this, s); + + if (this.g) { + int j = crashreportsystemdetails.a(i); + StackTraceElement[] astacktraceelement = this.c.getStackTrace(); + StackTraceElement stacktraceelement = null; + StackTraceElement stacktraceelement1 = null; + int k = astacktraceelement.length - j; + + if (k < 0) { + System.out.println("Negative index in crash report handler (" + astacktraceelement.length + "/" + j + ")"); + } + + if (astacktraceelement != null && 0 <= k && k < astacktraceelement.length) { + stacktraceelement = astacktraceelement[k]; + if (astacktraceelement.length + 1 - j < astacktraceelement.length) { + stacktraceelement1 = astacktraceelement[astacktraceelement.length + 1 - j]; + } + } + + this.g = crashreportsystemdetails.a(stacktraceelement, stacktraceelement1); + if (j > 0 && !this.e.isEmpty()) { + CrashReportSystemDetails crashreportsystemdetails1 = (CrashReportSystemDetails) this.e.get(this.e.size() - 1); + + crashreportsystemdetails1.b(j); + } else if (astacktraceelement != null && astacktraceelement.length >= j && 0 <= k && k < astacktraceelement.length) { + this.h = new StackTraceElement[astacktraceelement.length - j]; + System.arraycopy(astacktraceelement, 0, this.h, 0, this.h.length); + } else { + this.g = false; + } + } + + this.e.add(crashreportsystemdetails); + return crashreportsystemdetails; + } + + private static String i() { + String[] astring = new String[] { "Who set us up the TNT?", "Everything\'s going to plan. No, really, that was supposed to happen.", "Uh... Did I do that?", "Oops.", "Why did you do that?", "I feel sad now :(", "My bad.", "I\'m sorry, Dave.", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", "Hey, that tickles! Hehehe!", "I blame Dinnerbone.", "You should try our sister game, Minceraft!", "Don\'t be sad. I\'ll do better next time, I promise!", "Don\'t be sad, have a hug! <3", "I just don\'t know what went wrong :(", "Shall we play a game?", "Quite honestly, I wouldn\'t worry myself about that.", "I bet Cylons wouldn\'t have this problem.", "Sorry :(", "Surprise! Haha. Well, this is awkward.", "Would you like a cupcake?", "Hi. I\'m Minecraft, and I\'m a crashaholic.", "Ooh. Shiny.", "This doesn\'t make any sense!", "Why is it breaking :(", "Don\'t do that.", "Ouch. That hurt :(", "You\'re mean.", "This is a token for 1 free hug. Redeem at your nearest Mojangsta: [~~HUG~~]", "There are four lights!", "But it works on my machine."}; + + try { + return astring[(int) (System.nanoTime() % (long) astring.length)]; + } catch (Throwable throwable) { + return "Witty comment unavailable :("; + } + } + + public static CrashReport a(Throwable throwable, String s) { + CrashReport crashreport; + + if (throwable instanceof ReportedException) { + crashreport = ((ReportedException) throwable).a(); + } else { + crashreport = new CrashReport(s, throwable); + } + + return crashreport; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DataWatcher.java b/vspigot-server/src/main/java/net/minecraft/server/DataWatcher.java new file mode 100644 index 0000000..46a8e17 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DataWatcher.java @@ -0,0 +1,379 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.valorhcf.util.WrappedArrayMap; +import net.minecraft.util.org.apache.commons.lang3.ObjectUtils; +import org.spigotmc.ProtocolData; // Spigot - protocol patch + +public class DataWatcher { + + private final Entity a; + private boolean b = true; + // Spigot Start + private static final net.minecraft.util.gnu.trove.map.TObjectIntMap classToId = new net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap( 10, 0.5f, -1 ); + // private final net.minecraft.util.gnu.trove.map.TIntObjectMap dataValues = new net.minecraft.util.gnu.trove.map.hash.TIntObjectHashMap( 10, 0.5f, -1 ); // MineHQ + private final WrappedArrayMap dataValues; // MineHQ + // These exist as an attempt at backwards compatability for (broken) NMS plugins + private static final Map c = net.minecraft.util.gnu.trove.TDecorators.wrap( classToId ); + // Spigot End + private boolean e; + // private ReadWriteLock f = new ReentrantReadWriteLock(); // MineHQ + + public DataWatcher(Entity entity) { + this.a = entity; + this.dataValues = new WrappedArrayMap(); // MineHQ - lockless DataWatcher + } + + // MineHQ start + public DataWatcher(DataWatcher dataWatcher) { + this.a = dataWatcher.a; + this.dataValues = dataWatcher.dataValues.clone(); + this.e = dataWatcher.e; + } + // MineHQ end + + public void a(int i, Object object) { + int integer = classToId.get(object.getClass()); // Spigot + + // Spigot start - protocol patch + if ( object instanceof ProtocolData.ByteShort + || object instanceof ProtocolData.DualByte + || object instanceof ProtocolData.HiddenByte ) + { + integer = classToId.get( Byte.class ); + } + if ( object instanceof ProtocolData.IntByte + || object instanceof ProtocolData.DualInt ) { + integer = classToId.get( Integer.class ); + } + // Spigot end + + if (integer == -1) { // Spigot + throw new IllegalArgumentException("Unknown data type: " + object.getClass()); + } else if (i > 31) { + throw new IllegalArgumentException("Data value id is too big with " + i + "! (Max is " + 31 + ")"); + } else if (this.dataValues.containsKey(i)) { // Spigot + throw new IllegalArgumentException("Duplicate id value for " + i + "!"); + } else { + WatchableObject watchableobject = new WatchableObject(integer, i, object); // Spigot + + // this.f.writeLock().lock(); // MineHQ + this.dataValues.put(i, watchableobject); // Spigot + // this.f.writeLock().unlock(); // MineHQ + this.b = false; + } + } + + public void add(int i, int j) { + WatchableObject watchableobject = new WatchableObject(j, i, null); + + // this.f.writeLock().lock(); // MineHQ + this.dataValues.put(i, watchableobject); // Spigot + // this.f.writeLock().unlock(); // MineHQ + this.b = false; + } + + public byte getByte(int i) { + return ((Number) this.i(i).b()).byteValue(); // Spigot - protocol patch + } + + public short getShort(int i) { + return ((Number) this.i(i).b()).shortValue(); // Spigot - protocol patch + } + + public int getInt(int i) { + return ((Number) this.i(i).b()).intValue(); // Spigot - protocol patch + } + + public float getFloat(int i) { + return ((Number) this.i(i).b()).floatValue(); // Spigot - protocol patch + } + + public String getString(int i) { + return (String) this.i(i).b(); + } + + public ItemStack getItemStack(int i) { + return (ItemStack) this.i(i).b(); + } + + // Spigot start - protocol patch + public ProtocolData.DualByte getDualByte(int i) { + return (ProtocolData.DualByte) this.i(i).b(); + } + public ProtocolData.IntByte getIntByte(int i) { + return (ProtocolData.IntByte) this.i(i).b(); + } + public ProtocolData.DualInt getDualInt(int i) { + return (ProtocolData.DualInt) this.i(i).b(); + } + // Spigot end + + private WatchableObject i(int i) { + // this.f.readLock().lock(); // MineHQ + + WatchableObject watchableobject; + + try { + watchableobject = (WatchableObject) this.dataValues.get(i); // Spigot + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Getting synched entity data"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Synched entity data"); + + crashreportsystemdetails.a("Data ID", Integer.valueOf(i)); + throw new ReportedException(crashreport); + } + + // this.f.readLock().unlock(); // MineHQ + return watchableobject; + } + + public void watch(int i, Object object) { + WatchableObject watchableobject = this.i(i); + + if (ObjectUtils.notEqual(object, watchableobject.b())) { + watchableobject.a(object); + this.a.i(i); + watchableobject.a(true); + this.e = true; + } + } + + public void update(int i) { + WatchableObject.a(this.i(i), true); + this.e = true; + } + + public boolean a() { + return this.e; + } + + // Spigot start - protocol patch + public static void a(List list, PacketDataSerializer packetdataserializer) { + a(list, packetdataserializer, 5); + } + + public static void a(List list, PacketDataSerializer packetdataserializer, int version) { + // Spigot end - protocol patch + if (list != null) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + WatchableObject watchableobject = (WatchableObject) iterator.next(); + + a(packetdataserializer, watchableobject, version); // Spigot - protocol patch + } + } + + packetdataserializer.writeByte(127); + } + + public List b() { + ArrayList arraylist = null; + + if (this.e) { + // MineHQ start + for (int i = 0; i < this.dataValues.size(); i++) { + WatchableObject watchableobject = this.dataValues.get(i); + + if (watchableobject != null && watchableobject.d()) { + watchableobject.a(false); + if (arraylist == null) { + arraylist = new ArrayList(); + } + + arraylist.add(watchableobject.b() instanceof ItemStack ? new WatchableObject(watchableobject.c(), watchableobject.a(), ((ItemStack) watchableobject.b()).cloneItemStack()) : watchableobject.clone()); + } + } + + // MineHQ end + } + + this.e = false; + return arraylist; + } + + // Spigot start - protocol patch + public void a(PacketDataSerializer packetdataserializer) { + a(packetdataserializer, 5); + } + + public void a(PacketDataSerializer packetdataserializer, int version) { + // Spigot end + // this.f.readLock().lock(); // MineHQ + Iterator iterator = this.dataValues.values().iterator(); // Spigot // MineHQ + + while (iterator.hasNext()) { + WatchableObject watchableobject = (WatchableObject) iterator.next(); + + a(packetdataserializer, watchableobject, version); // Spigot - protocol patch + } + + // this.f.readLock().unlock(); // MineHQ + packetdataserializer.writeByte(127); + } + public List c() { + ArrayList arraylist = new ArrayList(); // Spigot + + // MineHQ start + for (int i = 0; i < this.dataValues.size(); i++) { + WatchableObject watchableObject = this.dataValues.get(i); + if (watchableObject == null) continue; + + arraylist.add(watchableObject.b() instanceof ItemStack ? new WatchableObject(watchableObject.c(), watchableObject.a(), ((ItemStack) watchableObject.b()).cloneItemStack()) : watchableObject.clone()); + } + // MineHQ end + return arraylist; + } + + // Spigot start - protocol patch + private static void a(PacketDataSerializer packetdataserializer, WatchableObject watchableobject, int version) { + int type = watchableobject.c(); + if (watchableobject.b() instanceof ProtocolData.ByteShort && version >= 16) { + type = 1; + } + if (watchableobject.b() instanceof ProtocolData.IntByte && version >= 28) { + type = 0; + } + if ( version < 16 && watchableobject.b() instanceof ProtocolData.HiddenByte ) return; + + int i = (type << 5 | watchableobject.a() & 31) & 255; + + packetdataserializer.writeByte(i); + switch (type) { + case 0: + if ( watchableobject.b() instanceof ProtocolData.DualByte ) + { + ProtocolData.DualByte dualByte = (ProtocolData.DualByte) watchableobject.b(); + packetdataserializer.writeByte( version >= 16 ? dualByte.value2 : dualByte.value ); + } else + { + packetdataserializer.writeByte( ( (Number) watchableobject.b() ).byteValue() ); + } + break; + + case 1: + packetdataserializer.writeShort(((Number) watchableobject.b()).shortValue()); + break; + + case 2: + int val = ((Number) watchableobject.b()).intValue(); + if ( watchableobject.b() instanceof ProtocolData.DualInt && version >= 46 ) { + val = ((ProtocolData.DualInt) watchableobject.b()).value2; + } + packetdataserializer.writeInt(val); + break; + + case 3: + packetdataserializer.writeFloat(((Number) watchableobject.b()).floatValue()); + break; + + case 4: + try { + packetdataserializer.a((String) watchableobject.b()); + } catch (java.io.IOException ex) { + throw new RuntimeException(ex); + } + break; + + case 5: + ItemStack itemstack = (ItemStack) watchableobject.b(); + + packetdataserializer.a(itemstack); + break; + + case 6: + ChunkCoordinates chunkcoordinates = (ChunkCoordinates) watchableobject.b(); + + packetdataserializer.writeInt(chunkcoordinates.x); + packetdataserializer.writeInt(chunkcoordinates.y); + packetdataserializer.writeInt(chunkcoordinates.z); + } + } + // Spigot end + + public static List b(PacketDataSerializer packetdataserializer) { + ArrayList arraylist = null; + + for (byte b0 = packetdataserializer.readByte(); b0 != 127; b0 = packetdataserializer.readByte()) { + if (arraylist == null) { + arraylist = new ArrayList(); + } + + int i = (b0 & 224) >> 5; + int j = b0 & 31; + WatchableObject watchableobject = null; + + switch (i) { + case 0: + watchableobject = new WatchableObject(i, j, Byte.valueOf(packetdataserializer.readByte())); + break; + + case 1: + watchableobject = new WatchableObject(i, j, Short.valueOf(packetdataserializer.readShort())); + break; + + case 2: + watchableobject = new WatchableObject(i, j, Integer.valueOf(packetdataserializer.readInt())); + break; + + case 3: + watchableobject = new WatchableObject(i, j, Float.valueOf(packetdataserializer.readFloat())); + break; + + case 4: + try { + watchableobject = new WatchableObject(i, j, packetdataserializer.c(32767)); + } catch (java.io.IOException ex) { + throw new RuntimeException(ex); + } + break; + + case 5: + watchableobject = new WatchableObject(i, j, packetdataserializer.c()); + break; + + case 6: + int k = packetdataserializer.readInt(); + int l = packetdataserializer.readInt(); + int i1 = packetdataserializer.readInt(); + + watchableobject = new WatchableObject(i, j, new ChunkCoordinates(k, l, i1)); + } + + arraylist.add(watchableobject); + } + + return arraylist; + } + + public boolean d() { + return this.b; + } + + public void e() { + this.e = false; + } + + // MineHQ start + public DataWatcher clone() { + return new DataWatcher(this); + } + // MineHQ end + + static { + // Spigot Start - remove valueOf + classToId.put(Byte.class, 0); + classToId.put(Short.class, 1); + classToId.put(Integer.class, 2); + classToId.put(Float.class, 3); + classToId.put(String.class, 4); + classToId.put(ItemStack.class, 5); + classToId.put(ChunkCoordinates.class, 6); + // Spigot End + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DedicatedServer.java b/vspigot-server/src/main/java/net/minecraft/server/DedicatedServer.java new file mode 100644 index 0000000..6253582 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DedicatedServer.java @@ -0,0 +1,505 @@ +package net.minecraft.server; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; // PaperSpigot + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.io.PrintStream; +import org.apache.logging.log4j.Level; + +import org.bukkit.craftbukkit.LoggerOutputStream; +import org.bukkit.craftbukkit.SpigotTimings; // Spigot +import org.bukkit.event.server.ServerCommandEvent; +// CraftBukkit end + +import net.minecraft.util.com.google.common.collect.Queues; + +public class DedicatedServer extends MinecraftServer implements IMinecraftServer { + + private static final Logger i = LogManager.getLogger(); + private final Queue commandsQueue = Queues.newConcurrentLinkedQueue(); + private RemoteStatusListener k; + private RemoteControlListener l; + public PropertyManager propertyManager; // CraftBukkit - private -> public + private EULA n; + private boolean generateStructures; + private EnumGamemode p; + private boolean q; + + // CraftBukkit start - Signature changed + public DedicatedServer(joptsimple.OptionSet options) { + super(options, Proxy.NO_PROXY); + // super(file1, Proxy.NO_PROXY); + // CraftBukkit end + new ThreadSleepForever(this, "Server Infinisleeper"); + } + + protected boolean init() throws java.net.UnknownHostException { // CraftBukkit - throws UnknownHostException + ThreadCommandReader threadcommandreader = new ThreadCommandReader(this, "Server console handler"); + + threadcommandreader.setDaemon(true); + threadcommandreader.start(); + + // CraftBukkit start - TODO: handle command-line logging arguments + java.util.logging.Logger global = java.util.logging.Logger.getLogger(""); + global.setUseParentHandlers(false); + for (java.util.logging.Handler handler : global.getHandlers()) { + global.removeHandler(handler); + } + global.addHandler(new org.bukkit.craftbukkit.util.ForwardLogHandler()); + + final org.apache.logging.log4j.core.Logger logger = ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()); + for (org.apache.logging.log4j.core.Appender appender : logger.getAppenders().values()) { + if (appender instanceof org.apache.logging.log4j.core.appender.ConsoleAppender) { + logger.removeAppender(appender); + } + } + + new Thread(new org.bukkit.craftbukkit.util.TerminalConsoleWriterThread(System.out, this.reader)).start(); + + System.setOut(new PrintStream(new LoggerOutputStream(logger, Level.INFO), true)); + System.setErr(new PrintStream(new LoggerOutputStream(logger, Level.WARN), true)); + // CraftBukkit end + + aF().info("Starting minecraft server version 1.7.10"); + if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { + aF().warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); + } + + aF().info("Loading properties"); + this.propertyManager = new PropertyManager(this.options); // CraftBukkit - CLI argument support + // MineHQ - screw the EULA + if (true) { + if (this.N()) { + this.c("127.0.0.1"); + } else { + this.setOnlineMode(this.propertyManager.getBoolean("online-mode", true)); + this.c(this.propertyManager.getString("server-ip", "")); + } + + this.setSpawnAnimals(this.propertyManager.getBoolean("spawn-animals", true)); + this.setSpawnNPCs(this.propertyManager.getBoolean("spawn-npcs", true)); + this.setPvP(this.propertyManager.getBoolean("pvp", true)); + this.setAllowFlight(this.propertyManager.getBoolean("allow-flight", false)); + this.setTexturePack(this.propertyManager.getString("resource-pack", "")); + this.setMotd(this.propertyManager.getString("motd", "A Minecraft Server")); + this.setForceGamemode(this.propertyManager.getBoolean("force-gamemode", false)); + this.setIdleTimeout(this.propertyManager.getInt("player-idle-timeout", 0)); + if (this.propertyManager.getInt("difficulty", 1) < 0) { + this.propertyManager.setProperty("difficulty", Integer.valueOf(0)); + } else if (this.propertyManager.getInt("difficulty", 1) > 3) { + this.propertyManager.setProperty("difficulty", Integer.valueOf(3)); + } + + this.generateStructures = this.propertyManager.getBoolean("generate-structures", true); + int gamemode = this.propertyManager.getInt("gamemode", EnumGamemode.SURVIVAL.getId()); // CraftBukkit - Unique name to avoid stomping on logger + + this.p = WorldSettings.a(gamemode); // CraftBukkit - Use new name + aF().info("Default game type: " + this.p); + InetAddress inetaddress = null; + + if (this.getServerIp().length() > 0) { + inetaddress = InetAddress.getByName(this.getServerIp()); + } + + if (this.L() < 0) { + this.setPort(this.propertyManager.getInt("server-port", 25565)); + } + // Spigot start + this.a((PlayerList) (new DedicatedPlayerList(this))); + org.spigotmc.SpigotConfig.init(); + org.spigotmc.SpigotConfig.registerCommands(); + // Spigot end + // PaperSpigot start + org.github.paperspigot.PaperSpigotConfig.init(); + org.github.paperspigot.PaperSpigotConfig.registerCommands(); + // PaperSpigot stop + + aF().info("Generating keypair"); + this.a(MinecraftEncryption.b()); + aF().info("Starting Minecraft server on " + (this.getServerIp().length() == 0 ? "*" : this.getServerIp()) + ":" + this.L()); + + if (!org.spigotmc.SpigotConfig.lateBind) { + try { + this.ai().a(inetaddress, this.L()); + } catch (Throwable ioexception) { // CraftBukkit - IOException -> Throwable + aF().warn("**** FAILED TO BIND TO PORT!"); + aF().warn("The exception was: {}", new Object[] { ioexception.toString()}); + aF().warn("Perhaps a server is already running on that port?"); + return false; + } + } + + // Spigot Start - Move DedicatedPlayerList up and bring plugin loading from CraftServer to here + // this.a((PlayerList) (new DedicatedPlayerList(this))); // CraftBukkit + server.loadPlugins(); + server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP); + // Spigot End + + if (!this.getOnlineMode()) { + aF().warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); + aF().warn("The server will make no attempt to authenticate usernames. Beware."); + aF().warn("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose."); + aF().warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); + } + + if (this.aE()) { + this.getUserCache().c(); + } + + if (!NameReferencingFileConverter.a(this.propertyManager)) { + return false; + } else { + // this.a((PlayerList) (new DedicatedPlayerList(this))); // CraftBukkit - moved up + this.convertable = new WorldLoaderServer(server.getWorldContainer()); // CraftBukkit - moved from MinecraftServer constructor + long j = System.nanoTime(); + + if (this.O() == null) { + this.k(this.propertyManager.getString("level-name", "world")); + } + + String s = this.propertyManager.getString("level-seed", ""); + String s1 = this.propertyManager.getString("level-type", "DEFAULT"); + String s2 = this.propertyManager.getString("generator-settings", ""); + long k = (new Random()).nextLong(); + + if (s.length() > 0) { + try { + long l = Long.parseLong(s); + + if (l != 0L) { + k = l; + } + } catch (NumberFormatException numberformatexception) { + k = (long) s.hashCode(); + } + } + + WorldType worldtype = WorldType.getType(s1); + + if (worldtype == null) { + worldtype = WorldType.NORMAL; + } + + this.at(); + this.getEnableCommandBlock(); + this.l(); + this.getSnooperEnabled(); + this.c(this.propertyManager.getInt("max-build-height", 256)); + this.c((this.getMaxBuildHeight() + 8) / 16 * 16); + this.c(MathHelper.a(this.getMaxBuildHeight(), 64, 256)); + this.propertyManager.setProperty("max-build-height", Integer.valueOf(this.getMaxBuildHeight())); + aF().info("Preparing level \"" + this.O() + "\""); + this.a(this.O(), this.O(), k, worldtype, s2); + long i1 = System.nanoTime() - j; + String s3 = String.format("%.3fs", new Object[] { Double.valueOf((double) i1 / 1.0E9D)}); + + aF().info("Done (" + s3 + ")! For help, type \"help\" or \"?\""); + if (this.propertyManager.getBoolean("enable-query", false)) { + aF().info("Starting GS4 status listener"); + this.k = new RemoteStatusListener(this); + this.k.a(); + } + + if (this.propertyManager.getBoolean("enable-rcon", false)) { + aF().info("Starting remote control listener"); + this.l = new RemoteControlListener(this); + this.l.a(); + this.remoteConsole = new org.bukkit.craftbukkit.command.CraftRemoteConsoleCommandSender(); // CraftBukkit + } + + // CraftBukkit start + if (this.server.getBukkitSpawnRadius() > -1) { + aF().info("'settings.spawn-radius' in bukkit.yml has been moved to 'spawn-protection' in server.properties. I will move your config for you."); + this.propertyManager.properties.remove("spawn-protection"); + this.propertyManager.getInt("spawn-protection", this.server.getBukkitSpawnRadius()); + this.server.removeBukkitSpawnRadius(); + this.propertyManager.savePropertiesFile(); + } + // CraftBukkit end + + if (org.spigotmc.SpigotConfig.lateBind) { + try { + this.ai().a(inetaddress, this.L()); + } catch (Throwable ioexception) { // CraftBukkit - IOException -> Throwable + aF().warn("**** FAILED TO BIND TO PORT!"); + aF().warn("The exception was: {}", new Object[] { ioexception.toString()}); + aF().warn("Perhaps a server is already running on that port?"); + return false; + } + } + return true; + } + } + return true; // PaperSpigot + } + + // CraftBukkit start + public PropertyManager getPropertyManager() { + return this.propertyManager; + } + // CraftBukkit end + + public boolean getGenerateStructures() { + return this.generateStructures; + } + + public EnumGamemode getGamemode() { + return this.p; + } + + public EnumDifficulty getDifficulty() { + return EnumDifficulty.getById(this.propertyManager.getInt("difficulty", 1)); + } + + public boolean isHardcore() { + return this.propertyManager.getBoolean("hardcore", false); + } + + protected void a(CrashReport crashreport) {} + + public CrashReport b(CrashReport crashreport) { + crashreport = super.b(crashreport); + crashreport.g().a("Is Modded", (Callable) (new CrashReportModded(this))); + crashreport.g().a("Type", (Callable) (new CrashReportType(this))); + return crashreport; + } + + protected void t() { + System.exit(abnormalTermination ? 1 : 0); // SportBukkit + } + + public void v() { // CraftBukkit - protected -> public (decompile error?) + super.v(); + this.aB(); + } + + public boolean getAllowNether() { + return this.propertyManager.getBoolean("allow-nether", true); + } + + public boolean getSpawnMonsters() { + return this.propertyManager.getBoolean("spawn-monsters", true); + } + + public void a(MojangStatisticsGenerator mojangstatisticsgenerator) { + mojangstatisticsgenerator.a("whitelist_enabled", Boolean.valueOf(this.aC().getHasWhitelist())); + mojangstatisticsgenerator.a("whitelist_count", Integer.valueOf(this.aC().getWhitelisted().length)); + super.a(mojangstatisticsgenerator); + } + + public boolean getSnooperEnabled() { + return this.propertyManager.getBoolean("snooper-enabled", true); + } + + public void issueCommand(String s, ICommandListener icommandlistener) { + this.commandsQueue.add(new ServerCommand(s, icommandlistener)); + } + + public void aB() { + SpigotTimings.serverCommandTimer.startTiming(); // Spigot + + // Velt start - better server command queue + ServerCommand queuedCommand; + while ((queuedCommand = this.commandsQueue.poll()) != null) { + // CraftBukkit start - ServerCommand for preprocessing + ServerCommandEvent event = new ServerCommandEvent(this.console, queuedCommand.command); + this.server.getPluginManager().callEvent(event); + queuedCommand = new ServerCommand(event.getCommand(), queuedCommand.source); + + // this.getCommandHandler().a(servercommand.source, servercommand.command); // Called in dispatchServerCommand + this.server.dispatchServerCommand(this.console, queuedCommand); + // CraftBukkit end + } + // Velt end + + SpigotTimings.serverCommandTimer.stopTiming(); // Spigot + } + + public boolean X() { + return true; + } + + public DedicatedPlayerList aC() { + return (DedicatedPlayerList) super.getPlayerList(); + } + + public int a(String s, int i) { + return this.propertyManager.getInt(s, i); + } + + public String a(String s, String s1) { + return this.propertyManager.getString(s, s1); + } + + public boolean a(String s, boolean flag) { + return this.propertyManager.getBoolean(s, flag); + } + + public void a(String s, Object object) { + this.propertyManager.setProperty(s, object); + } + + public void a() { + this.propertyManager.savePropertiesFile(); + } + + public String b() { + File file1 = this.propertyManager.c(); + + return file1 != null ? file1.getAbsolutePath() : "No settings file"; + } + + public void aD() { + ServerGUI.a(this); + this.q = true; + } + + public boolean ak() { + return this.q; + } + + public String a(EnumGamemode enumgamemode, boolean flag) { + return ""; + } + + public boolean getEnableCommandBlock() { + return this.propertyManager.getBoolean("enable-command-block", false); + } + + public int getSpawnProtection() { + return this.propertyManager.getInt("spawn-protection", super.getSpawnProtection()); + } + + public boolean a(World world, int i, int j, int k, EntityHuman entityhuman) { + if (world.worldProvider.dimension != 0) { + return false; + } else if (this.aC().getOPs().isEmpty()) { + return false; + } else if (this.aC().isOp(entityhuman.getProfile())) { + return false; + } else if (this.getSpawnProtection() <= 0) { + return false; + } else { + ChunkCoordinates chunkcoordinates = world.getSpawn(); + int l = MathHelper.a(i - chunkcoordinates.x); + int i1 = MathHelper.a(k - chunkcoordinates.z); + int j1 = Math.max(l, i1); + + return j1 <= this.getSpawnProtection(); + } + } + + public int l() { + return this.propertyManager.getInt("op-permission-level", 4); + } + + public void setIdleTimeout(int i) { + super.setIdleTimeout(i); + this.propertyManager.setProperty("player-idle-timeout", Integer.valueOf(i)); + this.a(); + } + + public boolean m() { + return this.propertyManager.getBoolean("broadcast-rcon-to-ops", true); + } + + public boolean at() { + return this.propertyManager.getBoolean("announce-player-achievements", true); + } + + protected boolean aE() { + server.getLogger().info( "**** Beginning UUID conversion, this may take A LONG time ****"); // Spigot, let the user know whats up! + boolean flag = false; + + int i; + + for (i = 0; !flag && i <= 2; ++i) { + if (i > 0) { + // CraftBukkit - Fix decompiler stomping on field + DedicatedServer.i.warn("Encountered a problem while converting the user banlist, retrying in a few seconds"); + this.aG(); + } + + flag = NameReferencingFileConverter.a((MinecraftServer) this); + } + + boolean flag1 = false; + + for (i = 0; !flag1 && i <= 2; ++i) { + if (i > 0) { + // CraftBukkit - Fix decompiler stomping on field + DedicatedServer.i.warn("Encountered a problem while converting the ip banlist, retrying in a few seconds"); + this.aG(); + } + + flag1 = NameReferencingFileConverter.b((MinecraftServer) this); + } + + boolean flag2 = false; + + for (i = 0; !flag2 && i <= 2; ++i) { + if (i > 0) { + // CraftBukkit - Fix decompiler stomping on field + DedicatedServer.i.warn("Encountered a problem while converting the op list, retrying in a few seconds"); + this.aG(); + } + + flag2 = NameReferencingFileConverter.c((MinecraftServer) this); + } + + boolean flag3 = false; + + for (i = 0; !flag3 && i <= 2; ++i) { + if (i > 0) { + // CraftBukkit - Fix decompiler stomping on field + DedicatedServer.i.warn("Encountered a problem while converting the whitelist, retrying in a few seconds"); + this.aG(); + } + + flag3 = NameReferencingFileConverter.d((MinecraftServer) this); + } + + boolean flag4 = false; + + for (i = 0; !flag4 && i <= 2; ++i) { + if (i > 0) { + // CraftBukkit - Fix decompiler stomping on field + DedicatedServer.i.warn("Encountered a problem while converting the player save files, retrying in a few seconds"); + this.aG(); + } + + flag4 = NameReferencingFileConverter.a(this, this.propertyManager); + } + + return flag || flag1 || flag2 || flag3 || flag4; + } + + private void aG() { + try { + Thread.sleep(5000L); + } catch (InterruptedException interruptedexception) { + ; + } + } + + public PlayerList getPlayerList() { + return this.aC(); + } + + static Logger aF() { + return i; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorArmor.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorArmor.java new file mode 100644 index 0000000..9df12c0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorArmor.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorArmor extends DispenseBehaviorItem { + + DispenseBehaviorArmor() {} + + protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + int i = isourceblock.getBlockX() + enumfacing.getAdjacentX(); + int j = isourceblock.getBlockY() + enumfacing.getAdjacentY(); + int k = isourceblock.getBlockZ() + enumfacing.getAdjacentZ(); + AxisAlignedBB axisalignedbb = AxisAlignedBB.a((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1)); + List list = isourceblock.k().a(EntityLiving.class, axisalignedbb, (IEntitySelector) (new EntitySelectorEquipable(itemstack))); + + if (list.size() > 0) { + EntityLiving entityliving = (EntityLiving) list.get(0); + int l = entityliving instanceof EntityHuman ? 1 : 0; + int i1 = EntityInsentient.b(itemstack); + + // CraftBukkit start + ItemStack itemstack1 = itemstack.a(1); + World world = isourceblock.k(); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + itemstack1.count = 1; + entityliving.setEquipment(i1 - l, itemstack1); + if (entityliving instanceof EntityInsentient) { + ((EntityInsentient) entityliving).a(i1, 2.0F); + } + + // --itemstack.count; // CraftBukkit - handled above + return itemstack; + } else { + return super.b(isourceblock, itemstack); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorBoat.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorBoat.java new file mode 100644 index 0000000..5bd6ec7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorBoat.java @@ -0,0 +1,73 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorBoat extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); + + DispenseBehaviorBoat() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + World world = isourceblock.k(); + double d0 = isourceblock.getX() + (double) ((float) enumfacing.getAdjacentX() * 1.125F); + double d1 = isourceblock.getY() + (double) ((float) enumfacing.getAdjacentY() * 1.125F); + double d2 = isourceblock.getZ() + (double) ((float) enumfacing.getAdjacentZ() * 1.125F); + int i = isourceblock.getBlockX() + enumfacing.getAdjacentX(); + int j = isourceblock.getBlockY() + enumfacing.getAdjacentY(); + int k = isourceblock.getBlockZ() + enumfacing.getAdjacentZ(); + Material material = world.getType(i, j, k).getMaterial(); + double d3; + + if (Material.WATER.equals(material)) { + d3 = 1.0D; + } else { + if (!Material.AIR.equals(material) || !Material.WATER.equals(world.getType(i, j - 1, k).getMaterial())) { + return this.b.a(isourceblock, itemstack); + } + + d3 = 0.0D; + } + + // CraftBukkit start + ItemStack itemstack1 = itemstack.a(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + EntityBoat entityboat = new EntityBoat(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); + // CraftBukkit end + + world.addEntity(entityboat); + // itemstack.a(1); // CraftBukkit - handled during event processing + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.k().triggerEffect(1000, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorBonemeal.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorBonemeal.java new file mode 100644 index 0000000..c06e9a1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorBonemeal.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorBonemeal extends DispenseBehaviorItem { + + private boolean b = true; + + DispenseBehaviorBonemeal() {} + + protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + if (itemstack.getData() == 15) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + World world = isourceblock.k(); + int i = isourceblock.getBlockX() + enumfacing.getAdjacentX(); + int j = isourceblock.getBlockY() + enumfacing.getAdjacentY(); + int k = isourceblock.getBlockZ() + enumfacing.getAdjacentZ(); + + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + if (ItemDye.a(itemstack, world, i, j, k)) { + if (!world.isStatic) { + world.triggerEffect(2005, i, j, k, 0); + } + } else { + this.b = false; + } + + return itemstack; + } else { + return super.b(isourceblock, itemstack); + } + } + + protected void a(ISourceBlock isourceblock) { + if (this.b) { + isourceblock.k().triggerEffect(1000, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } else { + isourceblock.k().triggerEffect(1001, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorEmptyBucket.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorEmptyBucket.java new file mode 100644 index 0000000..d7decfc --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorEmptyBucket.java @@ -0,0 +1,68 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorEmptyBucket extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); + + DispenseBehaviorEmptyBucket() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + World world = isourceblock.k(); + int i = isourceblock.getBlockX() + enumfacing.getAdjacentX(); + int j = isourceblock.getBlockY() + enumfacing.getAdjacentY(); + int k = isourceblock.getBlockZ() + enumfacing.getAdjacentZ(); + Material material = world.getType(i, j, k).getMaterial(); + int l = world.getData(i, j, k); + Item item; + + if (Material.WATER.equals(material) && l == 0) { + item = Items.WATER_BUCKET; + } else { + if (!Material.LAVA.equals(material) || l != 0) { + return super.b(isourceblock, itemstack); + } + + item = Items.LAVA_BUCKET; + } + + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(i, j, k)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + world.setAir(i, j, k); + if (--itemstack.count == 0) { + itemstack.setItem(item); + itemstack.count = 1; + } else if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) { + this.b.a(isourceblock, new ItemStack(item)); + } + + return itemstack; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFilledBucket.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFilledBucket.java new file mode 100644 index 0000000..4a3691a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFilledBucket.java @@ -0,0 +1,69 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorFilledBucket extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); + + DispenseBehaviorFilledBucket() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + ItemBucket itembucket = (ItemBucket) itemstack.getItem(); + int i = isourceblock.getBlockX(); + int j = isourceblock.getBlockY(); + int k = isourceblock.getBlockZ(); + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + + // CraftBukkit start + World world = isourceblock.k(); + int x = i + enumfacing.getAdjacentX(); + int y = j + enumfacing.getAdjacentY(); + int z = k + enumfacing.getAdjacentZ(); + if (world.isEmpty(x, y, z) || !world.getType(x, y, z).getMaterial().isBuildable()) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + itembucket = (ItemBucket) CraftItemStack.asNMSCopy(event.getItem()).getItem(); + } + // CraftBukkit end + + if (itembucket.a(isourceblock.k(), i + enumfacing.getAdjacentX(), j + enumfacing.getAdjacentY(), k + enumfacing.getAdjacentZ())) { + // CraftBukkit start - Handle stacked buckets + Item item = Items.BUCKET; + if (--itemstack.count == 0) { + itemstack.setItem(Items.BUCKET); + itemstack.count = 1; + } else if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) { + this.b.a(isourceblock, new ItemStack(item)); + } + // CraftBukkit end + + return itemstack; + } else { + return this.b.a(isourceblock, itemstack); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFireball.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFireball.java new file mode 100644 index 0000000..8ba4085 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFireball.java @@ -0,0 +1,70 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorFireball extends DispenseBehaviorItem { + + DispenseBehaviorFireball() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + IPosition iposition = BlockDispenser.a(isourceblock); + double d0 = iposition.getX() + (double) ((float) enumfacing.getAdjacentX() * 0.3F); + double d1 = iposition.getY() + (double) ((float) enumfacing.getAdjacentX() * 0.3F); + double d2 = iposition.getZ() + (double) ((float) enumfacing.getAdjacentZ() * 0.3F); + World world = isourceblock.k(); + Random random = world.random; + double d3 = random.nextGaussian() * 0.05D + (double) enumfacing.getAdjacentX(); + double d4 = random.nextGaussian() * 0.05D + (double) enumfacing.getAdjacentY(); + double d5 = random.nextGaussian() * 0.05D + (double) enumfacing.getAdjacentZ(); + + // CraftBukkit start + ItemStack itemstack1 = itemstack.a(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + EntitySmallFireball entitysmallfireball = new EntitySmallFireball(world, d0, d1, d2, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); + entitysmallfireball.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity()); + + // Velt start + if (!world.addEntity(entitysmallfireball)) { + itemstack.count++; + return itemstack; + } + // Velt end + // itemstack.a(1); // Handled during event processing + // CraftBukkit end + + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.k().triggerEffect(1009, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFireworks.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFireworks.java new file mode 100644 index 0000000..09a0842 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFireworks.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorFireworks extends DispenseBehaviorItem { + + DispenseBehaviorFireworks() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + double d0 = isourceblock.getX() + (double) enumfacing.getAdjacentX(); + double d1 = (double) ((float) isourceblock.getBlockY() + 0.2F); + double d2 = isourceblock.getZ() + (double) enumfacing.getAdjacentZ(); + + // CraftBukkit start + World world = isourceblock.k(); + ItemStack itemstack1 = itemstack.a(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); + EntityFireworks entityfireworks = new EntityFireworks(isourceblock.k(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), itemstack1); + + isourceblock.k().addEntity(entityfireworks); + // itemstack.a(1); // Handled during event processing + // CraftBukkit end + + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.k().triggerEffect(1002, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFlintAndSteel.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFlintAndSteel.java new file mode 100644 index 0000000..c4bbd6b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorFlintAndSteel.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorFlintAndSteel extends DispenseBehaviorItem { + + private boolean b = true; + + DispenseBehaviorFlintAndSteel() {} + + protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + World world = isourceblock.k(); + int i = isourceblock.getBlockX() + enumfacing.getAdjacentX(); + int j = isourceblock.getBlockY() + enumfacing.getAdjacentY(); + int k = isourceblock.getBlockZ() + enumfacing.getAdjacentZ(); + + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + if (world.isEmpty(i, j, k)) { + // CraftBukkit start - Ignition by dispensing flint and steel + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, i, j, k, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()).isCancelled()) { + world.setTypeUpdate(i, j, k, Blocks.FIRE); + if (itemstack.isDamaged(1, world.random)) { + itemstack.count = 0; + } + } + // CraftBukkit end + } else if (world.getType(i, j, k) == Blocks.TNT) { + Blocks.TNT.postBreak(world, i, j, k, 1); + world.setAir(i, j, k); + } else { + this.b = false; + } + + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + if (this.b) { + isourceblock.k().triggerEffect(1000, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } else { + isourceblock.k().triggerEffect(1001, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorItem.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorItem.java new file mode 100644 index 0000000..868207c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorItem.java @@ -0,0 +1,99 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +public class DispenseBehaviorItem implements IDispenseBehavior { + + public DispenseBehaviorItem() {} + + public final ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + if (itemstack != null && itemstack.count < 0) itemstack.count = 0; // EMC + ItemStack itemstack1 = this.b(isourceblock, itemstack); + + this.a(isourceblock); + this.a(isourceblock, BlockDispenser.b(isourceblock.h())); + return itemstack1; + } + + protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + IPosition iposition = BlockDispenser.a(isourceblock); + ItemStack itemstack1 = itemstack.a(1); + + // CraftBukkit start + if (!a(isourceblock.k(), itemstack1, 6, enumfacing, isourceblock)) { + itemstack.count++; + } + // CraftBukkit end + + return itemstack; + } + + // CraftBukkit start - void -> boolean return, IPosition -> ISourceBlock last argument + public static boolean a(World world, ItemStack itemstack, int i, EnumFacing enumfacing, ISourceBlock isourceblock) { + IPosition iposition = BlockDispenser.a(isourceblock); + // CraftBukkit end + double d0 = iposition.getX(); + double d1 = iposition.getY(); + double d2 = iposition.getZ(); + EntityItem entityitem = new EntityItem(world, d0, d1 - 0.3D, d2, itemstack); + double d3 = world.random.nextDouble() * 0.1D + 0.2D; + + entityitem.motX = (double) enumfacing.getAdjacentX() * d3; + entityitem.motY = 0.20000000298023224D; + entityitem.motZ = (double) enumfacing.getAdjacentZ() * d3; + entityitem.motX += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + entityitem.motY += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + entityitem.motZ += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(entityitem.motX, entityitem.motY, entityitem.motZ)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return false; + } + + entityitem.setItemStack(CraftItemStack.asNMSCopy(event.getItem())); + entityitem.motX = event.getVelocity().getX(); + entityitem.motY = event.getVelocity().getY(); + entityitem.motZ = event.getVelocity().getZ(); + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior.getClass() != DispenseBehaviorItem.class) { + idispensebehavior.a(isourceblock, eventStack); + } else { + world.addEntity(entityitem); + } + return false; + } + + world.addEntity(entityitem); + + return true; + // CraftBukkit end + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.k().triggerEffect(1000, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } + + protected void a(ISourceBlock isourceblock, EnumFacing enumfacing) { + isourceblock.k().triggerEffect(2000, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), this.a(enumfacing)); + } + + private int a(EnumFacing enumfacing) { + return enumfacing.getAdjacentX() + 1 + (enumfacing.getAdjacentZ() + 1) * 3; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorMinecart.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorMinecart.java new file mode 100644 index 0000000..3df54aa --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorMinecart.java @@ -0,0 +1,78 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorMinecart extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); + + DispenseBehaviorMinecart() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + World world = isourceblock.k(); + double d0 = isourceblock.getX() + (double) ((float) enumfacing.getAdjacentX() * 1.125F); + double d1 = isourceblock.getY() + (double) ((float) enumfacing.getAdjacentY() * 1.125F); + double d2 = isourceblock.getZ() + (double) ((float) enumfacing.getAdjacentZ() * 1.125F); + int i = isourceblock.getBlockX() + enumfacing.getAdjacentX(); + int j = isourceblock.getBlockY() + enumfacing.getAdjacentY(); + int k = isourceblock.getBlockZ() + enumfacing.getAdjacentZ(); + Block block = world.getType(i, j, k); + double d3; + + if (BlockMinecartTrackAbstract.a(block)) { + d3 = 0.0D; + } else { + if (block.getMaterial() != Material.AIR || !BlockMinecartTrackAbstract.a(world.getType(i, j - 1, k))) { + return this.b.a(isourceblock, itemstack); + } + + d3 = -1.0D; + } + + // CraftBukkit start + ItemStack itemstack1 = itemstack.a(1); + org.bukkit.block.Block block2 = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); + EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), ((ItemMinecart) itemstack1.getItem()).a); + // CraftBukkit end + + if (itemstack.hasName()) { + entityminecartabstract.a(itemstack.getName()); + } + + world.addEntity(entityminecartabstract); + // itemstack.a(1); // CraftBukkit - handled during event processing + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.k().triggerEffect(1000, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorMonsterEgg.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorMonsterEgg.java new file mode 100644 index 0000000..c345410 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorMonsterEgg.java @@ -0,0 +1,57 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorMonsterEgg extends DispenseBehaviorItem { + + DispenseBehaviorMonsterEgg() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + double d0 = isourceblock.getX() + (double) enumfacing.getAdjacentX(); + double d1 = (double) ((float) isourceblock.getBlockY() + 0.2F); + double d2 = isourceblock.getZ() + (double) enumfacing.getAdjacentZ(); + + // CraftBukkit start + World world = isourceblock.k(); + ItemStack itemstack1 = itemstack.a(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); + + Entity entity = ItemMonsterEgg.spawnCreature(isourceblock.k(), itemstack.getData(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG); + + if (entity instanceof EntityLiving && itemstack.hasName()) { + ((EntityInsentient) entity).setCustomName(itemstack.getName()); + } + + // itemstack.a(1); // Handled during event processing + // CraftBukkit end + return itemstack; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java new file mode 100644 index 0000000..b906373 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java @@ -0,0 +1,66 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +public abstract class DispenseBehaviorProjectile extends DispenseBehaviorItem { + + public DispenseBehaviorProjectile() {} + + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.k(); + IPosition iposition = BlockDispenser.a(isourceblock); + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + IProjectile iprojectile = this.a(world, iposition); + + // CraftBukkit start + ItemStack itemstack1 = itemstack.a(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) enumfacing.getAdjacentX(), (double) ((float) enumfacing.getAdjacentY() + 0.1F), (double) enumfacing.getAdjacentZ())); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + iprojectile.shoot(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.b(), this.a()); + ((Entity) iprojectile).projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity()); + // CraftBukkit end + + world.addEntity((Entity) iprojectile); + // itemstack.a(1); // CraftBukkit - Handled during event processing + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.k().triggerEffect(1002, isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ(), 0); + } + + protected abstract IProjectile a(World world, IPosition iposition); + + protected float a() { + return 6.0F; + } + + protected float b() { + return 1.1F; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorTNT.java b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorTNT.java new file mode 100644 index 0000000..439e130 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/DispenseBehaviorTNT.java @@ -0,0 +1,55 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +final class DispenseBehaviorTNT extends DispenseBehaviorItem { + + DispenseBehaviorTNT() {} + + protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + World world = isourceblock.k(); + int i = isourceblock.getBlockX() + enumfacing.getAdjacentX(); + int j = isourceblock.getBlockY() + enumfacing.getAdjacentY(); + int k = isourceblock.getBlockZ() + enumfacing.getAdjacentZ(); + + // CraftBukkit start + ItemStack itemstack1 = itemstack.a(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockX(), isourceblock.getBlockY(), isourceblock.getBlockZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(i + 0.5, j + 0.5, k + 0.5)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.count++; + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.count++; + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.a.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { + idispensebehavior.a(isourceblock, eventStack); + return itemstack; + } + } + + // PaperSpigot start - Add FallingBlock and TNT source location API + org.bukkit.Location loc = new org.bukkit.Location(world.getWorld(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); + EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(loc, world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (EntityLiving) null); + // PaperSpigot end + // CraftBukkit end + + world.addEntity(entitytntprimed); + // --itemstack.count; // CraftBukkit - handled above + return itemstack; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Enchantment.java b/vspigot-server/src/main/java/net/minecraft/server/Enchantment.java new file mode 100644 index 0000000..4263827 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Enchantment.java @@ -0,0 +1,165 @@ +package net.minecraft.server; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.craftbukkit.enchantments.CraftEnchantment; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public abstract class Enchantment { + + private static final File CONFIG_FILE = new File("config/server", "enchantments.yml"); // MineHQ - Dedicated config directory + protected static YamlConfiguration config = YamlConfiguration.loadConfiguration(CONFIG_FILE); + + // CraftBukkit - update CraftEnchant.getName(i) if this changes + public static final Enchantment[] byId = new Enchantment[256]; + public static final Enchantment[] c; + public static final Enchantment PROTECTION_ENVIRONMENTAL = new EnchantmentProtection(0, 10, 0); + public static final Enchantment PROTECTION_FIRE = new EnchantmentProtection(1, 5, 1); + public static final Enchantment PROTECTION_FALL = new EnchantmentProtection(2, 5, 2); + public static final Enchantment PROTECTION_EXPLOSIONS = new EnchantmentProtection(3, 2, 3); + public static final Enchantment PROTECTION_PROJECTILE = new EnchantmentProtection(4, 5, 4); + public static final Enchantment OXYGEN = new EnchantmentOxygen(5, 2); + public static final Enchantment WATER_WORKER = new EnchantmentWaterWorker(6, 2); + public static final Enchantment THORNS = new EnchantmentThorns(7, 1); + public static final Enchantment DAMAGE_ALL = new EnchantmentWeaponDamage(16, 10, 0); + public static final Enchantment DAMAGE_UNDEAD = new EnchantmentWeaponDamage(17, 5, 1); + public static final Enchantment DAMAGE_ARTHROPODS = new EnchantmentWeaponDamage(18, 5, 2); + public static final Enchantment KNOCKBACK = new EnchantmentKnockback(19, 5); + public static final Enchantment FIRE_ASPECT = new EnchantmentFire(20, 2); + public static final Enchantment LOOT_BONUS_MOBS = new EnchantmentLootBonus(21, 2, EnchantmentSlotType.WEAPON); + public static final Enchantment DIG_SPEED = new EnchantmentDigging(32, 10); + public static final Enchantment SILK_TOUCH = new EnchantmentSilkTouch(33, 1); + public static final Enchantment DURABILITY = new EnchantmentDurability(34, 5); + public static final Enchantment LOOT_BONUS_BLOCKS = new EnchantmentLootBonus(35, 2, EnchantmentSlotType.DIGGER); + public static final Enchantment ARROW_DAMAGE = new EnchantmentArrowDamage(48, 10); + public static final Enchantment ARROW_KNOCKBACK = new EnchantmentArrowKnockback(49, 2); + public static final Enchantment ARROW_FIRE = new EnchantmentFlameArrows(50, 2); + public static final Enchantment ARROW_INFINITE = new EnchantmentInfiniteArrows(51, 1); + public static final Enchantment LUCK = new EnchantmentLootBonus(61, 2, EnchantmentSlotType.FISHING_ROD); + public static final Enchantment LURE = new EnchantmentLure(62, 2, EnchantmentSlotType.FISHING_ROD); + public final int id; + private final int weight; + public EnchantmentSlotType slot; + protected String name; + protected String configName; + private final int startLevel; + private final int maxLevel; + private List conflictingNames; + private boolean[] conflicts; + + protected Enchantment(int i, int j, EnchantmentSlotType enchantmentslottype) { + this.id = i; + this.slot = enchantmentslottype; + if (byId[i] != null) { + throw new IllegalArgumentException("Duplicate enchantment id!"); + } else { + byId[i] = this; + } + + CraftEnchantment craftEnch = new CraftEnchantment(this); + org.bukkit.enchantments.Enchantment.registerEnchantment(craftEnch); // CraftBukkit + this.configName = craftEnch.getName().toLowerCase().replace('_', '-'); + this.weight = config.getInt(configName + ".weight", j); + this.startLevel = config.getInt(configName + ".start-level", 1); + this.maxLevel = config.getInt(configName + ".max-level", getDefaultMaxLevel()); + this.conflictingNames = config.getStringList(configName + ".conflicting"); + } + + public int getRandomWeight() { + return this.weight; + } + + public int getStartLevel() { + return startLevel; + } + + public int getMaxLevel() { + return maxLevel; + } + + public int getDefaultMaxLevel() { + return 1; + } + + public int a(int i) { + return 1 + i * 10; + } + + public int b(int i) { + return this.a(i) + 5; + } + + public int a(int i, DamageSource damagesource) { + return 0; + } + + public float a(int i, EnumMonsterType enummonstertype) { + return 0.0F; + } + + public boolean a(Enchantment enchantment) { + if (enchantment == this) { + return false; + } + initCustomConflicts(); + enchantment.initCustomConflicts(); + return !conflicts[enchantment.id] && !enchantment.conflicts[id]; + } + + private void initCustomConflicts() { + if (this.conflicts == null) { + this.conflicts = new boolean[256]; + + for (String s : conflictingNames) { + for (int i = 0; i < 256; i++) { + if (byId[i] != null && byId[i].configName.equals(s)) { + this.conflicts[i] = true; + System.out.println(this.configName + " conflicts " + s); + break; + } + } + } + } + } + + public Enchantment b(String s) { + this.name = s; + return this; + } + + public String a() { + return "enchantment." + this.name; + } + + public String c(int i) { + String s = LocaleI18n.get(this.a()); + + return s + " " + LocaleI18n.get("enchantment.level." + i); + } + + public boolean canEnchant(ItemStack itemstack) { + return this.slot.canEnchant(itemstack.getItem()); + } + + public void a(EntityLiving entityliving, Entity entity, int i) {} + + public void b(EntityLiving entityliving, Entity entity, int i) {} + + static { + ArrayList arraylist = new ArrayList(); + Enchantment[] aenchantment = byId; + int i = aenchantment.length; + + for (int j = 0; j < i; ++j) { + Enchantment enchantment = aenchantment[j]; + + if (enchantment != null) { + arraylist.add(enchantment); + } + } + + c = (Enchantment[]) arraylist.toArray(new Enchantment[0]); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentArrowDamage.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentArrowDamage.java new file mode 100644 index 0000000..70187c1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentArrowDamage.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentArrowDamage extends Enchantment { + + public EnchantmentArrowDamage(int i, int j) { + super(i, j, EnchantmentSlotType.BOW); + this.b("arrowDamage"); + } + + public int a(int i) { + return 1 + (i - 1) * 10; + } + + public int b(int i) { + return this.a(i) + 15; + } + + public int getDefaultMaxLevel() { + return 5; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentArrowKnockback.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentArrowKnockback.java new file mode 100644 index 0000000..0f91041 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentArrowKnockback.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentArrowKnockback extends Enchantment { + + public EnchantmentArrowKnockback(int i, int j) { + super(i, j, EnchantmentSlotType.BOW); + this.b("arrowKnockback"); + } + + public int a(int i) { + return 12 + (i - 1) * 20; + } + + public int b(int i) { + return this.a(i) + 25; + } + + public int getDefaultMaxLevel() { + return 2; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentDigging.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentDigging.java new file mode 100644 index 0000000..5e9ab56 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentDigging.java @@ -0,0 +1,25 @@ +package net.minecraft.server; + +public class EnchantmentDigging extends Enchantment { + + protected EnchantmentDigging(int i, int j) { + super(i, j, EnchantmentSlotType.DIGGER); + this.b("digging"); + } + + public int a(int i) { + return 1 + 10 * (i - 1); + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getDefaultMaxLevel() { + return 5; + } + + public boolean canEnchant(ItemStack itemstack) { + return itemstack.getItem() == Items.SHEARS ? true : super.canEnchant(itemstack); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentDurability.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentDurability.java new file mode 100644 index 0000000..4a15bdb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentDurability.java @@ -0,0 +1,31 @@ +package net.minecraft.server; + +import java.util.Random; + +public class EnchantmentDurability extends Enchantment { + + protected EnchantmentDurability(int i, int j) { + super(i, j, EnchantmentSlotType.BREAKABLE); + this.b("durability"); + } + + public int a(int i) { + return 5 + (i - 1) * 8; + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getDefaultMaxLevel() { + return 3; + } + + public boolean canEnchant(ItemStack itemstack) { + return itemstack.g() ? true : super.canEnchant(itemstack); + } + + public static boolean a(ItemStack itemstack, int i, Random random) { + return itemstack.getItem() instanceof ItemArmor && random.nextFloat() < 0.6F ? false : random.nextInt(i + 1) > 0; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentFire.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentFire.java new file mode 100644 index 0000000..3d101a2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentFire.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentFire extends Enchantment { + + protected EnchantmentFire(int i, int j) { + super(i, j, EnchantmentSlotType.WEAPON); + this.b("fire"); + } + + public int a(int i) { + return 10 + 20 * (i - 1); + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getDefaultMaxLevel() { + return 2; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentFlameArrows.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentFlameArrows.java new file mode 100644 index 0000000..2c936e1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentFlameArrows.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentFlameArrows extends Enchantment { + + public EnchantmentFlameArrows(int i, int j) { + super(i, j, EnchantmentSlotType.BOW); + this.b("arrowFire"); + } + + public int a(int i) { + return 20; + } + + public int b(int i) { + return 50; + } + + public int getDefaultMaxLevel() { + return 1; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentGlow.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentGlow.java new file mode 100644 index 0000000..c876109 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentGlow.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.inventory.ItemStack; + +/** + * @author xanderume@gmail.com + */ +public class EnchantmentGlow extends org.bukkit.enchantments.Enchantment { + + public EnchantmentGlow(int id) { + super(id); + } + + @Override + public String getName() { + return ""; + } + + @Override + public int getMaxLevel() { + return 0; + } + + @Override + public int getStartLevel() { + return 0; + } + + @Override + public EnchantmentTarget getItemTarget() { + return null; + } + + @Override + public boolean conflictsWith(Enchantment other) { + return false; + } + + @Override + public boolean canEnchantItem(ItemStack item) { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentInfiniteArrows.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentInfiniteArrows.java new file mode 100644 index 0000000..525f09b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentInfiniteArrows.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentInfiniteArrows extends Enchantment { + + public EnchantmentInfiniteArrows(int i, int j) { + super(i, j, EnchantmentSlotType.BOW); + this.b("arrowInfinite"); + } + + public int a(int i) { + return 20; + } + + public int b(int i) { + return 50; + } + + public int getDefaultMaxLevel() { + return 1; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentKnockback.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentKnockback.java new file mode 100644 index 0000000..44a6841 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentKnockback.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentKnockback extends Enchantment { + + protected EnchantmentKnockback(int i, int j) { + super(i, j, EnchantmentSlotType.WEAPON); + this.b("knockback"); + } + + public int a(int i) { + return 5 + 20 * (i - 1); + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getDefaultMaxLevel() { + return 2; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentLootBonus.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentLootBonus.java new file mode 100644 index 0000000..8f0d287 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentLootBonus.java @@ -0,0 +1,31 @@ +package net.minecraft.server; + +public class EnchantmentLootBonus extends Enchantment { + + protected EnchantmentLootBonus(int i, int j, EnchantmentSlotType enchantmentslottype) { + super(i, j, enchantmentslottype); + if (enchantmentslottype == EnchantmentSlotType.DIGGER) { + this.b("lootBonusDigger"); + } else if (enchantmentslottype == EnchantmentSlotType.FISHING_ROD) { + this.b("lootBonusFishing"); + } else { + this.b("lootBonus"); + } + } + + public int a(int i) { + return 15 + (i - 1) * 9; + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getDefaultMaxLevel() { + return 3; + } + + public boolean a(Enchantment enchantment) { + return super.a(enchantment) && enchantment.id != SILK_TOUCH.id; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentManager.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentManager.java new file mode 100644 index 0000000..b8d2adf --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentManager.java @@ -0,0 +1,364 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class EnchantmentManager { + + private static final Random random = new Random(); + private static final EnchantmentModifierProtection b = new EnchantmentModifierProtection((EmptyClass) null); + private static final EnchantmentModifierDamage c = new EnchantmentModifierDamage((EmptyClass) null); + private static final EnchantmentModifierThorns d = new EnchantmentModifierThorns((EmptyClass) null); + private static final EnchantmentModifierArthropods e = new EnchantmentModifierArthropods((EmptyClass) null); + + public static int getEnchantmentLevel(int i, ItemStack itemstack) { + if (itemstack == null) { + return 0; + } else { + NBTTagList nbttaglist = itemstack.getEnchantments(); + + if (nbttaglist == null) { + return 0; + } else { + for (int j = 0; j < nbttaglist.size(); ++j) { + short short1 = nbttaglist.get(j).getShort("id"); + short short2 = nbttaglist.get(j).getShort("lvl"); + + if (short1 == i) { + return short2; + } + } + + return 0; + } + } + } + + public static Map a(ItemStack itemstack) { + LinkedHashMap linkedhashmap = new LinkedHashMap(); + NBTTagList nbttaglist = itemstack.getItem() == Items.ENCHANTED_BOOK ? Items.ENCHANTED_BOOK.g(itemstack) : itemstack.getEnchantments(); + + if (nbttaglist != null) { + for (int i = 0; i < nbttaglist.size(); ++i) { + short short1 = nbttaglist.get(i).getShort("id"); + short short2 = nbttaglist.get(i).getShort("lvl"); + + linkedhashmap.put(Integer.valueOf(short1), Integer.valueOf(short2)); + } + } + + return linkedhashmap; + } + + public static void a(Map map, ItemStack itemstack) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = map.keySet().iterator(); + + while (iterator.hasNext()) { + int i = ((Integer) iterator.next()).intValue(); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setShort("id", (short) i); + nbttagcompound.setShort("lvl", (short) ((Integer) map.get(Integer.valueOf(i))).intValue()); + nbttaglist.add(nbttagcompound); + if (itemstack.getItem() == Items.ENCHANTED_BOOK) { + Items.ENCHANTED_BOOK.a(itemstack, new EnchantmentInstance(i, ((Integer) map.get(Integer.valueOf(i))).intValue())); + } + } + + if (nbttaglist.size() > 0) { + if (itemstack.getItem() != Items.ENCHANTED_BOOK) { + itemstack.a("ench", (NBTBase) nbttaglist); + } + } else if (itemstack.hasTag()) { + itemstack.getTag().remove("ench"); + } + } + + public static int getEnchantmentLevel(int i, ItemStack[] aitemstack) { + if (aitemstack == null) { + return 0; + } else { + int j = 0; + ItemStack[] aitemstack1 = aitemstack; + int k = aitemstack.length; + + for (int l = 0; l < k; ++l) { + ItemStack itemstack = aitemstack1[l]; + int i1 = getEnchantmentLevel(i, itemstack); + + if (i1 > j) { + j = i1; + } + } + + return j; + } + } + + private static void a(EnchantmentModifier enchantmentmodifier, ItemStack itemstack) { + if (itemstack != null) { + NBTTagList nbttaglist = itemstack.getEnchantments(); + + if (nbttaglist != null) { + for (int i = 0; i < nbttaglist.size(); ++i) { + short short1 = nbttaglist.get(i).getShort("id"); + short short2 = nbttaglist.get(i).getShort("lvl"); + + if (Enchantment.byId[short1] != null) { + enchantmentmodifier.a(Enchantment.byId[short1], short2); + } + } + } + } + } + + private static void a(EnchantmentModifier enchantmentmodifier, ItemStack[] aitemstack) { + ItemStack[] aitemstack1 = aitemstack; + int i = aitemstack.length; + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = aitemstack1[j]; + + a(enchantmentmodifier, itemstack); + } + } + + public static int a(ItemStack[] aitemstack, DamageSource damagesource) { + b.a = 0; + b.b = damagesource; + a((EnchantmentModifier) b, aitemstack); + if (b.a > 25) { + b.a = 25; + } + + return b.a; + } + + public static float a(EntityLiving entityliving, EntityLiving entityliving1) { + return a(entityliving.be(), entityliving1.getMonsterType()); + } + + public static float a(ItemStack itemstack, EnumMonsterType enummonstertype) { + c.a = 0.0F; + c.b = enummonstertype; + a(c, itemstack); + return c.a; + } + + public static void a(EntityLiving entityliving, Entity entity) { + d.b = entity; + d.a = entityliving; + a(d, entityliving.getEquipment()); + if (entity instanceof EntityHuman) { + a(d, entityliving.be()); + } + d.b = null; + d.a = null; + } + + public static void b(EntityLiving entityliving, Entity entity) { + e.a = entityliving; + e.b = entity; + a((EnchantmentModifier) e, entityliving.getEquipment()); + if (entityliving instanceof EntityHuman) { + a((EnchantmentModifier) e, entityliving.be()); + } + e.a = null; + e.b = null; + } + + public static int getKnockbackEnchantmentLevel(EntityLiving entityliving, EntityLiving entityliving1) { + return getEnchantmentLevel(Enchantment.KNOCKBACK.id, entityliving.be()); + } + + public static int getFireAspectEnchantmentLevel(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.FIRE_ASPECT.id, entityliving.be()); + } + + public static int getOxygenEnchantmentLevel(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.OXYGEN.id, entityliving.getEquipment()); + } + + public static int getDigSpeedEnchantmentLevel(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.DIG_SPEED.id, entityliving.be()); + } + + public static boolean hasSilkTouchEnchantment(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.SILK_TOUCH.id, entityliving.be()) > 0; + } + + public static int getBonusBlockLootEnchantmentLevel(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS.id, entityliving.be()); + } + + public static int getLuckEnchantmentLevel(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.LUCK.id, entityliving.be()); + } + + public static int getLureEnchantmentLevel(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.LURE.id, entityliving.be()); + } + + public static int getBonusMonsterLootEnchantmentLevel(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.LOOT_BONUS_MOBS.id, entityliving.be()); + } + + public static boolean hasWaterWorkerEnchantment(EntityLiving entityliving) { + return getEnchantmentLevel(Enchantment.WATER_WORKER.id, entityliving.getEquipment()) > 0; + } + + public static ItemStack a(Enchantment enchantment, EntityLiving entityliving) { + ItemStack[] aitemstack = entityliving.getEquipment(); + int i = aitemstack.length; + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = aitemstack[j]; + + if (itemstack != null && getEnchantmentLevel(enchantment.id, itemstack) > 0) { + return itemstack; + } + } + + return null; + } + + public static int a(Random random, int i, int j, ItemStack itemstack) { + Item item = itemstack.getItem(); + int k = item.c(); + + if (k <= 0) { + return 0; + } else { + if (j > 15) { + j = 15; + } + + int l = random.nextInt(8) + 1 + (j >> 1) + random.nextInt(j + 1); + + return i == 0 ? Math.max(l / 3, 1) : (i == 1 ? l * 2 / 3 + 1 : Math.max(l, j * 2)); + } + } + + public static ItemStack a(Random random, ItemStack itemstack, int i) { + List list = b(random, itemstack, i); + boolean flag = itemstack.getItem() == Items.BOOK; + + if (flag) { + itemstack.setItem(Items.ENCHANTED_BOOK); + } + + if (list != null) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EnchantmentInstance enchantmentinstance = (EnchantmentInstance) iterator.next(); + + if (flag) { + Items.ENCHANTED_BOOK.a(itemstack, enchantmentinstance); + } else { + itemstack.addEnchantment(enchantmentinstance.enchantment, enchantmentinstance.level); + } + } + } + + return itemstack; + } + + public static List b(Random random, ItemStack itemstack, int i) { + Item item = itemstack.getItem(); + int j = item.c(); + + if (j <= 0) { + return null; + } else { + j /= 2; + j = 1 + random.nextInt((j >> 1) + 1) + random.nextInt((j >> 1) + 1); + int k = j + i; + float f = (random.nextFloat() + random.nextFloat() - 1.0F) * 0.15F; + int l = (int) ((float) k * (1.0F + f) + 0.5F); + + if (l < 1) { + l = 1; + } + + ArrayList arraylist = null; + Map map = b(l, itemstack); + + if (map != null && !map.isEmpty()) { + EnchantmentInstance enchantmentinstance = (EnchantmentInstance) WeightedRandom.a(random, map.values()); + + if (enchantmentinstance != null) { + arraylist = new ArrayList(); + arraylist.add(enchantmentinstance); + + for (int i1 = l; random.nextInt(50) <= i1; i1 >>= 1) { + Iterator iterator = map.keySet().iterator(); + + while (iterator.hasNext()) { + Integer integer = (Integer) iterator.next(); + boolean flag = true; + Iterator iterator1 = arraylist.iterator(); + + while (true) { + if (iterator1.hasNext()) { + EnchantmentInstance enchantmentinstance1 = (EnchantmentInstance) iterator1.next(); + + if (enchantmentinstance1.enchantment.a(Enchantment.byId[integer.intValue()])) { + continue; + } + + flag = false; + } + + if (!flag) { + iterator.remove(); + } + break; + } + } + + if (!map.isEmpty()) { + EnchantmentInstance enchantmentinstance2 = (EnchantmentInstance) WeightedRandom.a(random, map.values()); + + arraylist.add(enchantmentinstance2); + } + } + } + } + + return arraylist; + } + } + + public static Map b(int i, ItemStack itemstack) { + Item item = itemstack.getItem(); + HashMap hashmap = null; + boolean flag = itemstack.getItem() == Items.BOOK; + Enchantment[] aenchantment = Enchantment.byId; + int j = aenchantment.length; + + for (int k = 0; k < j; ++k) { + Enchantment enchantment = aenchantment[k]; + + if (enchantment != null && (enchantment.slot.canEnchant(item) || flag)) { + for (int l = enchantment.getStartLevel(); l <= enchantment.getMaxLevel(); ++l) { + if (i >= enchantment.a(l) && i <= enchantment.b(l)) { + if (hashmap == null) { + hashmap = new HashMap(); + } + + hashmap.put(Integer.valueOf(enchantment.id), new EnchantmentInstance(enchantment, l)); + } + } + } + } + + return hashmap; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierArthropods.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierArthropods.java new file mode 100644 index 0000000..c8e11db --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierArthropods.java @@ -0,0 +1,17 @@ +package net.minecraft.server; + +final class EnchantmentModifierArthropods implements EnchantmentModifier { + + public EntityLiving a; + public Entity b; + + private EnchantmentModifierArthropods() {} + + public void a(Enchantment enchantment, int i) { + enchantment.a(this.a, this.b, i); + } + + EnchantmentModifierArthropods(EmptyClass emptyclass) { + this(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierDamage.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierDamage.java new file mode 100644 index 0000000..8610c7a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierDamage.java @@ -0,0 +1,17 @@ +package net.minecraft.server; + +final class EnchantmentModifierDamage implements EnchantmentModifier { + + public float a; + public EnumMonsterType b; + + private EnchantmentModifierDamage() {} + + public void a(Enchantment enchantment, int i) { + this.a += enchantment.a(i, this.b); + } + + EnchantmentModifierDamage(EmptyClass emptyclass) { + this(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierProtection.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierProtection.java new file mode 100644 index 0000000..8c03320 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierProtection.java @@ -0,0 +1,17 @@ +package net.minecraft.server; + +final class EnchantmentModifierProtection implements EnchantmentModifier { + + public int a; + public DamageSource b; + + private EnchantmentModifierProtection() {} + + public void a(Enchantment enchantment, int i) { + this.a += enchantment.a(i, this.b); + } + + EnchantmentModifierProtection(EmptyClass emptyclass) { + this(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierThorns.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierThorns.java new file mode 100644 index 0000000..41993e0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentModifierThorns.java @@ -0,0 +1,17 @@ +package net.minecraft.server; + +final class EnchantmentModifierThorns implements EnchantmentModifier { + + public EntityLiving a; + public Entity b; + + private EnchantmentModifierThorns() {} + + public void a(Enchantment enchantment, int i) { + enchantment.b(this.a, this.b, i); + } + + EnchantmentModifierThorns(EmptyClass emptyclass) { + this(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentOxygen.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentOxygen.java new file mode 100644 index 0000000..3d52cd5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentOxygen.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentOxygen extends Enchantment { + + public EnchantmentOxygen(int i, int j) { + super(i, j, EnchantmentSlotType.ARMOR_HEAD); + this.b("oxygen"); + } + + public int a(int i) { + return 10 * i; + } + + public int b(int i) { + return this.a(i) + 30; + } + + public int getDefaultMaxLevel() { + return 3; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentProtection.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentProtection.java new file mode 100644 index 0000000..b5d4d5c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentProtection.java @@ -0,0 +1,76 @@ +package net.minecraft.server; + +public class EnchantmentProtection extends Enchantment { + + private static final String[] E = new String[] { "all", "fire", "fall", "explosion", "projectile"}; + private static final int[] F = new int[] { 1, 10, 5, 5, 3}; + private static final int[] G = new int[] { 11, 8, 6, 8, 6}; + private static final int[] H = new int[] { 20, 12, 10, 12, 15}; + public final int a; + + public EnchantmentProtection(int i, int j, int k) { + super(i, j, EnchantmentSlotType.ARMOR); + this.a = k; + if (k == 2) { + this.slot = EnchantmentSlotType.ARMOR_FEET; + } + } + + public int a(int i) { + return F[this.a] + (i - 1) * G[this.a]; + } + + public int b(int i) { + return this.a(i) + H[this.a]; + } + + public int getDefaultMaxLevel() { + return 4; + } + + public int a(int i, DamageSource damagesource) { + if (damagesource.ignoresInvulnerability()) { + return 0; + } else { + float f = (float) (6 + i * i) / 3.0F; + + return this.a == 0 ? MathHelper.d(f * 0.75F) : (this.a == 1 && damagesource.o() ? MathHelper.d(f * 1.25F) : (this.a == 2 && damagesource == DamageSource.FALL ? MathHelper.d(f * 2.5F) : (this.a == 3 && damagesource.isExplosion() ? MathHelper.d(f * 1.5F) : (this.a == 4 && damagesource.a() ? MathHelper.d(f * 1.5F) : 0)))); + } + } + + public String a() { + return "enchantment.protect." + E[this.a]; + } + + public boolean a(Enchantment enchantment) { + if (!super.a(enchantment)) { + return false; + } + if (enchantment instanceof EnchantmentProtection) { + EnchantmentProtection enchantmentprotection = (EnchantmentProtection) enchantment; + + return enchantmentprotection.a == this.a ? false : this.a == 2 || enchantmentprotection.a == 2; + } + return true; + } + + public static int a(Entity entity, int i) { + int j = EnchantmentManager.getEnchantmentLevel(Enchantment.PROTECTION_FIRE.id, entity.getEquipment()); + + if (j > 0) { + i -= MathHelper.d((float) i * (float) j * 0.15F); + } + + return i; + } + + public static double a(Entity entity, double d0) { + int i = EnchantmentManager.getEnchantmentLevel(Enchantment.PROTECTION_EXPLOSIONS.id, entity.getEquipment()); + + if (i > 0) { + d0 -= (double) MathHelper.floor(d0 * (double) ((float) i * 0.15F)); + } + + return d0; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentSilkTouch.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentSilkTouch.java new file mode 100644 index 0000000..99c3179 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentSilkTouch.java @@ -0,0 +1,29 @@ +package net.minecraft.server; + +public class EnchantmentSilkTouch extends Enchantment { + + protected EnchantmentSilkTouch(int i, int j) { + super(i, j, EnchantmentSlotType.DIGGER); + this.b("untouching"); + } + + public int a(int i) { + return 15; + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getDefaultMaxLevel() { + return 1; + } + + public boolean a(Enchantment enchantment) { + return super.a(enchantment) && enchantment.id != LOOT_BONUS_BLOCKS.id; + } + + public boolean canEnchant(ItemStack itemstack) { + return itemstack.getItem() == Items.SHEARS ? true : super.canEnchant(itemstack); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentThorns.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentThorns.java new file mode 100644 index 0000000..4a2c8f0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentThorns.java @@ -0,0 +1,50 @@ +package net.minecraft.server; + +import java.util.Random; + +public class EnchantmentThorns extends Enchantment { + + public EnchantmentThorns(int i, int j) { + super(i, j, EnchantmentSlotType.ARMOR_TORSO); + this.b("thorns"); + } + + public int a(int i) { + return 10 + 20 * (i - 1); + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getDefaultMaxLevel() { + return 3; + } + + public boolean canEnchant(ItemStack itemstack) { + return itemstack.getItem() instanceof ItemArmor ? true : super.canEnchant(itemstack); + } + + public void b(EntityLiving entityliving, Entity entity, int i) { + Random random = entityliving.aI(); + ItemStack itemstack = EnchantmentManager.a(Enchantment.THORNS, entityliving); + + if (a(i, random)) { + entity.damageEntity(DamageSource.a(entityliving), (float) b(i, random)); + entity.makeSound("damage.thorns", 0.5F, 1.0F); + if (itemstack != null) { + itemstack.damage(3, entityliving); + } + } else if (itemstack != null) { + itemstack.damage(1, entityliving); + } + } + + public static boolean a(int i, Random random) { + return i <= 0 ? false : random.nextFloat() < 0.15F * (float) i; + } + + public static int b(int i, Random random) { + return i > 10 ? i - 10 : 1 + random.nextInt(4); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentWaterWorker.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentWaterWorker.java new file mode 100644 index 0000000..9064fd0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentWaterWorker.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class EnchantmentWaterWorker extends Enchantment { + + public EnchantmentWaterWorker(int i, int j) { + super(i, j, EnchantmentSlotType.ARMOR_HEAD); + this.b("waterWorker"); + } + + public int a(int i) { + return 1; + } + + public int b(int i) { + return this.a(i) + 40; + } + + public int getDefaultMaxLevel() { + return 1; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/EnchantmentWeaponDamage.java b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentWeaponDamage.java new file mode 100644 index 0000000..72ab515 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EnchantmentWeaponDamage.java @@ -0,0 +1,55 @@ +package net.minecraft.server; + +public class EnchantmentWeaponDamage extends Enchantment { + + private static final String[] E = new String[] { "all", "undead", "arthropods"}; + private static final int[] F = new int[] { 1, 5, 5}; + private static final int[] G = new int[] { 11, 8, 8}; + private static final int[] H = new int[] { 20, 20, 20}; + public final int a; + + public EnchantmentWeaponDamage(int i, int j, int k) { + super(i, j, EnchantmentSlotType.WEAPON); + this.a = k; + } + + public int a(int i) { + return F[this.a] + (i - 1) * G[this.a]; + } + + public int b(int i) { + return this.a(i) + H[this.a]; + } + + public int getDefaultMaxLevel() { + return 5; + } + + public float a(int i, EnumMonsterType enummonstertype) { + return this.a == 0 ? (float) i * 1.25F : (this.a == 1 && enummonstertype == EnumMonsterType.UNDEAD ? (float) i * 2.5F : (this.a == 2 && enummonstertype == EnumMonsterType.ARTHROPOD ? (float) i * 2.5F : 0.0F)); + } + + public String a() { + return "enchantment.damage." + E[this.a]; + } + + public boolean a(Enchantment enchantment) { + return super.a(enchantment) && !(enchantment instanceof EnchantmentWeaponDamage); + } + + public boolean canEnchant(ItemStack itemstack) { + return itemstack.getItem() instanceof ItemAxe ? true : super.canEnchant(itemstack); + } + + public void a(EntityLiving entityliving, Entity entity, int i) { + if (entity instanceof EntityLiving) { + EntityLiving entityliving1 = (EntityLiving) entity; + + if (this.a == 2 && entityliving1.getMonsterType() == EnumMonsterType.ARTHROPOD) { + int j = 20 + entityliving.aI().nextInt(10 * i); + + entityliving1.addEffect(new MobEffect(MobEffectList.SLOWER_MOVEMENT.id, j, 3)); + } + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/Entity.java b/vspigot-server/src/main/java/net/minecraft/server/Entity.java new file mode 100644 index 0000000..592ae79 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Entity.java @@ -0,0 +1,2000 @@ +package net.minecraft.server; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.TravelAgent; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.SpigotTimings; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Painting; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.painting.PaintingBreakByEntityEvent; +import org.bukkit.event.vehicle.VehicleBlockCollisionEvent; +import org.bukkit.event.vehicle.VehicleEnterEvent; +import org.bukkit.event.vehicle.VehicleExitEvent; +import org.bukkit.plugin.PluginManager; +import org.spigotmc.CustomTimingsHandler; + +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.Callable; + +// CraftBukkit start +// CraftBukkit end +// Poweruser start +// Poweruser end + +public abstract class Entity { + + // CraftBukkit start + private static final int CURRENT_LEVEL = 2; + public int inWebTick; // Guardian + public int inPistonTick; // Guardian + static boolean isLevelAtLeast(NBTTagCompound tag, int level) { + return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; + } + // PaperSpigot start + public void retrack() { + final EntityTracker entityTracker = ((WorldServer) world).getTracker(); + entityTracker.untrackEntity(this); + entityTracker.track(this); + } + // PaperSpigot end + // CraftBukkit end + + private static int entityCount; + private int id; + public double j; + public boolean k; + public Entity passenger; + public Entity vehicle; + public boolean attachedToPlayer; + public World world; + public double lastX; + public double lastY; + public double lastZ; + public double locX; + public double locY; + public double locZ; + public double motX; + public double motY; + public double motZ; + public float yaw; + public float pitch; + public float lastYaw; + public float lastPitch; + public final AxisAlignedBB boundingBox; + public boolean onGround; + public boolean positionChanged; + public boolean F; + public boolean G; + public boolean velocityChanged; + protected boolean I; + public boolean J; + public boolean dead; + public float height; + public float width; + public float length; + public float O; + public float P; + public float Q; + public float fallDistance; + private int d; + public double S; + public double T; + public double U; + public float V; + public float W; + public boolean X; + public float Y; + public float Z; + protected Random random; + public int ticksLived; + public int maxFireTicks; + public int fireTicks; // CraftBukkit - private -> public + public boolean inWater; // Spigot - protected -> public + public int noDamageTicks; + private boolean justCreated; + protected boolean fireProof; + protected DataWatcher datawatcher; + private double g; + private double h; + public boolean ag; public boolean isAddedToChunk() { return ag; } // PaperSpigot - EAR backport + public int ah; + public int ai; + public int aj; + public boolean ak; + public boolean al; + public int portalCooldown; + protected boolean an; + protected int ao; + public int dimension; + protected int aq; + private boolean invulnerable; + public UUID uniqueID; // CraftBukkit - protected -> public + public EnumEntitySize as; + public boolean valid; // CraftBukkit + public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only + public boolean inUnloadedChunk = false; // PaperSpigot - Remove entities in unloaded chunks + public boolean loadChunks = false; // PaperSpigot - Entities can load chunks they move through and keep them loaded + + // Spigot start + public CustomTimingsHandler tickTimer = org.bukkit.craftbukkit.SpigotTimings.getEntityTimings(this); // Spigot + public final byte activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this); + public final boolean defaultActivationState; + public long activatedTick = MinecraftServer.currentTick + 20; // Kohi - activate for 20 ticks on first adding to the world + public boolean fromMobSpawner; + public void inactiveTick() { } + // Spigot end + + // Poweruser start + private boolean isInLava; + private int lastLavaCheck; + // Poweruser end + + public int getId() { + return this.id; + } + + public void d(int i) { + this.id = i; + } + + public Entity(World world) { + this.id = entityCount++; + this.j = 1.0D; + this.boundingBox = AxisAlignedBB.a(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D); + this.J = true; + this.width = 0.6F; + this.length = 1.8F; + this.d = 1; + this.random = new Random(); + this.maxFireTicks = 1; + this.justCreated = true; + this.uniqueID = new UUID(random.nextLong(), random.nextLong()); // Spigot + this.as = EnumEntitySize.SIZE_2; + this.world = world; + this.setPosition(0.0D, 0.0D, 0.0D); + if (world != null) { + this.dimension = world.worldProvider.dimension; + // Spigot start + this.defaultActivationState = org.spigotmc.ActivationRange.initializeEntityActivationState(this, world.spigotConfig); + } else { + this.defaultActivationState = false; + } + // Spigot end + + this.datawatcher = new DataWatcher(this); + this.datawatcher.a(0, Byte.valueOf((byte) 0)); + this.datawatcher.a(1, Short.valueOf((short) 300)); + this.c(); + } + + protected abstract void c(); + + public DataWatcher getDataWatcher() { + return this.datawatcher; + } + + public boolean equals(Object object) { + return object instanceof Entity ? ((Entity) object).id == this.id : false; + } + + public int hashCode() { + return this.id; + } + + public void die() { + this.dead = true; + } + + protected void a(float f, float f1) { + float f2; + + if (f != this.width || f1 != this.length) { + f2 = this.width; + this.width = f; + this.length = f1; + this.boundingBox.d = this.boundingBox.a + (double) this.width; + this.boundingBox.f = this.boundingBox.c + (double) this.width; + this.boundingBox.e = this.boundingBox.b + (double) this.length; + if (this.width > f2 && !this.justCreated && !this.world.isStatic) { + this.move((double) (f2 - this.width), 0.0D, (double) (f2 - this.width)); + } + } + + f2 = f % 2.0F; + if ((double) f2 < 0.375D) { + this.as = EnumEntitySize.SIZE_1; + } else if ((double) f2 < 0.75D) { + this.as = EnumEntitySize.SIZE_2; + } else if ((double) f2 < 1.0D) { + this.as = EnumEntitySize.SIZE_3; + } else if ((double) f2 < 1.375D) { + this.as = EnumEntitySize.SIZE_4; + } else if ((double) f2 < 1.75D) { + this.as = EnumEntitySize.SIZE_5; + } else { + this.as = EnumEntitySize.SIZE_6; + } + } + + protected void b(float f, float f1) { + // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0 + if (Float.isNaN(f)) { + f = 0; + } + + if ((f == Float.POSITIVE_INFINITY) || (f == Float.NEGATIVE_INFINITY)) { + if (this instanceof EntityPlayer) { + this.world.getServer().getLogger().warning(((CraftPlayer) this.getBukkitEntity()).getName() + " was caught trying to crash the server with an invalid yaw"); + ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite yaw (Hacking?)"); //Spigot "Nope" -> Descriptive reason + } + f = 0; + } + + // pitch was sometimes set to NaN, so we need to set it back to 0. + if (Float.isNaN(f1)) { + f1 = 0; + } + + if ((f1 == Float.POSITIVE_INFINITY) || (f1 == Float.NEGATIVE_INFINITY)) { + if (this instanceof EntityPlayer) { + this.world.getServer().getLogger().warning(((CraftPlayer) this.getBukkitEntity()).getName() + " was caught trying to crash the server with an invalid pitch"); + ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite pitch (Hacking?)"); //Spigot "Nope" -> Descriptive reason + } + f1 = 0; + } + // CraftBukkit end + + this.yaw = f % 360.0F; + this.pitch = f1 % 360.0F; + } + + public void setPosition(double d0, double d1, double d2) { + this.locX = d0; + this.locY = d1; + this.locZ = d2; + float f = this.width / 2.0F; + float f1 = this.length; + + this.boundingBox.b(d0 - (double) f, d1 - (double) this.height + (double) this.V, d2 - (double) f, d0 + (double) f, d1 - (double) this.height + (double) this.V + (double) f1, d2 + (double) f); + } + + public void h() { + this.C(); + } + + public void C() { + SpigotTimings.timerEntity_C.startTiming(); // Poweruser + this.world.methodProfiler.a("entityBaseTick"); + if (this.vehicle != null && this.vehicle.dead) { + this.vehicle = null; + } + + this.O = this.P; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.lastPitch = this.pitch; + this.lastYaw = this.yaw; + int i; + + if (!this.world.isStatic && this.world instanceof WorldServer) { + this.world.methodProfiler.a("portal"); + MinecraftServer minecraftserver = ((WorldServer) this.world).getMinecraftServer(); + + i = this.D(); + if (this.an) { + if (true || minecraftserver.getAllowNether()) { // CraftBukkit + if (this.vehicle == null && this.ao++ >= i) { + this.ao = i; + this.portalCooldown = this.ai(); + byte b0; + + if (this.world.worldProvider.dimension == -1) { + b0 = 0; + } else { + b0 = -1; + } + + SpigotTimings.timerEntity_C_portal.startTiming(); // Poweruser + this.b(b0); + SpigotTimings.timerEntity_C_portal.stopTiming(); // Poweruser + } + + this.an = false; + } + } else { + if (this.ao > 0) { + this.ao -= 4; + } + + if (this.ao < 0) { + this.ao = 0; + } + } + + if (this.portalCooldown > 0) { + --this.portalCooldown; + } + + this.world.methodProfiler.b(); + } + + if (this.isSprinting() && !this.M()) { + int j = MathHelper.floor(this.locX); + + i = MathHelper.floor(this.locY - 0.20000000298023224D - (double) this.height); + int k = MathHelper.floor(this.locZ); + Block block = this.world.getType(j, i, k); + + if (block.getMaterial() != Material.AIR) { + this.world.addParticle("blockcrack_" + Block.getId(block) + "_" + this.world.getData(j, i, k), this.locX + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, this.boundingBox.b + 0.1D, this.locZ + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, -this.motX * 4.0D, 1.5D, -this.motZ * 4.0D); + } + } + + this.N(); + if (this.world.isStatic) { + this.fireTicks = 0; + } else if (this.fireTicks > 0) { + if (this.fireProof) { + this.fireTicks -= 4; + if (this.fireTicks < 0) { + this.fireTicks = 0; + } + } else { + if (this.fireTicks % 20 == 0) { + this.damageEntity(DamageSource.BURN, 1.0F); + } + + --this.fireTicks; + } + } + + if (this.P()) { + this.E(); + this.fallDistance *= 0.5F; + } + + if (this.locY < -64.0D) { + this.G(); + } + + if (!this.world.isStatic) { + this.a(0, this.fireTicks > 0); + } + + this.justCreated = false; + this.world.methodProfiler.b(); + SpigotTimings.timerEntity_C.stopTiming(); // Poweruser + } + + public int D() { + return 0; + } + + protected void E() { + if (!this.fireProof) { + this.damageEntity(DamageSource.LAVA, 4); + + // CraftBukkit start - Fallen in lava TODO: this event spams! + if (this instanceof EntityLiving) { + if (this.fireTicks <= 0) { + // not on fire yet + // TODO: shouldn't be sending null for the block. + org.bukkit.block.Block damager = null; // ((WorldServer) this.l).getWorld().getBlockAt(i, j, k); + org.bukkit.entity.Entity damagee = this.getBukkitEntity(); + EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15); + this.world.getServer().getPluginManager().callEvent(combustEvent); + + if (!combustEvent.isCancelled()) { + this.setOnFire(combustEvent.getDuration()); + } + } else { + // This will be called every single tick the entity is in lava, so don't throw an event + this.setOnFire(15); + } + return; + } + // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls + + this.setOnFire(15); + } + } + + public void setOnFire(int i) { + int j = i * 20; + + j = EnchantmentProtection.a(this, j); + if (this.fireTicks < j) { + this.fireTicks = j; + } + } + + public void extinguish() { + this.fireTicks = 0; + } + + protected void G() { + this.die(); + } + + public boolean c(double d0, double d1, double d2) { + AxisAlignedBB axisalignedbb = this.boundingBox.c(d0, d1, d2); + List list = this.world.getCubes(this, axisalignedbb); + + return !list.isEmpty() ? false : !this.world.containsLiquid(axisalignedbb); + } + + /** + * PaperSpigot - Load surrounding chunks the entity is moving through + */ + public void loadChunks() { + int xstart = MathHelper.floor(this.locX); + int zstart = MathHelper.floor(this.locZ); + int xend = MathHelper.floor(this.locX + this.motX); + int zend = MathHelper.floor(this.locZ + this.motZ); + + int xmin = Math.min(xstart, xend) - 3; + int xmax = Math.max(xstart, xend) + 3; + int zmin = Math.min(zstart, zend) - 3; + int zmax = Math.max(zstart, zend) + 3; + for (int cx = xmin >> 4; cx <= xmax >> 4; ++cx) { + for (int cz = zmin >> 4; cz <= zmax >> 4; ++cz) { + world.chunkProvider.getChunkAt(cx, cz); + } + } + } + + public void move(double d0, double d1, double d2) { + if (this.loadChunks) loadChunks(); // PaperSpigot - Load chunks + // CraftBukkit start - Don't do anything if we aren't moving + // We need to do this regardless of whether or not we are moving thanks to portals + try { + this.I(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Checking entity block collision"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being checked for collision"); + + this.a(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + // Check if we're moving + if (d0 == 0 && d1 == 0 && d2 == 0 && this.vehicle == null && this.passenger == null) { + return; + } + // CraftBukkit end + org.bukkit.craftbukkit.SpigotTimings.entityMoveTimer.startTiming(); // Spigot + if (this.X) { + this.boundingBox.d(d0, d1, d2); + this.locX = (this.boundingBox.a + this.boundingBox.d) / 2.0D; + this.locY = this.boundingBox.b + (double) this.height - (double) this.V; + this.locZ = (this.boundingBox.c + this.boundingBox.f) / 2.0D; + } else { + this.world.methodProfiler.a("move"); + this.V *= 0.4F; + double d3 = this.locX; + double d4 = this.locY; + double d5 = this.locZ; + + // if in web, make the entity's motion slower + if (this.I) { + this.I = false; + d0 *= 0.25D; + d1 *= 0.05000000074505806D; + d2 *= 0.25D; + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + + double d6 = d0; + double d7 = d1; + double d8 = d2; + AxisAlignedBB axisalignedbb = this.boundingBox.clone(); + boolean flag = this.onGround && this.isSneaking() && this instanceof EntityHuman; + + if (flag) { + double d9; + + for (d9 = 0.05D; d0 != 0.0D && this.world.getCubes(this, this.boundingBox.c(d0, -1.0D, 0.0D)).isEmpty(); d6 = d0) { + if (d0 < d9 && d0 >= -d9) { + d0 = 0.0D; + } else if (d0 > 0.0D) { + d0 -= d9; + } else { + d0 += d9; + } + } + + for (; d2 != 0.0D && this.world.getCubes(this, this.boundingBox.c(0.0D, -1.0D, d2)).isEmpty(); d8 = d2) { + if (d2 < d9 && d2 >= -d9) { + d2 = 0.0D; + } else if (d2 > 0.0D) { + d2 -= d9; + } else { + d2 += d9; + } + } + + while (d0 != 0.0D && d2 != 0.0D && this.world.getCubes(this, this.boundingBox.c(d0, -1.0D, d2)).isEmpty()) { + if (d0 < d9 && d0 >= -d9) { + d0 = 0.0D; + } else if (d0 > 0.0D) { + d0 -= d9; + } else { + d0 += d9; + } + + if (d2 < d9 && d2 >= -d9) { + d2 = 0.0D; + } else if (d2 > 0.0D) { + d2 -= d9; + } else { + d2 += d9; + } + + d6 = d0; + d8 = d2; + } + } + + List list = this.world.getCubes(this, this.boundingBox.a(d0, d1, d2)); + + for (int i = 0; i < list.size(); ++i) { + d1 = ((AxisAlignedBB) list.get(i)).b(this.boundingBox, d1); + } + + this.boundingBox.d(0.0D, d1, 0.0D); + if (!this.J && d7 != d1) { + d2 = 0.0D; + d1 = 0.0D; + d0 = 0.0D; + } + + boolean flag1 = this.onGround || d7 != d1 && d7 < 0.0D; + + int j; + + for (j = 0; j < list.size(); ++j) { + d0 = ((AxisAlignedBB) list.get(j)).a(this.boundingBox, d0); + } + + this.boundingBox.d(d0, 0.0D, 0.0D); + if (!this.J && d6 != d0) { + d2 = 0.0D; + d1 = 0.0D; + d0 = 0.0D; + } + + for (j = 0; j < list.size(); ++j) { + d2 = ((AxisAlignedBB) list.get(j)).c(this.boundingBox, d2); + } + + this.boundingBox.d(0.0D, 0.0D, d2); + if (!this.J && d8 != d2) { + d2 = 0.0D; + d1 = 0.0D; + d0 = 0.0D; + } + + double d10; + double d11; + double d12; + int k; + + if (this.W > 0.0F && flag1 && (flag || this.V < 0.05F) && (d6 != d0 || d8 != d2)) { + d10 = d0; + d11 = d1; + d12 = d2; + d0 = d6; + d1 = (double) this.W; + d2 = d8; + AxisAlignedBB axisalignedbb1 = this.boundingBox.clone(); + + this.boundingBox.d(axisalignedbb); + list = this.world.getCubes(this, this.boundingBox.a(d6, d1, d8)); + + for (k = 0; k < list.size(); ++k) { + d1 = ((AxisAlignedBB) list.get(k)).b(this.boundingBox, d1); + } + + this.boundingBox.d(0.0D, d1, 0.0D); + if (!this.J && d7 != d1) { + d2 = 0.0D; + d1 = 0.0D; + d0 = 0.0D; + } + + for (k = 0; k < list.size(); ++k) { + d0 = ((AxisAlignedBB) list.get(k)).a(this.boundingBox, d0); + } + + this.boundingBox.d(d0, 0.0D, 0.0D); + if (!this.J && d6 != d0) { + d2 = 0.0D; + d1 = 0.0D; + d0 = 0.0D; + } + + for (k = 0; k < list.size(); ++k) { + d2 = ((AxisAlignedBB) list.get(k)).c(this.boundingBox, d2); + } + + this.boundingBox.d(0.0D, 0.0D, d2); + if (!this.J && d8 != d2) { + d2 = 0.0D; + d1 = 0.0D; + d0 = 0.0D; + } + + if (!this.J && d7 != d1) { + d2 = 0.0D; + d1 = 0.0D; + d0 = 0.0D; + } else { + d1 = (double) (-this.W); + + for (k = 0; k < list.size(); ++k) { + d1 = ((AxisAlignedBB) list.get(k)).b(this.boundingBox, d1); + } + + this.boundingBox.d(0.0D, d1, 0.0D); + } + + if (d10 * d10 + d12 * d12 >= d0 * d0 + d2 * d2) { + d0 = d10; + d1 = d11; + d2 = d12; + this.boundingBox.d(axisalignedbb1); + } + } + + this.world.methodProfiler.b(); + this.world.methodProfiler.a("rest"); + this.locX = (this.boundingBox.a + this.boundingBox.d) / 2.0D; + this.locY = this.boundingBox.b + (double) this.height - (double) this.V; + this.locZ = (this.boundingBox.c + this.boundingBox.f) / 2.0D; + this.positionChanged = d6 != d0 || d8 != d2; + this.F = d7 != d1; + this.onGround = d7 != d1 && d7 < 0.0D; + this.G = this.positionChanged || this.F; + this.a(d1, this.onGround); + if (d6 != d0) { + this.motX = 0.0D; + } + + if (d7 != d1) { + this.motY = 0.0D; + } + + if (d8 != d2) { + this.motZ = 0.0D; + } + + d10 = this.locX - d3; + d11 = this.locY - d4; + d12 = this.locZ - d5; + + // CraftBukkit start + if ((this.positionChanged) && (this.getBukkitEntity() instanceof Vehicle)) { + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + org.bukkit.block.Block block = this.world.getWorld().getBlockAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY - (double) this.height), MathHelper.floor(this.locZ)); + + if (d6 > d0) { + block = block.getRelative(BlockFace.EAST); + } else if (d6 < d0) { + block = block.getRelative(BlockFace.WEST); + } else if (d8 > d2) { + block = block.getRelative(BlockFace.SOUTH); + } else if (d8 < d2) { + block = block.getRelative(BlockFace.NORTH); + } + + VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, block); + this.world.getServer().getPluginManager().callEvent(event); + } + // CraftBukkit end + + if (this.g_() && !flag && this.vehicle == null) { + int l = MathHelper.floor(this.locX); + + k = MathHelper.floor(this.locY - 0.20000000298023224D - (double) this.height); + int i1 = MathHelper.floor(this.locZ); + Block block = this.world.getType(l, k, i1); + int j1 = this.world.getType(l, k - 1, i1).b(); + + if (j1 == 11 || j1 == 32 || j1 == 21) { + block = this.world.getType(l, k - 1, i1); + } + + if (block != Blocks.LADDER) { + d11 = 0.0D; + } + + this.P = (float) ((double) this.P + (double) MathHelper.sqrt(d10 * d10 + d12 * d12) * 0.6D); + this.Q = (float) ((double) this.Q + (double) MathHelper.sqrt(d10 * d10 + d11 * d11 + d12 * d12) * 0.6D); + if (this.Q > (float) this.d && block.getMaterial() != Material.AIR) { + this.d = (int) this.Q + 1; + if (this.M()) { + float f = MathHelper.sqrt(this.motX * this.motX * 0.20000000298023224D + this.motY * this.motY + this.motZ * this.motZ * 0.20000000298023224D) * 0.35F; + + if (f > 1.0F) { + f = 1.0F; + } + + this.makeSound(this.H(), f, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + } + + this.a(l, k, i1, block); + block.b(this.world, l, k, i1, this); + } + } + + // CraftBukkit start - Move to the top of the method + /* + try { + this.I(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Checking entity block collision"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being checked for collision"); + + this.a(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + */ + // CraftBukkit end + boolean flag2 = this.L(); + + if (this.world.e(this.boundingBox.shrink(0.001D, 0.001D, 0.001D))) { + this.burn(1); + if (!flag2) { + ++this.fireTicks; + // CraftBukkit start - Not on fire yet + if (this.fireTicks <= 0) { // Only throw events on the first combust, otherwise it spams + EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setOnFire(event.getDuration()); + } + } else { + // CraftBukkit end + this.setOnFire(8); + } + } + } else if (this.fireTicks <= 0) { + this.fireTicks = -this.maxFireTicks; + } + + if (flag2 && this.fireTicks > 0) { + this.makeSound("random.fizz", 0.7F, 1.6F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + this.fireTicks = -this.maxFireTicks; + } + + this.world.methodProfiler.b(); + } + org.bukkit.craftbukkit.SpigotTimings.entityMoveTimer.stopTiming(); // Spigot + } + + protected String H() { + return "game.neutral.swim"; + } + + protected void I() { + int i = MathHelper.floor(this.boundingBox.a + 0.001D); + int j = MathHelper.floor(this.boundingBox.b + 0.001D); + int k = MathHelper.floor(this.boundingBox.c + 0.001D); + int l = MathHelper.floor(this.boundingBox.d - 0.001D); + int i1 = MathHelper.floor(this.boundingBox.e - 0.001D); + int j1 = MathHelper.floor(this.boundingBox.f - 0.001D); + + if (this.world.b(i, j, k, l, i1, j1)) { + for (int k1 = i; k1 <= l; ++k1) { + for (int l1 = j; l1 <= i1; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + Block block = this.world.getType(k1, l1, i2); + + try { + block.a(this.world, k1, l1, i2, this); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Colliding entity with block"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being collided with"); + + CrashReportSystemDetails.a(crashreportsystemdetails, k1, l1, i2, block, this.world.getData(k1, l1, i2)); + throw new ReportedException(crashreport); + } + } + } + } + } + } + + protected void a(int i, int j, int k, Block block) { + StepSound stepsound = block.stepSound; + + if (this.world.getType(i, j + 1, k) == Blocks.SNOW) { + stepsound = Blocks.SNOW.stepSound; + this.makeSound(stepsound.getStepSound(), stepsound.getVolume1() * 0.15F, stepsound.getVolume2()); + } else if (!block.getMaterial().isLiquid()) { + this.makeSound(stepsound.getStepSound(), stepsound.getVolume1() * 0.15F, stepsound.getVolume2()); + } + } + + public void makeSound(String s, float f, float f1) { + this.world.makeSound(this, s, f, f1); + } + + protected boolean g_() { + return true; + } + + protected void a(double d0, boolean flag) { + if (flag) { + if (this.fallDistance > 0.0F) { + this.b(this.fallDistance); + this.fallDistance = 0.0F; + } + } else if (d0 < 0.0D) { + this.fallDistance = (float) ((double) this.fallDistance - d0); + } + } + + public AxisAlignedBB J() { + return null; + } + + protected void burn(float i) { // CraftBukkit - int -> float + if (!this.fireProof) { + this.damageEntity(DamageSource.FIRE, (float) i); + } + } + + public final boolean isFireproof() { + return this.fireProof; + } + + protected void b(float f) { + if (this.passenger != null) { + this.passenger.b(f); + } + } + + public boolean L() { + return this.inWater || this.world.isRainingAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)) || this.world.isRainingAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY + (double) this.length), MathHelper.floor(this.locZ)); + } + + public boolean M() { + return this.inWater; + } + + public boolean N() { + if (this.world.a(this.boundingBox.grow(0.0D, -0.4000000059604645D, 0.0D).shrink(0.001D, 0.001D, 0.001D), Material.WATER, this)) { + if (!this.inWater && !this.justCreated) { + float f = MathHelper.sqrt(this.motX * this.motX * 0.20000000298023224D + this.motY * this.motY + this.motZ * this.motZ * 0.20000000298023224D) * 0.2F; + + if (f > 1.0F) { + f = 1.0F; + } + + this.makeSound(this.O(), f, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + float f1 = (float) MathHelper.floor(this.boundingBox.b); + + int i; + float f2; + float f3; + + for (i = 0; (float) i < 1.0F + this.width * 20.0F; ++i) { + f2 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + this.world.addParticle("bubble", this.locX + (double) f2, (double) (f1 + 1.0F), this.locZ + (double) f3, this.motX, this.motY - (double) (this.random.nextFloat() * 0.2F), this.motZ); + } + + for (i = 0; (float) i < 1.0F + this.width * 20.0F; ++i) { + f2 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + this.world.addParticle("splash", this.locX + (double) f2, (double) (f1 + 1.0F), this.locZ + (double) f3, this.motX, this.motY, this.motZ); + } + } + + this.fallDistance = 0.0F; + this.inWater = true; + this.fireTicks = 0; + } else { + this.inWater = false; + } + + return this.inWater; + } + + protected String O() { + return "game.neutral.swim.splash"; + } + + public boolean a(Material material) { + double d0 = this.locY + (double) this.getHeadHeight(); + int i = MathHelper.floor(this.locX); + int j = MathHelper.d((float) MathHelper.floor(d0)); + int k = MathHelper.floor(this.locZ); + Block block = this.world.getType(i, j, k); + + if (block.getMaterial() == material) { + float f = BlockFluids.b(this.world.getData(i, j, k)) - 0.11111111F; + float f1 = (float) (j + 1) - f; + + return d0 < (double) f1; + } else { + return false; + } + } + + public float getHeadHeight() { + return 0.0F; + } + + public boolean P() { + // Poweruser start + return this.P(this.world); + } + + public boolean P(IBlockAccess iblockaccess) { + int currentTick = MinecraftServer.getServer().al(); + if(this.lastLavaCheck != currentTick) { + this.lastLavaCheck = currentTick; + this.isInLava = this.world.a(this.boundingBox.grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA, iblockaccess); + } + return this.isInLava; + } + // Poweruser end + + public void a(float f, float f1, float f2) { + float f3 = f * f + f1 * f1; + + if (f3 >= 1.0E-4F) { + f3 = MathHelper.c(f3); + if (f3 < 1.0F) { + f3 = 1.0F; + } + + f3 = f2 / f3; + f *= f3; + f1 *= f3; + float f4 = MathHelper.sin(this.yaw * 3.1415927F / 180.0F); + float f5 = MathHelper.cos(this.yaw * 3.1415927F / 180.0F); + + this.motX += (double) (f * f5 - f1 * f4); + this.motZ += (double) (f1 * f5 + f * f4); + } + } + + public float d(float f) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locZ); + + if (this.world.isLoaded(i, 0, j)) { + double d0 = (this.boundingBox.e - this.boundingBox.b) * 0.66D; + int k = MathHelper.floor(this.locY - (double) this.height + d0); + + return this.world.n(i, k, j); + } else { + return 0.0F; + } + } + + public void spawnIn(World world) { + // CraftBukkit start + if (world == null) { + this.die(); + this.world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); + return; + } + // CraftBukkit end + + this.world = world; + } + + public void setLocation(double d0, double d1, double d2, float f, float f1) { + this.lastX = this.locX = d0; + this.lastY = this.locY = d1; + this.lastZ = this.locZ = d2; + this.lastYaw = this.yaw = f; + this.lastPitch = this.pitch = f1; + this.V = 0.0F; + double d3 = (double) (this.lastYaw - f); + + if (d3 < -180.0D) { + this.lastYaw += 360.0F; + } + + if (d3 >= 180.0D) { + this.lastYaw -= 360.0F; + } + + this.setPosition(this.locX, this.locY, this.locZ); + this.b(f, f1); + } + + public void setPositionRotation(double d0, double d1, double d2, float f, float f1) { + this.S = this.lastX = this.locX = d0; + this.T = this.lastY = this.locY = d1 + (double) this.height; + this.U = this.lastZ = this.locZ = d2; + this.yaw = f; + this.pitch = f1; + this.setPosition(this.locX, this.locY, this.locZ); + } + + public float e(Entity entity) { + float f = (float) (this.locX - entity.locX); + float f1 = (float) (this.locY - entity.locY); + float f2 = (float) (this.locZ - entity.locZ); + + return MathHelper.c(f * f + f1 * f1 + f2 * f2); + } + + public double e(double d0, double d1, double d2) { + double d3 = this.locX - d0; + double d4 = this.locY - d1; + double d5 = this.locZ - d2; + + return d3 * d3 + d4 * d4 + d5 * d5; + } + + public double f(double d0, double d1, double d2) { + double d3 = this.locX - d0; + double d4 = this.locY - d1; + double d5 = this.locZ - d2; + + return (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + } + + public double f(Entity entity) { + double d0 = this.locX - entity.locX; + double d1 = this.locY - entity.locY; + double d2 = this.locZ - entity.locZ; + + return d0 * d0 + d1 * d1 + d2 * d2; + } + + public void b_(EntityHuman entityhuman) {} + + int numCollisions = 0; // Spigot + public void collide(Entity entity) { + if (entity.passenger != this && entity.vehicle != this) { + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2 = MathHelper.a(d0, d1); + + if (d2 >= 0.009999999776482582D) { + d2 = (double) MathHelper.sqrt(d2); + d0 /= d2; + d1 /= d2; + double d3 = 1.0D / d2; + + if (d3 > 1.0D) { + d3 = 1.0D; + } + + d0 *= d3; + d1 *= d3; + d0 *= 0.05000000074505806D; + d1 *= 0.05000000074505806D; + d0 *= (double) (1.0F - this.Y); + d1 *= (double) (1.0F - this.Y); + this.g(-d0, 0.0D, -d1); + entity.g(d0, 0.0D, d1); + } + } + } + + public void g(double d0, double d1, double d2) { + this.motX += d0; + this.motY += d1; + this.motZ += d2; + this.al = true; + } + + protected void Q() { + this.velocityChanged = true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + this.Q(); + return false; + } + } + + public boolean R() { + return false; + } + + public boolean S() { + return false; + } + + public void b(Entity entity, int i) {} + + public boolean c(NBTTagCompound nbttagcompound) { + String s = this.W(); + + if (!this.dead && s != null) { + nbttagcompound.setString("id", s); + this.e(nbttagcompound); + return true; + } else { + return false; + } + } + + public boolean d(NBTTagCompound nbttagcompound) { + String s = this.W(); + + if (!this.dead && s != null && this.passenger == null) { + nbttagcompound.setString("id", s); + this.e(nbttagcompound); + return true; + } else { + return false; + } + } + + public void e(NBTTagCompound nbttagcompound) { + try { + nbttagcompound.set("Pos", this.a(new double[] { this.locX, this.locY + (double) this.V, this.locZ})); + nbttagcompound.set("Motion", this.a(new double[] { this.motX, this.motY, this.motZ})); + + // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero + // TODO: make sure this is the best way to address this. + if (Float.isNaN(this.yaw)) { + this.yaw = 0; + } + + if (Float.isNaN(this.pitch)) { + this.pitch = 0; + } + // CraftBukkit end + + nbttagcompound.set("Rotation", this.a(new float[] { this.yaw, this.pitch})); + nbttagcompound.setFloat("FallDistance", this.fallDistance); + nbttagcompound.setShort("Fire", (short) this.fireTicks); + nbttagcompound.setShort("Air", (short) this.getAirTicks()); + nbttagcompound.setBoolean("OnGround", this.onGround); + nbttagcompound.setInt("Dimension", this.dimension); + nbttagcompound.setBoolean("Invulnerable", this.invulnerable); + nbttagcompound.setInt("PortalCooldown", this.portalCooldown); + nbttagcompound.setLong("UUIDMost", this.getUniqueID().getMostSignificantBits()); + nbttagcompound.setLong("UUIDLeast", this.getUniqueID().getLeastSignificantBits()); + // CraftBukkit start + nbttagcompound.setLong("WorldUUIDLeast", this.world.getDataManager().getUUID().getLeastSignificantBits()); + nbttagcompound.setLong("WorldUUIDMost", this.world.getDataManager().getUUID().getMostSignificantBits()); + nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL); + nbttagcompound.setInt("Spigot.ticksLived", this.ticksLived); + // CraftBukkit end + this.b(nbttagcompound); + if (this.vehicle != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + if (this.vehicle.c(nbttagcompound1)) { + nbttagcompound.set("Riding", nbttagcompound1); + } + } + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being saved"); + + this.a(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + public void f(NBTTagCompound nbttagcompound) { + try { + NBTTagList nbttaglist = nbttagcompound.getList("Pos", 6); + NBTTagList nbttaglist1 = nbttagcompound.getList("Motion", 6); + NBTTagList nbttaglist2 = nbttagcompound.getList("Rotation", 5); + + this.motX = nbttaglist1.d(0); + this.motY = nbttaglist1.d(1); + this.motZ = nbttaglist1.d(2); + /* CraftBukkit start - Moved section down + if (Math.abs(this.motX) > 10.0D) { + this.motX = 0.0D; + } + + if (Math.abs(this.motY) > 10.0D) { + this.motY = 0.0D; + } + + if (Math.abs(this.motZ) > 10.0D) { + this.motZ = 0.0D; + } + // CraftBukkit end */ + + this.lastX = this.S = this.locX = nbttaglist.d(0); + this.lastY = this.T = this.locY = nbttaglist.d(1); + this.lastZ = this.U = this.locZ = nbttaglist.d(2); + this.lastYaw = this.yaw = nbttaglist2.e(0); + this.lastPitch = this.pitch = nbttaglist2.e(1); + this.fallDistance = nbttagcompound.getFloat("FallDistance"); + this.fireTicks = nbttagcompound.getShort("Fire"); + this.setAirTicks(nbttagcompound.getShort("Air")); + this.onGround = nbttagcompound.getBoolean("OnGround"); + this.dimension = nbttagcompound.getInt("Dimension"); + this.invulnerable = nbttagcompound.getBoolean("Invulnerable"); + this.portalCooldown = nbttagcompound.getInt("PortalCooldown"); + if (nbttagcompound.hasKeyOfType("UUIDMost", 4) && nbttagcompound.hasKeyOfType("UUIDLeast", 4)) { + this.uniqueID = new UUID(nbttagcompound.getLong("UUIDMost"), nbttagcompound.getLong("UUIDLeast")); + } + + this.setPosition(this.locX, this.locY, this.locZ); + this.b(this.yaw, this.pitch); + this.a(nbttagcompound); + if (this.V()) { + this.setPosition(this.locX, this.locY, this.locZ); + } + + // CraftBukkit start + if (this instanceof EntityLiving) { + EntityLiving entity = (EntityLiving) this; + + this.ticksLived = nbttagcompound.getInt("Spigot.ticksLived"); + + // Reset the persistence for tamed animals + if (entity instanceof EntityTameableAnimal && !isLevelAtLeast(nbttagcompound, 2) && !nbttagcompound.getBoolean("PersistenceRequired")) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + entityinsentient.persistent = !entityinsentient.isTypeNotPersistent(); + } + } + // CraftBukkit end + + // CraftBukkit start - Exempt Vehicles from notch's sanity check + if (!(this.getBukkitEntity() instanceof Vehicle)) { + if (Math.abs(this.motX) > 10.0D) { + this.motX = 0.0D; + } + + if (Math.abs(this.motY) > 10.0D) { + this.motY = 0.0D; + } + + if (Math.abs(this.motZ) > 10.0D) { + this.motZ = 0.0D; + } + } + // CraftBukkit end + + // CraftBukkit start - Reset world + if (this instanceof EntityPlayer) { + Server server = Bukkit.getServer(); + org.bukkit.World bworld = null; + + // TODO: Remove World related checks, replaced with WorldUID. + String worldName = nbttagcompound.getString("World"); + + if (nbttagcompound.hasKey("WorldUUIDMost") && nbttagcompound.hasKey("WorldUUIDLeast")) { + UUID uid = new UUID(nbttagcompound.getLong("WorldUUIDMost"), nbttagcompound.getLong("WorldUUIDLeast")); + bworld = server.getWorld(uid); + } else { + bworld = server.getWorld(worldName); + } + + if (bworld == null) { + EntityPlayer entityPlayer = (EntityPlayer) this; + bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getWorldServer(entityPlayer.dimension).getWorld(); + } + + this.spawnIn(bworld == null ? null : ((CraftWorld) bworld).getHandle()); + } + // CraftBukkit end + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded"); + + this.a(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + protected boolean V() { + return true; + } + + protected final String W() { + return EntityTypes.b(this); + } + + protected abstract void a(NBTTagCompound nbttagcompound); + + protected abstract void b(NBTTagCompound nbttagcompound); + + public void X() {} + + protected NBTTagList a(double... adouble) { + NBTTagList nbttaglist = new NBTTagList(); + double[] adouble1 = adouble; + int i = adouble.length; + + for (int j = 0; j < i; ++j) { + double d0 = adouble1[j]; + + nbttaglist.add(new NBTTagDouble(d0)); + } + + return nbttaglist; + } + + protected NBTTagList a(float... afloat) { + NBTTagList nbttaglist = new NBTTagList(); + float[] afloat1 = afloat; + int i = afloat.length; + + for (int j = 0; j < i; ++j) { + float f = afloat1[j]; + + nbttaglist.add(new NBTTagFloat(f)); + } + + return nbttaglist; + } + + public EntityItem a(Item item, int i) { + return this.a(item, i, 0.0F); + } + + public EntityItem a(Item item, int i, float f) { + return this.a(new ItemStack(item, i, 0), f); + } + + public EntityItem a(ItemStack itemstack, float f) { + if (itemstack.count != 0 && itemstack.getItem() != null) { + // CraftBukkit start - Capture drops for death event + if (this instanceof EntityLiving && ((EntityLiving) this).drops != null) { + ((EntityLiving) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); + return null; + } + // CraftBukkit end + + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY + (double) f, this.locZ, itemstack); + + entityitem.pickupDelay = 10; + this.world.addEntity(entityitem); + return entityitem; + } else { + return null; + } + } + + public boolean isAlive() { + return !this.dead; + } + + private int lastInBlockTick = -1; + private boolean lastInBlockResult = false; + + public boolean inBlock() { + int currentTick = MinecraftServer.currentTick; + + if (lastInBlockTick == currentTick) { + return lastInBlockResult; + } + + for (int i = 0; i < 8; ++i) { + float f = ((float) ((i >> 0) % 2) - 0.5F) * this.width * 0.8F; + float f1 = ((float) ((i >> 1) % 2) - 0.5F) * 0.1F; + float f2 = ((float) ((i >> 2) % 2) - 0.5F) * this.width * 0.8F; + int j = MathHelper.floor(this.locX + (double) f); + int k = MathHelper.floor(this.locY + (double) this.getHeadHeight() + (double) f1); + int l = MathHelper.floor(this.locZ + (double) f2); + + if (this.world.getType(j, k, l).r()) { + lastInBlockTick = currentTick; + lastInBlockResult = true; + return true; + } + } + + lastInBlockTick = currentTick; + lastInBlockResult = false; + return false; + } + + public boolean c(EntityHuman entityhuman) { + return false; + } + + public AxisAlignedBB h(Entity entity) { + return null; + } + + public void ab() { + if (this.vehicle.dead) { + this.vehicle = null; + } else { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.h(); + if (this.vehicle != null) { + this.vehicle.ac(); + this.h += (double) (this.vehicle.yaw - this.vehicle.lastYaw); + + for (this.g += (double) (this.vehicle.pitch - this.vehicle.lastPitch); this.h >= 180.0D; this.h -= 360.0D) { + ; + } + + while (this.h < -180.0D) { + this.h += 360.0D; + } + + while (this.g >= 180.0D) { + this.g -= 360.0D; + } + + while (this.g < -180.0D) { + this.g += 360.0D; + } + + double d0 = this.h * 0.5D; + double d1 = this.g * 0.5D; + float f = 10.0F; + + if (d0 > (double) f) { + d0 = (double) f; + } + + if (d0 < (double) (-f)) { + d0 = (double) (-f); + } + + if (d1 > (double) f) { + d1 = (double) f; + } + + if (d1 < (double) (-f)) { + d1 = (double) (-f); + } + + this.h -= d0; + this.g -= d1; + } + } + } + + public void ac() { + if (this.passenger != null) { + this.passenger.setPosition(this.locX, this.locY + this.ae() + this.passenger.ad(), this.locZ); // Spigot + } + } + + public double ad() { + return (double) this.height; + } + + public double ae() { + return (double) this.length * 0.75D; + } + + public void mount(Entity entity) { + // CraftBukkit start + this.setPassengerOf(entity); + } + + protected CraftEntity bukkitEntity; + + public CraftEntity getBukkitEntity() { + if (this.bukkitEntity == null) { + this.bukkitEntity = CraftEntity.getEntity(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + public void setPassengerOf(Entity entity) { + // b(null) doesn't really fly for overloaded methods, + // so this method is needed + + Entity originalVehicle = this.vehicle; + Entity originalPassenger = this.vehicle == null ? null : this.vehicle.passenger; + PluginManager pluginManager = Bukkit.getPluginManager(); + this.getBukkitEntity(); // make sure bukkitEntity is initialised + // CraftBukkit end + this.g = 0.0D; + this.h = 0.0D; + if (entity == null) { + if (this.vehicle != null) { + // CraftBukkit start + if ((this.bukkitEntity instanceof LivingEntity) && (this.vehicle.getBukkitEntity() instanceof Vehicle)) { + VehicleExitEvent event = new VehicleExitEvent((Vehicle) this.vehicle.getBukkitEntity(), (LivingEntity) this.bukkitEntity); + pluginManager.callEvent(event); + + if (event.isCancelled() || this.vehicle != originalVehicle) { + return; + } + } + // CraftBukkit end + pluginManager.callEvent( new org.spigotmc.event.entity.EntityDismountEvent( this.getBukkitEntity(), this.vehicle.getBukkitEntity() ) ); // Spigot + + this.setPositionRotation(this.vehicle.locX, this.vehicle.boundingBox.b + (double) this.vehicle.length, this.vehicle.locZ, this.yaw, this.pitch); + this.vehicle.passenger = null; + } + + this.vehicle = null; + } else { + // CraftBukkit start + if ((this.bukkitEntity instanceof LivingEntity) && (entity.getBukkitEntity() instanceof Vehicle) && entity.world.isChunkLoaded((int) entity.locX >> 4, (int) entity.locZ >> 4)) { + // It's possible to move from one vehicle to another. We need to check if they're already in a vehicle, and fire an exit event if they are. + VehicleExitEvent exitEvent = null; + if (this.vehicle != null && this.vehicle.getBukkitEntity() instanceof Vehicle) { + exitEvent = new VehicleExitEvent((Vehicle) this.vehicle.getBukkitEntity(), (LivingEntity) this.bukkitEntity); + pluginManager.callEvent(exitEvent); + + if (exitEvent.isCancelled() || this.vehicle != originalVehicle || (this.vehicle != null && this.vehicle.passenger != originalPassenger)) { + return; + } + } + + VehicleEnterEvent event = new VehicleEnterEvent((Vehicle) entity.getBukkitEntity(), this.bukkitEntity); + pluginManager.callEvent(event); + + // If a plugin messes with the vehicle or the vehicle's passenger + if (event.isCancelled() || this.vehicle != originalVehicle || (this.vehicle != null && this.vehicle.passenger != originalPassenger)) { + // If we only cancelled the enterevent then we need to put the player in a decent position. + if (exitEvent != null && this.vehicle == originalVehicle && this.vehicle != null && this.vehicle.passenger == originalPassenger) { + this.setPositionRotation(this.vehicle.locX, this.vehicle.boundingBox.b + (double) this.vehicle.length, this.vehicle.locZ, this.yaw, this.pitch); + this.vehicle.passenger = null; + this.vehicle = null; + } + return; + } + } + // CraftBukkit end + // Spigot Start + if ( entity.world.isChunkLoaded( (int) entity.locX >> 4, (int) entity.locZ >> 4 ) ) + { + org.spigotmc.event.entity.EntityMountEvent event = new org.spigotmc.event.entity.EntityMountEvent( this.getBukkitEntity(), entity.getBukkitEntity() ); + pluginManager.callEvent( event ); + if ( event.isCancelled() ) + { + return; + } + } + // Spigot End + + if (this.vehicle != null) { + this.vehicle.passenger = null; + } + + if (entity != null) { + for (Entity entity1 = entity.vehicle; entity1 != null; entity1 = entity1.vehicle) { + if (entity1 == this) { + return; + } + } + } + + this.vehicle = entity; + entity.passenger = this; + } + } + + public float af() { + return 0.1F; + } + + public Vec3D ag() { + return null; + } + + public void ah() { + if (this.portalCooldown > 0) { + this.portalCooldown = this.ai(); + } else { + double d0 = this.lastX - this.locX; + double d1 = this.lastZ - this.locZ; + + if (!this.world.isStatic && !this.an) { + this.aq = Direction.a(d0, d1); + } + + this.an = true; + } + } + + public int ai() { + return 300; + } + + public ItemStack[] getEquipment() { + return null; + } + + public void setEquipment(int i, ItemStack itemstack) {} + + public boolean isBurning() { + boolean flag = this.world != null && this.world.isStatic; + + return !this.fireProof && (this.fireTicks > 0 || flag && this.g(0)); + } + + public boolean am() { + return this.vehicle != null; + } + + public boolean isSneaking() { + return this.g(1); + } + + public void setSneaking(boolean flag) { + this.a(1, flag); + } + + public boolean isSprinting() { + return this.g(3); + } + + public void setSprinting(boolean flag) { + this.a(3, flag); + } + + public boolean isInvisible() { + return this.g(5); + } + + public void setInvisible(boolean flag) { + this.a(5, flag); + } + + public void e(boolean flag) { + this.a(4, flag); + } + + protected boolean g(int i) { + return (this.datawatcher.getByte(0) & 1 << i) != 0; + } + + protected void a(int i, boolean flag) { + byte b0 = this.datawatcher.getByte(0); + + if (flag) { + this.datawatcher.watch(0, Byte.valueOf((byte) (b0 | 1 << i))); + } else { + this.datawatcher.watch(0, Byte.valueOf((byte) (b0 & ~(1 << i)))); + } + } + + public int getAirTicks() { + return this.datawatcher.getShort(1); + } + + public void setAirTicks(int i) { + this.datawatcher.watch(1, Short.valueOf((short) i)); + } + + public void a(EntityLightning entitylightning) { + // CraftBukkit start + final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity(); + final org.bukkit.entity.Entity stormBukkitEntity = entitylightning.getBukkitEntity(); + final PluginManager pluginManager = Bukkit.getPluginManager(); + + if (thisBukkitEntity instanceof Hanging) { + HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity); + PaintingBreakByEntityEvent paintingEvent = null; + + if (thisBukkitEntity instanceof Painting) { + paintingEvent = new PaintingBreakByEntityEvent((Painting) thisBukkitEntity, stormBukkitEntity); + } + + pluginManager.callEvent(hangingEvent); + + if (paintingEvent != null) { + paintingEvent.setCancelled(hangingEvent.isCancelled()); + pluginManager.callEvent(paintingEvent); + } + + if (hangingEvent.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { + return; + } + } + + if (this.fireProof) { + return; + } + CraftEventFactory.entityDamage = entitylightning; + if (!this.damageEntity(DamageSource.FIRE, 5.0F)) { + CraftEventFactory.entityDamage = null; + return; + } + // CraftBukkit end + + ++this.fireTicks; + if (this.fireTicks == 0) { + // CraftBukkit start - Call a combust event when lightning strikes + EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8); + pluginManager.callEvent(entityCombustEvent); + if (!entityCombustEvent.isCancelled()) { + this.setOnFire(entityCombustEvent.getDuration()); + } + // CraftBukkit end + } + } + + public void a(EntityLiving entityliving) {} + + protected boolean j(double d0, double d1, double d2) { + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + double d3 = d0 - (double) i; + double d4 = d1 - (double) j; + double d5 = d2 - (double) k; + List list = this.world.a(this.boundingBox); + + if (list.isEmpty() && !this.world.q(i, j, k)) { + return false; + } else { + boolean flag = !this.world.q(i - 1, j, k); + boolean flag1 = !this.world.q(i + 1, j, k); + boolean flag2 = !this.world.q(i, j - 1, k); + boolean flag3 = !this.world.q(i, j + 1, k); + boolean flag4 = !this.world.q(i, j, k - 1); + boolean flag5 = !this.world.q(i, j, k + 1); + byte b0 = 3; + double d6 = 9999.0D; + + if (flag && d3 < d6) { + d6 = d3; + b0 = 0; + } + + if (flag1 && 1.0D - d3 < d6) { + d6 = 1.0D - d3; + b0 = 1; + } + + if (flag3 && 1.0D - d4 < d6) { + d6 = 1.0D - d4; + b0 = 3; + } + + if (flag4 && d5 < d6) { + d6 = d5; + b0 = 4; + } + + if (flag5 && 1.0D - d5 < d6) { + d6 = 1.0D - d5; + b0 = 5; + } + + float f = this.random.nextFloat() * 0.2F + 0.1F; + + if (b0 == 0) { + this.motX = (double) (-f); + } + + if (b0 == 1) { + this.motX = (double) f; + } + + if (b0 == 2) { + this.motY = (double) (-f); + } + + if (b0 == 3) { + this.motY = (double) f; + } + + if (b0 == 4) { + this.motZ = (double) (-f); + } + + if (b0 == 5) { + this.motZ = (double) f; + } + + return true; + } + } + + public void as() { + this.I = true; + this.fallDistance = 0.0F; + this.inWebTick = MinecraftServer.currentTick; // Guardian + } + + public String getName() { + String s = EntityTypes.b(this); + + if (s == null) { + s = "generic"; + } + + return LocaleI18n.get("entity." + s + ".name"); + } + + public Entity[] at() { + return null; + } + + public boolean i(Entity entity) { + return this == entity; + } + + public float getHeadRotation() { + return 0.0F; + } + + public boolean av() { + return true; + } + + public boolean j(Entity entity) { + return false; + } + + public String toString() { + return String.format("%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", new Object[] { this.getClass().getSimpleName(), this.getName(), Integer.valueOf(this.id), this.world == null ? "~NULL~" : this.world.getWorldData().getName(), Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ)}); + } + + public boolean isInvulnerable() { + return this.invulnerable; + } + + public void k(Entity entity) { + this.setPositionRotation(entity.locX, entity.locY, entity.locZ, entity.yaw, entity.pitch); + } + + public void a(Entity entity, boolean flag) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + entity.e(nbttagcompound); + this.f(nbttagcompound); + this.portalCooldown = entity.portalCooldown; + this.aq = entity.aq; + } + + public void b(int i) { + if (!this.world.isStatic && !this.dead) { + this.world.methodProfiler.a("changeDimension"); + MinecraftServer minecraftserver = MinecraftServer.getServer(); + // CraftBukkit start - Move logic into new function "teleportToLocation" + // int j = this.dimension; + WorldServer exitWorld = null; + if (this.dimension < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // Plugins must specify exit from custom Bukkit worlds + // Only target existing worlds (compensate for allow-nether/allow-end as false) + for (WorldServer world : minecraftserver.worlds) { + if (world.dimension == i) { + exitWorld = world; + } + } + } + + Location enter = this.getBukkitEntity().getLocation(); + Location exit = exitWorld != null ? minecraftserver.getPlayerList().calculateTarget(enter, minecraftserver.getWorldServer(i)) : null; + boolean useTravelAgent = exitWorld != null && !(this.dimension == 1 && exitWorld.dimension == 1); // don't use agent for custom worlds or return from THE_END + + TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent() : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins + EntityPortalEvent event = new EntityPortalEvent(this.getBukkitEntity(), enter, exit, agent); + event.useTravelAgent(useTravelAgent); + event.getEntity().getServer().getPluginManager().callEvent(event); + if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !this.isAlive()) { + return; + } + exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); + this.teleportTo(exit, true); + } + } + + public void teleportTo(Location exit, boolean portal) { + if (true) { + WorldServer worldserver = ((CraftWorld) this.getBukkitEntity().getLocation().getWorld()).getHandle(); + WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); + int i = worldserver1.dimension; + // CraftBukkit end + + this.dimension = i; + /* CraftBukkit start - TODO: Check if we need this + if (j == 1 && i == 1) { + worldserver1 = minecraftserver.getWorldServer(0); + this.dimension = 0; + } + // CraftBukkit end */ + + this.world.kill(this); + this.dead = false; + this.world.methodProfiler.a("reposition"); + // CraftBukkit start - Ensure chunks are loaded in case TravelAgent is not used which would initially cause chunks to load during find/create + // minecraftserver.getPlayerList().a(this, j, worldserver, worldserver1); + boolean before = worldserver1.chunkProviderServer.forceChunkLoad; + worldserver1.chunkProviderServer.forceChunkLoad = true; + //worldserver1.getMinecraftServer().getPlayerList().repositionEntity(this, exit, portal); // PaperSpigot - no... this entity is dead + worldserver1.chunkProviderServer.forceChunkLoad = before; + // CraftBukkit end + this.world.methodProfiler.c("reloading"); + Entity entity = EntityTypes.createEntityByName(EntityTypes.b(this), worldserver1); + + if (entity != null) { + entity.a(this, true); + // PaperSpigot start - move entity to new location + exit.getBlock(); // force load + entity.setLocation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); + // PaperSpigot end + /* CraftBukkit start - We need to do this... + if (j == 1 && i == 1) { + ChunkCoordinates chunkcoordinates = worldserver1.getSpawn(); + + chunkcoordinates.y = this.world.i(chunkcoordinates.x, chunkcoordinates.z); + entity.setPositionRotation((double) chunkcoordinates.x, (double) chunkcoordinates.y, (double) chunkcoordinates.z, entity.yaw, entity.pitch); + } + // CraftBukkit end */ + worldserver1.addEntity(entity); + // CraftBukkit start - Forward the CraftEntity to the new entity + this.getBukkitEntity().setHandle(entity); + entity.bukkitEntity = this.getBukkitEntity(); + // CraftBukkit end + } + + this.dead = true; + this.world.methodProfiler.b(); + worldserver.i(); + worldserver1.i(); + this.world.methodProfiler.b(); + } + } + + public float a(Explosion explosion, World world, int i, int j, int k, Block block) { + return block.a(this); + } + + public boolean a(Explosion explosion, World world, int i, int j, int k, Block block, float f) { + return true; + } + + public int ax() { + return 3; + } + + public int ay() { + return this.aq; + } + + public boolean az() { + return false; + } + + public void a(CrashReportSystemDetails crashreportsystemdetails) { + crashreportsystemdetails.a("Entity Type", (Callable) (new CrashReportEntityType(this))); + crashreportsystemdetails.a("Entity ID", Integer.valueOf(this.id)); + crashreportsystemdetails.a("Entity Name", (Callable) (new CrashReportEntityName(this))); + crashreportsystemdetails.a("Entity\'s Exact location", String.format("%.2f, %.2f, %.2f", new Object[] { Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ)})); + crashreportsystemdetails.a("Entity\'s Block location", CrashReportSystemDetails.a(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ))); + crashreportsystemdetails.a("Entity\'s Momentum", String.format("%.2f, %.2f, %.2f", new Object[] { Double.valueOf(this.motX), Double.valueOf(this.motY), Double.valueOf(this.motZ)})); + } + + public UUID getUniqueID() { + return this.uniqueID; + } + + public boolean aC() { + return true; + } + + public IChatBaseComponent getScoreboardDisplayName() { + return new ChatComponentText(this.getName()); + } + + public void i(int i) {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityAgeable.java b/vspigot-server/src/main/java/net/minecraft/server/EntityAgeable.java new file mode 100644 index 0000000..bba31ed --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityAgeable.java @@ -0,0 +1,149 @@ +package net.minecraft.server; + +public abstract class EntityAgeable extends EntityCreature { + + private float bp = -1.0F; + private float bq; + public boolean ageLocked = false; // CraftBukkit + + // Spigot start + @Override + public void inactiveTick() + { + super.inactiveTick(); + if ( this.world.isStatic || this.ageLocked ) + { // CraftBukkit + this.a( this.isBaby() ); + } else + { + int i = this.getAge(); + + if ( i < 0 ) + { + ++i; + this.setAge( i ); + } else if ( i > 0 ) + { + --i; + this.setAge( i ); + } + } + } + // Spigot end + + public EntityAgeable(World world) { + super(world); + } + + public abstract EntityAgeable createChild(EntityAgeable entityageable); + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() == Items.MONSTER_EGG) { + if (!this.world.isStatic) { + Class oclass = EntityTypes.a(itemstack.getData()); + + if (oclass != null && oclass.isAssignableFrom(this.getClass())) { + EntityAgeable entityageable = this.createChild(this); + + if (entityageable != null) { + entityageable.setAge(-24000); + entityageable.setPositionRotation(this.locX, this.locY, this.locZ, 0.0F, 0.0F); + this.world.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // CraftBukkit + if (itemstack.hasName()) { + entityageable.setCustomName(itemstack.getName()); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + if (itemstack.count <= 0) { // EMC + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + } + } + } + } + + return true; + } else { + return false; + } + } + + protected void c() { + super.c(); + this.datawatcher.a(12, new org.spigotmc.ProtocolData.IntByte(0, (byte) 0)); // Spigot - protocol patch + } + + public int getAge() { + return this.datawatcher.getIntByte(12).value; // Spigot - protocol patch + } + + public void a(int i) { + int j = this.getAge(); + + j += i * 20; + if (j > 0) { + j = 0; + } + + this.setAge(j); + } + + public void setAge(int i) { + this.datawatcher.watch(12, new org.spigotmc.ProtocolData.IntByte(i, (byte) ( i < 0 ? -1 : (i >= 6000 ? 1 : 0)))); // Spigot - protocol patch + this.a(this.isBaby()); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Age", this.getAge()); + nbttagcompound.setBoolean("AgeLocked", this.ageLocked); // CraftBukkit + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setAge(nbttagcompound.getInt("Age")); + this.ageLocked = nbttagcompound.getBoolean("AgeLocked"); // CraftBukkit + } + + public void e() { + super.e(); + if (this.world.isStatic || this.ageLocked) { // CraftBukkit + this.a(this.isBaby()); + } else { + int i = this.getAge(); + + if (i < 0) { + ++i; + this.setAge(i); + } else if (i > 0) { + --i; + this.setAge(i); + } + } + } + + public boolean isBaby() { + return this.getAge() < 0; + } + + public void a(boolean flag) { + this.a(flag ? 0.5F : 1.0F); + } + + protected final void a(float f, float f1) { + boolean flag = this.bp > 0.0F; + + this.bp = f; + this.bq = f1; + if (!flag) { + this.a(1.0F); + } + } + + protected final void a(float f) { + super.a(this.bp * f, this.bq * f); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityArrow.java b/vspigot-server/src/main/java/net/minecraft/server/EntityArrow.java new file mode 100644 index 0000000..5796d86 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityArrow.java @@ -0,0 +1,481 @@ +package net.minecraft.server; + +import java.util.List; + +// CraftBukkit start +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +// CraftBukkit end + +public class EntityArrow extends Entity implements IProjectile { + + private int d = -1; + private int e = -1; + private int f = -1; + private Block g; + private int h; + public boolean inGround = false; // Spigot - private -> public + public int fromPlayer; + public int shake; + public Entity shooter; + private int at; + private int au; + private double damage = 2.0D; + public int knockbackStrength; // CraftBukkit - private -> public + + // Spigot Start + @Override + public void inactiveTick() + { + if ( this.inGround ) + { + this.at += 19; // Despawn counter. First int after shooter + } + super.inactiveTick(); + } + // Spigot End + + public EntityArrow(World world) { + super(world); + this.j = 10.0D; + this.a(0.5F, 0.5F); + } + + public EntityArrow(World world, double d0, double d1, double d2) { + super(world); + this.j = 10.0D; + this.a(0.5F, 0.5F); + this.setPosition(d0, d1, d2); + this.height = 0.0F; + } + + public EntityArrow(World world, EntityLiving entityliving, EntityLiving entityliving1, float f, float f1) { + super(world); + this.j = 10.0D; + this.shooter = entityliving; + this.projectileSource = (LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + if (entityliving instanceof EntityHuman) { + this.fromPlayer = 1; + } + + this.locY = entityliving.locY + (double) entityliving.getHeadHeight() - 0.10000000149011612D; + double d0 = entityliving1.locX - entityliving.locX; + double d1 = entityliving1.boundingBox.b + (double) (entityliving1.length / 3.0F) - this.locY; + double d2 = entityliving1.locZ - entityliving.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + + if (d3 >= 1.0E-7D) { + float f2 = (float) (Math.atan2(d2, d0) * 180.0D / 3.1415927410125732D) - 90.0F; + float f3 = (float) (-(Math.atan2(d1, d3) * 180.0D / 3.1415927410125732D)); + double d4 = d0 / d3; + double d5 = d2 / d3; + + this.setPositionRotation(entityliving.locX + d4, this.locY, entityliving.locZ + d5, f2, f3); + this.height = 0.0F; + float f4 = (float) d3 * 0.2F; + + this.shoot(d0, d1 + (double) f4, d2, f, f1); + } + } + + public EntityArrow(World world, EntityLiving entityliving, float f) { + super(world); + this.j = 10.0D; + this.shooter = entityliving; + this.projectileSource = (LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + if (entityliving instanceof EntityHuman) { + this.fromPlayer = 1; + } + + this.a(0.5F, 0.5F); + this.setPositionRotation(entityliving.locX, entityliving.locY + (double) entityliving.getHeadHeight(), entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.locX -= (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * 0.16F); + this.locY -= 0.10000000149011612D; + this.locZ -= (double) (MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * 0.16F); + this.setPosition(this.locX, this.locY, this.locZ); + this.height = 0.0F; + this.motX = (double) (-MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F)); + this.motZ = (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F)); + this.motY = (double) (-MathHelper.sin(this.pitch / 180.0F * 3.1415927F)); + this.shoot(this.motX, this.motY, this.motZ, f * 1.5F, 1.0F); + } + + protected void c() { + this.datawatcher.a(16, Byte.valueOf((byte) 0)); + } + + public void shoot(double d0, double d1, double d2, float f, float f1) { + float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d0 /= (double) f2; + d1 /= (double) f2; + d2 /= (double) f2; + d0 += this.random.nextGaussian() * (double) (this.random.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double) f1; + d1 += this.random.nextGaussian() * (double) (this.random.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double) f1; + d2 += this.random.nextGaussian() * (double) (this.random.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double) f1; + d0 *= (double) f; + d1 *= (double) f; + d2 *= (double) f; + this.motX = d0; + this.motY = d1; + this.motZ = d2; + float f3 = MathHelper.sqrt(d0 * d0 + d2 * d2); + + this.lastYaw = this.yaw = (float) (Math.atan2(d0, d2) * 180.0D / 3.1415927410125732D); + this.lastPitch = this.pitch = (float) (Math.atan2(d1, (double) f3) * 180.0D / 3.1415927410125732D); + this.at = 0; + } + + public void h() { + super.h(); + if (this.lastPitch == 0.0F && this.lastYaw == 0.0F) { + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.lastYaw = this.yaw = (float) (Math.atan2(this.motX, this.motZ) * 180.0D / 3.1415927410125732D); + this.lastPitch = this.pitch = (float) (Math.atan2(this.motY, (double) f) * 180.0D / 3.1415927410125732D); + } + + Block block = this.world.getType(this.d, this.e, this.f); + + if (block.getMaterial() != Material.AIR) { + block.updateShape(this.world, this.d, this.e, this.f); + AxisAlignedBB axisalignedbb = block.a(this.world, this.d, this.e, this.f); + + if (axisalignedbb != null && axisalignedbb.a(Vec3D.a(this.locX, this.locY, this.locZ))) { + this.inGround = true; + } + } + + if (this.shake > 0) { + --this.shake; + } + + if (this.inGround) { + int i = this.world.getData(this.d, this.e, this.f); + + if (block == this.g && i == this.h) { + ++this.at; + if (this.at >= world.spigotConfig.arrowDespawnRate) { // First int after shooter + this.die(); + } + } else { + this.inGround = false; + this.motX *= (double) (this.random.nextFloat() * 0.2F); + this.motY *= (double) (this.random.nextFloat() * 0.2F); + this.motZ *= (double) (this.random.nextFloat() * 0.2F); + this.at = 0; + this.au = 0; + } + } else { + ++this.au; + Vec3D vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1, false, true, false); + + vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + if (movingobjectposition != null) { + vec3d1 = Vec3D.a(movingobjectposition.pos.a, movingobjectposition.pos.b, movingobjectposition.pos.c); + } + + Entity entity = null; + List list = this.world.getEntities(this, this.boundingBox.a(this.motX, this.motY, this.motZ).grow(1.0D, 1.0D, 1.0D)); + double d0 = 0.0D; + + int j; + float f1; + + for (j = 0; j < list.size(); ++j) { + Entity entity1 = (Entity) list.get(j); + + if (entity1.R() && (entity1 != this.shooter || this.au >= 5)) { + f1 = 0.3F; + AxisAlignedBB axisalignedbb1 = entity1.boundingBox.grow((double) f1, (double) f1, (double) f1); + MovingObjectPosition movingobjectposition1 = axisalignedbb1.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { + double d1 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + if (movingobjectposition != null && movingobjectposition.entity != null && movingobjectposition.entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) movingobjectposition.entity; + + if (entityhuman.abilities.isInvulnerable || this.shooter instanceof EntityHuman && !((EntityHuman) this.shooter).a(entityhuman)) { + movingobjectposition = null; + } + } + + float f2; + float f3; + + // PaperSpigot start - Allow arrows to fly through players + if (movingobjectposition != null && movingobjectposition.entity instanceof EntityPlayer && shooter != null && shooter instanceof EntityPlayer) { + if (!((EntityPlayer) shooter).getBukkitEntity().canSee(((EntityPlayer) movingobjectposition.entity).getBukkitEntity())) { + movingobjectposition = null; + } + } + // PaperSpigot end + + if (movingobjectposition != null) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); // CraftBukkit - Call event + + if (movingobjectposition.entity != null) { + f2 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ); + int k = MathHelper.f((double) f2 * this.damage); + + if (this.isCritical()) { + k += this.random.nextInt(k / 2 + 2); + } + + DamageSource damagesource; + if (this.shooter == null) { + damagesource = DamageSource.arrow(this, this); + } else { + damagesource = DamageSource.arrow(this, this.shooter); + } + + // CraftBukkit start - Moved damage call + if (movingobjectposition.entity.damageEntity(damagesource, k)) { + if (this.isBurning() && !(movingobjectposition.entity instanceof EntityEnderman) && (!(movingobjectposition.entity instanceof EntityPlayer) || !(this.shooter instanceof EntityPlayer) || this.world.pvpMode)) { // CraftBukkit - abide by pvp setting if destination is a player + EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 5); + org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + + if (!combustEvent.isCancelled()) { + movingobjectposition.entity.setOnFire(combustEvent.getDuration()); + } + // CraftBukkit end + } + + // if (movingobjectposition.entity.damageEntity(damagesource, (float) k)) { // CraftBukkit - moved up + if (movingobjectposition.entity instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) movingobjectposition.entity; + + if (!this.world.isStatic) { + entityliving.p(entityliving.aZ() + 1); + } + + if (this.knockbackStrength > 0) { + f3 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + if (f3 > 0.0F) { + movingobjectposition.entity.g(this.motX * (double) this.knockbackStrength * 0.6000000238418579D / (double) f3, 0.1D, this.motZ * (double) this.knockbackStrength * 0.6000000238418579D / (double) f3); + } + } + + if (this.shooter != null && this.shooter instanceof EntityLiving) { + EnchantmentManager.a(entityliving, this.shooter); + EnchantmentManager.b((EntityLiving) this.shooter, entityliving); + } + + if (this.shooter != null && movingobjectposition.entity != this.shooter && movingobjectposition.entity instanceof EntityHuman && this.shooter instanceof EntityPlayer) { + ((EntityPlayer) this.shooter).playerConnection.sendPacket(new PacketPlayOutGameStateChange(6, 0.0F)); + } + } + + this.makeSound("random.bowhit", 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F)); + if (!(movingobjectposition.entity instanceof EntityEnderman)) { + this.die(); + } + } else { + this.motX *= -0.10000000149011612D; + this.motY *= -0.10000000149011612D; + this.motZ *= -0.10000000149011612D; + this.yaw += 180.0F; + this.lastYaw += 180.0F; + this.au = 0; + } + } else { + this.d = movingobjectposition.b; + this.e = movingobjectposition.c; + this.f = movingobjectposition.d; + this.g = this.world.getType(this.d, this.e, this.f); + this.h = this.world.getData(this.d, this.e, this.f); + this.motX = (double) ((float) (movingobjectposition.pos.a - this.locX)); + this.motY = (double) ((float) (movingobjectposition.pos.b - this.locY)); + this.motZ = (double) ((float) (movingobjectposition.pos.c - this.locZ)); + f2 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ); + this.locX -= this.motX / (double) f2 * 0.05000000074505806D; + this.locY -= this.motY / (double) f2 * 0.05000000074505806D; + this.locZ -= this.motZ / (double) f2 * 0.05000000074505806D; + this.makeSound("random.bowhit", 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F)); + this.inGround = true; + this.shake = 7; + this.setCritical(false); + if (this.g.getMaterial() != Material.AIR) { + this.g.a(this.world, this.d, this.e, this.f, (Entity) this); + } + } + } + + if (this.isCritical()) { + for (j = 0; j < 4; ++j) { + this.world.addParticle("crit", this.locX + this.motX * (double) j / 4.0D, this.locY + this.motY * (double) j / 4.0D, this.locZ + this.motZ * (double) j / 4.0D, -this.motX, -this.motY + 0.2D, -this.motZ); + } + } + + this.locX += this.motX; + this.locY += this.motY; + this.locZ += this.motZ; + f2 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + this.yaw = (float) (Math.atan2(this.motX, this.motZ) * 180.0D / 3.1415927410125732D); + + for (this.pitch = (float) (Math.atan2(this.motY, (double) f2) * 180.0D / 3.1415927410125732D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + float f4 = 0.99F; + + f1 = 0.05F; + if (this.M()) { + for (int l = 0; l < 4; ++l) { + f3 = 0.25F; + this.world.addParticle("bubble", this.locX - this.motX * (double) f3, this.locY - this.motY * (double) f3, this.locZ - this.motZ * (double) f3, this.motX, this.motY, this.motZ); + } + + f4 = 0.8F; + } + + if (this.L()) { + this.extinguish(); + } + + this.motX *= (double) f4; + this.motY *= (double) f4; + this.motZ *= (double) f4; + this.motY -= (double) f1; + this.setPosition(this.locX, this.locY, this.locZ); + this.I(); + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("xTile", (short) this.d); + nbttagcompound.setShort("yTile", (short) this.e); + nbttagcompound.setShort("zTile", (short) this.f); + nbttagcompound.setShort("life", (short) this.at); + nbttagcompound.setByte("inTile", (byte) Block.getId(this.g)); + nbttagcompound.setByte("inData", (byte) this.h); + nbttagcompound.setByte("shake", (byte) this.shake); + nbttagcompound.setByte("inGround", (byte) (this.inGround ? 1 : 0)); + nbttagcompound.setByte("pickup", (byte) this.fromPlayer); + nbttagcompound.setDouble("damage", this.damage); + } + + public void a(NBTTagCompound nbttagcompound) { + this.d = nbttagcompound.getShort("xTile"); + this.e = nbttagcompound.getShort("yTile"); + this.f = nbttagcompound.getShort("zTile"); + this.at = nbttagcompound.getShort("life"); + this.g = Block.getById(nbttagcompound.getByte("inTile") & 255); + this.h = nbttagcompound.getByte("inData") & 255; + this.shake = nbttagcompound.getByte("shake") & 255; + this.inGround = nbttagcompound.getByte("inGround") == 1; + if (nbttagcompound.hasKeyOfType("damage", 99)) { + this.damage = nbttagcompound.getDouble("damage"); + } + + if (nbttagcompound.hasKeyOfType("pickup", 99)) { + this.fromPlayer = nbttagcompound.getByte("pickup"); + } else if (nbttagcompound.hasKeyOfType("player", 99)) { + this.fromPlayer = nbttagcompound.getBoolean("player") ? 1 : 0; + } + } + + public void b_(EntityHuman entityhuman) { + if (!this.world.isStatic && this.inGround && this.shake <= 0) { + // CraftBukkit start + ItemStack itemstack = new ItemStack(Items.ARROW); + if (this.fromPlayer == 1 && entityhuman.inventory.canHold(itemstack) > 0) { + EntityItem item = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack); + + PlayerPickupItemEvent event = new PlayerPickupItemEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), new org.bukkit.craftbukkit.entity.CraftItem(this.world.getServer(), this, item), 0); + // event.setCancelled(!entityhuman.canPickUpLoot); TODO + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end + + boolean flag = this.fromPlayer == 1 || this.fromPlayer == 2 && entityhuman.abilities.canInstantlyBuild; + + if (this.fromPlayer == 1 && !entityhuman.inventory.pickup(new ItemStack(Items.ARROW, 1))) { + flag = false; + } + + if (flag) { + this.makeSound("random.pop", 0.2F, ((this.random.nextFloat() - this.random.nextFloat()) * 0.7F + 1.0F) * 2.0F); + entityhuman.receive(this, 1); + this.die(); + } + } + } + + protected boolean g_() { + return false; + } + + public void b(double d0) { + this.damage = d0; + } + + public double e() { + return this.damage; + } + + public void setKnockbackStrength(int i) { + this.knockbackStrength = i; + } + + public boolean av() { + return false; + } + + public void setCritical(boolean flag) { + byte b0 = this.datawatcher.getByte(16); + + if (flag) { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 | 1))); + } else { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 & -2))); + } + } + + public boolean isCritical() { + byte b0 = this.datawatcher.getByte(16); + + return (b0 & 1) != 0; + } + + // CraftBukkit start + public boolean isInGround() { + return inGround; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityBoat.java b/vspigot-server/src/main/java/net/minecraft/server/EntityBoat.java new file mode 100644 index 0000000..7c4c303 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityBoat.java @@ -0,0 +1,501 @@ +package net.minecraft.server; + +import java.util.List; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.vehicle.VehicleDamageEvent; +import org.bukkit.event.vehicle.VehicleDestroyEvent; +import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; +import org.bukkit.event.vehicle.VehicleMoveEvent; +// CraftBukkit end + +public class EntityBoat extends Entity { + + private boolean a; + private double b; + private int c; + private double d; + private double e; + private double f; + private double g; + private double h; + + // CraftBukkit start + public double maxSpeed = 0.4D; + public double occupiedDeceleration = 0.2D; + public double unoccupiedDeceleration = -1; + public boolean landBoats = false; + + @Override + public void collide(Entity entity) { + org.bukkit.entity.Entity hitEntity = (entity == null) ? null : entity.getBukkitEntity(); + + VehicleEntityCollisionEvent event = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), hitEntity); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + super.collide(entity); + } + // CraftBukkit end + + public EntityBoat(World world) { + super(world); + this.a = true; + this.b = 0.07D; + this.k = true; + this.a(1.5F, 0.6F); + this.height = this.length / 2.0F; + } + + protected boolean g_() { + return false; + } + + protected void c() { + this.datawatcher.a(17, new Integer(0)); + this.datawatcher.a(18, new Integer(1)); + this.datawatcher.a(19, new Float(0.0F)); + } + + public AxisAlignedBB h(Entity entity) { + return entity.boundingBox; + } + + public AxisAlignedBB J() { + return this.boundingBox; + } + + public boolean S() { + return true; + } + + public EntityBoat(World world, double d0, double d1, double d2) { + this(world); + this.setPosition(d0, d1 + (double) this.height, d2); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + + this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleCreateEvent((Vehicle) this.getBukkitEntity())); // CraftBukkit + } + + public double ae() { + return (double) this.length * 0.0D - 0.30000001192092896D; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else if (!this.world.isStatic && !this.dead) { + // CraftBukkit start + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + org.bukkit.entity.Entity attacker = (damagesource.getEntity() == null) ? null : damagesource.getEntity().getBukkitEntity(); + + VehicleDamageEvent event = new VehicleDamageEvent(vehicle, attacker, (double) f); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return true; + } + // f = event.getDamage(); // TODO Why don't we do this? + // CraftBukkit end + + this.c(-this.i()); + this.a(10); + this.setDamage(this.getDamage() + f * 10.0F); + this.Q(); + boolean flag = damagesource.getEntity() instanceof EntityHuman && ((EntityHuman) damagesource.getEntity()).abilities.canInstantlyBuild; + + if (flag || this.getDamage() > 40.0F) { + // CraftBukkit start + VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, attacker); + this.world.getServer().getPluginManager().callEvent(destroyEvent); + + if (destroyEvent.isCancelled()) { + this.setDamage(40F); // Maximize damage so this doesn't get triggered again right away + return true; + } + // CraftBukkit end + + if (this.passenger != null) { + this.passenger.mount(this); + } + + if (!flag) { + this.a(Items.BOAT, 1, 0.0F); + } + + this.die(); + } + + return true; + } else { + return true; + } + } + + public boolean R() { + return !this.dead; + } + + public void h() { + // CraftBukkit start + double prevX = this.locX; + double prevY = this.locY; + double prevZ = this.locZ; + float prevYaw = this.yaw; + float prevPitch = this.pitch; + // CraftBukkit end + + super.h(); + if (this.f() > 0) { + this.a(this.f() - 1); + } + + if (this.getDamage() > 0.0F) { + this.setDamage(this.getDamage() - 1.0F); + } + + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + byte b0 = 5; + double d0 = 0.0D; + + for (int i = 0; i < b0; ++i) { + double d1 = this.boundingBox.b + (this.boundingBox.e - this.boundingBox.b) * (double) (i + 0) / (double) b0 - 0.125D; + double d2 = this.boundingBox.b + (this.boundingBox.e - this.boundingBox.b) * (double) (i + 1) / (double) b0 - 0.125D; + AxisAlignedBB axisalignedbb = AxisAlignedBB.a(this.boundingBox.a, d1, this.boundingBox.c, this.boundingBox.d, d2, this.boundingBox.f); + + if (this.world.b(axisalignedbb, Material.WATER)) { + d0 += 1.0D / (double) b0; + } + } + + double d3 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + double d4; + double d5; + int j; + + if (d3 > 0.26249999999999996D) { + d4 = Math.cos((double) this.yaw * 3.141592653589793D / 180.0D); + d5 = Math.sin((double) this.yaw * 3.141592653589793D / 180.0D); + + for (j = 0; (double) j < 1.0D + d3 * 60.0D; ++j) { + double d6 = (double) (this.random.nextFloat() * 2.0F - 1.0F); + double d7 = (double) (this.random.nextInt(2) * 2 - 1) * 0.7D; + double d8; + double d9; + + if (this.random.nextBoolean()) { + d8 = this.locX - d4 * d6 * 0.8D + d5 * d7; + d9 = this.locZ - d5 * d6 * 0.8D - d4 * d7; + this.world.addParticle("splash", d8, this.locY - 0.125D, d9, this.motX, this.motY, this.motZ); + } else { + d8 = this.locX + d4 + d5 * d6 * 0.7D; + d9 = this.locZ + d5 - d4 * d6 * 0.7D; + this.world.addParticle("splash", d8, this.locY - 0.125D, d9, this.motX, this.motY, this.motZ); + } + } + } + + double d10; + double d11; + + if (this.world.isStatic && this.a) { + if (this.c > 0) { + d4 = this.locX + (this.d - this.locX) / (double) this.c; + d5 = this.locY + (this.e - this.locY) / (double) this.c; + d10 = this.locZ + (this.f - this.locZ) / (double) this.c; + d11 = MathHelper.g(this.g - (double) this.yaw); + this.yaw = (float) ((double) this.yaw + d11 / (double) this.c); + this.pitch = (float) ((double) this.pitch + (this.h - (double) this.pitch) / (double) this.c); + --this.c; + this.setPosition(d4, d5, d10); + this.b(this.yaw, this.pitch); + } else { + d4 = this.locX + this.motX; + d5 = this.locY + this.motY; + d10 = this.locZ + this.motZ; + this.setPosition(d4, d5, d10); + if (this.onGround) { + this.motX *= 0.5D; + this.motY *= 0.5D; + this.motZ *= 0.5D; + } + + this.motX *= 0.9900000095367432D; + this.motY *= 0.949999988079071D; + this.motZ *= 0.9900000095367432D; + } + } else { + if (d0 < 1.0D) { + d4 = d0 * 2.0D - 1.0D; + this.motY += 0.03999999910593033D * d4; + } else { + if (this.motY < 0.0D) { + this.motY /= 2.0D; + } + + this.motY += 0.007000000216066837D; + } + + if (this.passenger != null && this.passenger instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) this.passenger; + float f = this.passenger.yaw + -entityliving.bd * 90.0F; + + this.motX += -Math.sin((double) (f * 3.1415927F / 180.0F)) * this.b * (double) entityliving.be * 0.05000000074505806D; + this.motZ += Math.cos((double) (f * 3.1415927F / 180.0F)) * this.b * (double) entityliving.be * 0.05000000074505806D; + } + // CraftBukkit start - Support unoccupied deceleration + else if (unoccupiedDeceleration >= 0) { + this.motX *= unoccupiedDeceleration; + this.motZ *= unoccupiedDeceleration; + // Kill lingering speed + if (motX <= 0.00001) { + motX = 0; + } + if (motZ <= 0.00001) { + motZ = 0; + } + } + // CraftBukkit end + + d4 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + if (d4 > 0.35D) { + d5 = 0.35D / d4; + this.motX *= d5; + this.motZ *= d5; + d4 = 0.35D; + } + + if (d4 > d3 && this.b < 0.35D) { + this.b += (0.35D - this.b) / 35.0D; + if (this.b > 0.35D) { + this.b = 0.35D; + } + } else { + this.b -= (this.b - 0.07D) / 35.0D; + if (this.b < 0.07D) { + this.b = 0.07D; + } + } + + int k; + + for (k = 0; k < 4; ++k) { + int l = MathHelper.floor(this.locX + ((double) (k % 2) - 0.5D) * 0.8D); + + j = MathHelper.floor(this.locZ + ((double) (k / 2) - 0.5D) * 0.8D); + + for (int i1 = 0; i1 < 2; ++i1) { + int j1 = MathHelper.floor(this.locY) + i1; + Block block = this.world.getType(l, j1, j); + + if (block == Blocks.SNOW) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(this, l, j1, j, Blocks.AIR, 0).isCancelled()) { + continue; + } + // CraftBukkit end + this.world.setAir(l, j1, j); + this.positionChanged = false; + } else if (block == Blocks.WATER_LILY) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(this, l, j1, j, Blocks.AIR, 0).isCancelled()) { + continue; + } + // CraftBukkit end + this.world.setAir(l, j1, j, true); + this.positionChanged = false; + } + } + } + + if (this.onGround && !this.landBoats) { // CraftBukkit + this.motX *= 0.5D; + this.motY *= 0.5D; + this.motZ *= 0.5D; + } + + this.move(this.motX, this.motY, this.motZ); + if (this.positionChanged && d3 > 0.2D) { + if (!this.world.isStatic && !this.dead) { + // CraftBukkit start + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, null); + this.world.getServer().getPluginManager().callEvent(destroyEvent); + if (!destroyEvent.isCancelled()) { + this.die(); + + breakNaturally(); // PaperSpigot - Customizable boat drops + } + // CraftBukkit end + } + } else { + this.motX *= 0.9900000095367432D; + this.motY *= 0.949999988079071D; + this.motZ *= 0.9900000095367432D; + } + + this.pitch = 0.0F; + d5 = (double) this.yaw; + d10 = this.lastX - this.locX; + d11 = this.lastZ - this.locZ; + if (d10 * d10 + d11 * d11 > 0.001D) { + d5 = (double) ((float) (Math.atan2(d11, d10) * 180.0D / 3.141592653589793D)); + } + + double d12 = MathHelper.g(d5 - (double) this.yaw); + + if (d12 > 20.0D) { + d12 = 20.0D; + } + + if (d12 < -20.0D) { + d12 = -20.0D; + } + + this.yaw = (float) ((double) this.yaw + d12); + this.b(this.yaw, this.pitch); + + // CraftBukkit start + org.bukkit.Server server = this.world.getServer(); + org.bukkit.World bworld = this.world.getWorld(); + + Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch); + Location to = new Location(bworld, this.locX, this.locY, this.locZ, this.yaw, this.pitch); + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + + server.getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); + + if (!from.equals(to)) { + VehicleMoveEvent event = new VehicleMoveEvent(vehicle, from, to); + server.getPluginManager().callEvent(event); + } + // CraftBukkit end + + if (!this.world.isStatic) { + List list = this.world.getEntities(this, this.boundingBox.grow(0.20000000298023224D, 0.0D, 0.20000000298023224D)); + + if (list != null && !list.isEmpty()) { + for (int k1 = 0; k1 < list.size(); ++k1) { + Entity entity = (Entity) list.get(k1); + + if (entity != this.passenger && entity.S() && entity instanceof EntityBoat) { + entity.collide(this); + } + } + } + + if (this.passenger != null && this.passenger.dead) { + this.passenger.vehicle = null; // CraftBukkit + this.passenger = null; + } + } + } + } + + public void ac() { + if (this.passenger != null) { + double d0 = Math.cos((double) this.yaw * 3.141592653589793D / 180.0D) * 0.4D; + double d1 = Math.sin((double) this.yaw * 3.141592653589793D / 180.0D) * 0.4D; + + this.passenger.setPosition(this.locX + d0, this.locY + this.ae() + this.passenger.ad(), this.locZ + d1); + } + } + + protected void b(NBTTagCompound nbttagcompound) {} + + protected void a(NBTTagCompound nbttagcompound) {} + + public boolean c(EntityHuman entityhuman) { + if (this.passenger != null && this.passenger instanceof EntityHuman && this.passenger != entityhuman) { + return true; + } else { + if (!this.world.isStatic) { + entityhuman.mount(this); + } + + return true; + } + } + + protected void a(double d0, boolean flag) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (flag) { + if (this.fallDistance > 3.0F) { + this.b(this.fallDistance); + if (!this.world.isStatic && !this.dead) { + // CraftBukkit start + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, null); + this.world.getServer().getPluginManager().callEvent(destroyEvent); + if (!destroyEvent.isCancelled()) { + this.die(); + + breakNaturally(); // PaperSpigot - Customizable boat drops + } + // CraftBukkit end + } + + this.fallDistance = 0.0F; + } + } else if (this.world.getType(i, j - 1, k).getMaterial() != Material.WATER && d0 < 0.0D) { + this.fallDistance = (float) ((double) this.fallDistance - d0); + } + } + + public void setDamage(float f) { + this.datawatcher.watch(19, Float.valueOf(f)); + } + + public float getDamage() { + return this.datawatcher.getFloat(19); + } + + public void a(int i) { + this.datawatcher.watch(17, Integer.valueOf(i)); + } + + public int f() { + return this.datawatcher.getInt(17); + } + + public void c(int i) { + this.datawatcher.watch(18, Integer.valueOf(i)); + } + + public int i() { + return this.datawatcher.getInt(18); + } + + /** + * PaperSpigot - Handles boat drops depending on the user's config setting + */ + public void breakNaturally() { + if (this.world.paperSpigotConfig.boatsDropBoats) { + this.a(Items.BOAT, 1, 0.0F); + } else { + for (int k = 0; k < 3; ++k) { + this.a(Item.getItemOf(Blocks.WOOD), 1, 0.0F); + } + + for (int k = 0; k < 2; ++k) { + this.a(Items.STICK, 1, 0.0F); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityChicken.java b/vspigot-server/src/main/java/net/minecraft/server/EntityChicken.java new file mode 100644 index 0000000..5ff30a1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityChicken.java @@ -0,0 +1,168 @@ +package net.minecraft.server; + +public class EntityChicken extends EntityAnimal { + + public float bp; + public float bq; + public float br; + public float bs; + public float bt = 1.0F; + public int bu; + public boolean bv; + + public EntityChicken(World world) { + super(world); + this.a(0.3F, 0.7F); + this.bu = this.random.nextInt(6000) + 6000; + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.4D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.0D, Items.SEEDS, false)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.1D)); + this.goalSelector.a(5, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this)); + } + + @Override + public void h() { + super.h(); + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + } + + public boolean bk() { + return true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(4.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.25D); + } + + public void e() { + // CraftBukkit start + if (this.isChickenJockey()) { + this.persistent = !this.isTypeNotPersistent(); + } + // CraftBukkit end + super.e(); + this.bs = this.bp; + this.br = this.bq; + this.bq = (float) ((double) this.bq + (double) (this.onGround ? -1 : 4) * 0.3D); + if (this.bq < 0.0F) { + this.bq = 0.0F; + } + + if (this.bq > 1.0F) { + this.bq = 1.0F; + } + + if (!this.onGround && this.bt < 1.0F) { + this.bt = 1.0F; + } + + this.bt = (float) ((double) this.bt * 0.9D); + if (!this.onGround && this.motY < 0.0D) { + this.motY *= 0.6D; + } + + this.bp += this.bt * 2.0F; + if (!this.world.isStatic && !this.isBaby() && !this.isChickenJockey() && --this.bu <= 0) { + this.makeSound("mob.chicken.plop", 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + this.a(Items.EGG, 1); + this.bu = this.random.nextInt(6000) + 6000; + } + } + + protected void b(float f) {} + + protected String t() { + return "mob.chicken.say"; + } + + protected String aT() { + return "mob.chicken.hurt"; + } + + protected String aU() { + return "mob.chicken.hurt"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.chicken.step", 0.15F, 1.0F); + } + + protected Item getLoot() { + return Items.FEATHER; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(3) + this.random.nextInt(1 + i); + + for (int k = 0; k < j; ++k) { + this.a(Items.FEATHER, 1); + } + + if (this.isBurning()) { + this.a(Items.COOKED_CHICKEN, 1); + } else { + this.a(Items.RAW_CHICKEN, 1); + } + } + + public EntityChicken b(EntityAgeable entityageable) { + return new EntityChicken(this.world); + } + + public boolean c(ItemStack itemstack) { + return itemstack != null && itemstack.getItem() instanceof ItemSeeds; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.bv = nbttagcompound.getBoolean("IsChickenJockey"); + } + + protected int getExpValue(EntityHuman entityhuman) { + return this.isChickenJockey() ? 10 : super.getExpValue(entityhuman); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("IsChickenJockey", this.bv); + } + + protected boolean isTypeNotPersistent() { + return this.isChickenJockey() && this.passenger == null; + } + + public void ac() { + super.ac(); + float f = MathHelper.sin(this.aM * 3.1415927F / 180.0F); + float f1 = MathHelper.cos(this.aM * 3.1415927F / 180.0F); + float f2 = 0.1F; + float f3 = 0.0F; + + this.passenger.setPosition(this.locX + (double) (f2 * f), this.locY + (double) (this.length * 0.5F) + this.passenger.ad() + (double) f3, this.locZ - (double) (f2 * f1)); + if (this.passenger instanceof EntityLiving) { + ((EntityLiving) this.passenger).aM = this.aM; + } + } + + public boolean isChickenJockey() { + return this.bv; + } + + public void i(boolean flag) { + this.bv = flag; + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.b(entityageable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityCow.java b/vspigot-server/src/main/java/net/minecraft/server/EntityCow.java new file mode 100644 index 0000000..0b8d7f7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityCow.java @@ -0,0 +1,120 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +// CraftBukkit end + +public class EntityCow extends EntityAnimal { + + public EntityCow(World world) { + super(world); + this.a(0.9F, 1.3F); + this.getNavigation().a(true); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 2.0D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.25D, Items.WHEAT, false)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.25D)); + this.goalSelector.a(5, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this)); + } + + @Override + public void h() { + super.h(); + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + } + + public boolean bk() { + return true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.20000000298023224D); + } + + protected String t() { + return "mob.cow.say"; + } + + protected String aT() { + return "mob.cow.hurt"; + } + + protected String aU() { + return "mob.cow.hurt"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.cow.step", 0.15F, 1.0F); + } + + protected float bf() { + return 0.4F; + } + + protected Item getLoot() { + return Items.LEATHER; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(3) + this.random.nextInt(1 + i); + + int k; + + for (k = 0; k < j; ++k) { + this.a(Items.LEATHER, 1); + } + + j = this.random.nextInt(3) + 1 + this.random.nextInt(1 + i); + + for (k = 0; k < j; ++k) { + if (this.isBurning()) { + this.a(Items.COOKED_BEEF, 1); + } else { + this.a(Items.RAW_BEEF, 1); + } + } + } + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() == Items.BUCKET && !entityhuman.abilities.canInstantlyBuild) { + // CraftBukkit start - Got milk? + org.bukkit.Location loc = this.getBukkitEntity().getLocation(); + org.bukkit.event.player.PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), -1, itemstack, Items.MILK_BUCKET); + + if (event.isCancelled()) { + return false; + } + + if (--itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, CraftItemStack.asNMSCopy(event.getItemStack())); + } else if (!entityhuman.inventory.pickup(new ItemStack(Items.MILK_BUCKET))) { + entityhuman.drop(CraftItemStack.asNMSCopy(event.getItemStack()), false); + } + // CraftBukkit end + + return true; + } else { + return super.a(entityhuman); + } + } + + public EntityCow b(EntityAgeable entityageable) { + return new EntityCow(this.world); + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.b(entityageable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityCreature.java b/vspigot-server/src/main/java/net/minecraft/server/EntityCreature.java new file mode 100644 index 0000000..7ea9dc1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityCreature.java @@ -0,0 +1,378 @@ +package net.minecraft.server; + +import java.util.UUID; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.EntityUnleashEvent; +// CraftBukkit end + +// Poweruser start +import net.valorhcf.pathsearch.jobs.PathSearchJob; +import net.valorhcf.pathsearch.jobs.PathSearchJobEntity; +import net.valorhcf.pathsearch.jobs.PathSearchJobPosition; +import net.valorhcf.pathsearch.jobs.PathSearchQueuingManager; +// Poweruser end + +public abstract class EntityCreature extends EntityInsentient { + + public static final UUID h = UUID.fromString("E199AD21-BA8A-4C53-8D13-6182D5C69D3A"); + public static final AttributeModifier i = (new AttributeModifier(h, "Fleeing speed bonus", 2.0D, 2)).a(false); + public PathEntity pathEntity; // CraftBukkit - private -> public + public Entity target; // CraftBukkit - protected -> public + protected boolean bn; + protected int bo; + private ChunkCoordinates bq = new ChunkCoordinates(0, 0, 0); + private float br = -1.0F; + private PathfinderGoal bs = new PathfinderGoalMoveTowardsRestriction(this, 1.0D); + private boolean bt; + + // Poweruser start + private PathSearchQueuingManager queuingManager = new PathSearchQueuingManager(); + private boolean searchIssued = false; + private PathEntity returnedPathEntity; + + public boolean isSearchingForAPath() { + return this.queuingManager.hasAsyncSearchIssued(); + } + + public void cancelSearch(PathSearchJob pathSearch) { + this.queuingManager.checkLastSearchResult(pathSearch); + pathSearch.cleanup(); + } + + private void issueSearch(int x, int y, int z, float range) { + this.queuingManager.queueSearch(new PathSearchJobPosition(this, x, y, z, range, true, false, false, true)); + } + + private void issueSearch(Entity target, float range) { + this.queuingManager.queueSearch(new PathSearchJobEntity(this, target, range, true, false, false, true)); + } + + public void setSearchResult(PathSearchJobEntity pathSearchJobEntity, Entity target, PathEntity pathentity) { + this.queuingManager.checkLastSearchResult(pathSearchJobEntity); + if(this.target != null && this.target.equals(target)) { + this.returnedPathEntity = pathentity; + } + } + + public void setSearchResult(PathSearchJobPosition pathSearchJobPosition, PathEntity pathentity) { + this.queuingManager.checkLastSearchResult(pathSearchJobPosition); + this.returnedPathEntity = pathentity; + } + // Poweruser end + + // MineHQ start + private long lastRayTraceTick = -1L; + private boolean lastRayTraceResult = false; + // MineHQ end + + public EntityCreature(World world) { + super(world); + } + + protected boolean bP() { + return false; + } + + protected void bq() { + this.world.methodProfiler.a("ai"); + if (this.bo > 0 && --this.bo == 0) { + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.d); + + attributeinstance.b(i); + } + + this.bn = this.bP(); + float f11 = 16.0F; + + if (this.target == null) { + // CraftBukkit start + Entity target = this.findTarget(); + if (target != null) { + EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), target.getBukkitEntity(), EntityTargetEvent.TargetReason.CLOSEST_PLAYER); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.target = null; + } else { + this.target = ((CraftEntity) event.getTarget()).getHandle(); + } + } + } + // CraftBukkit end + + if (this.target != null) { + //this.pathEntity = this.world.findPath(this, this.target, f11, true, false, false, true); + this.issueSearch(this.target, f11); // Poweruser + } + } else if (this.target.isAlive()) { + float f1 = this.target.e((Entity) this); + + // MineHQ start - don't constantly ray trace + if (this.lastRayTraceTick + 50 < this.ticksLived) { + this.lastRayTraceTick = this.ticksLived; + this.lastRayTraceResult = this.hasLineOfSight(this.target); + } + + if (this.lastRayTraceResult) { + // MineHQ end + this.a(this.target, f1); + } + } else { + // CraftBukkit start + EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), null, EntityTargetEvent.TargetReason.TARGET_DIED); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.target = null; + } else { + this.target = ((CraftEntity) event.getTarget()).getHandle(); + } + } + // CraftBukkit end + } + + if (this.target instanceof EntityPlayer && ((EntityPlayer) this.target).playerInteractManager.isCreative()) { + this.target = null; + } + + this.world.methodProfiler.b(); + + // Poweruser start + if(this.returnedPathEntity != null) { + this.pathEntity = this.returnedPathEntity; + this.returnedPathEntity = null; + } + // Poweruser end + + if (!this.bn && this.target != null && (this.pathEntity == null || this.random.nextInt(20) == 0)) { + //this.pathEntity = this.world.findPath(this, this.target, f11, true, false, false, true); + this.issueSearch(this.target, f11); // Poweruser + } else if (!this.bn && (this.pathEntity == null && this.random.nextInt(180) == 0 || this.random.nextInt(120) == 0 || this.bo > 0) && this.aU < 100) { + this.bQ(); + } + + int i = MathHelper.floor(this.boundingBox.b + 0.5D); + boolean flag = this.M(); + boolean flag1 = this.P(); + + this.pitch = 0.0F; + if (this.pathEntity != null && this.random.nextInt(100) != 0) { + this.world.methodProfiler.a("followpath"); + Vec3D vec3d = this.pathEntity.a((Entity) this); + double d0 = (double) (this.width * 2.0F); + + while (vec3d != null && vec3d.d(this.locX, vec3d.b, this.locZ) < d0 * d0) { + this.pathEntity.a(); + if (this.pathEntity.b()) { + vec3d = null; + this.pathEntity = null; + } else { + vec3d = this.pathEntity.a((Entity) this); + } + } + + this.bc = false; + if (vec3d != null) { + double d1 = vec3d.a - this.locX; + double d2 = vec3d.c - this.locZ; + double d3 = vec3d.b - (double) i; + // CraftBukkit - Math -> TrigMath + float f2 = (float) (org.bukkit.craftbukkit.TrigMath.atan2(d2, d1) * 180.0D / 3.1415927410125732D) - 90.0F; + float f3 = MathHelper.g(f2 - this.yaw); + + this.be = (float) this.getAttributeInstance(GenericAttributes.d).getValue(); + if (f3 > 30.0F) { + f3 = 30.0F; + } + + if (f3 < -30.0F) { + f3 = -30.0F; + } + + this.yaw += f3; + if (this.bn && this.target != null) { + double d4 = this.target.locX - this.locX; + double d5 = this.target.locZ - this.locZ; + float f4 = this.yaw; + + this.yaw = (float) (Math.atan2(d5, d4) * 180.0D / 3.1415927410125732D) - 90.0F; + f3 = (f4 - this.yaw + 90.0F) * 3.1415927F / 180.0F; + this.bd = -MathHelper.sin(f3) * this.be * 1.0F; + this.be = MathHelper.cos(f3) * this.be * 1.0F; + } + + if (d3 > 0.0D) { + this.bc = true; + } + } + + if (this.target != null) { + this.a(this.target, 30.0F, 30.0F); + } + + if (this.positionChanged && !this.bS()) { + this.bc = true; + } + + if (this.random.nextFloat() < 0.8F && (flag || flag1)) { + this.bc = true; + } + + this.world.methodProfiler.b(); + } else { + super.bq(); + this.pathEntity = null; + } + } + + protected void bQ() { + this.world.methodProfiler.a("stroll"); + boolean flag = false; + int i = -1; + int j = -1; + int k = -1; + float f = -99999.0F; + + for (int l = 0; l < 10; ++l) { + int i1 = MathHelper.floor(this.locX + (double) this.random.nextInt(13) - 6.0D); + int j1 = MathHelper.floor(this.locY + (double) this.random.nextInt(7) - 3.0D); + int k1 = MathHelper.floor(this.locZ + (double) this.random.nextInt(13) - 6.0D); + float f1 = this.a(i1, j1, k1); + + if (f1 > f) { + f = f1; + i = i1; + j = j1; + k = k1; + flag = true; + } + } + + if (flag) { + //this.pathEntity = this.world.a(this, i, j, k, 10.0F, true, false, false, true); + this.issueSearch(i, j, k, 10.0F); // Poweruser + } + + this.world.methodProfiler.b(); + } + + protected void a(Entity entity, float f) {} + + public float a(int i, int j, int k) { + return 0.0F; + } + + protected Entity findTarget() { + return null; + } + + public boolean canSpawn() { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.boundingBox.b); + int k = MathHelper.floor(this.locZ); + + return super.canSpawn() && this.a(i, j, k) >= 0.0F; + } + + public boolean bS() { + return this.pathEntity != null; + } + + public void setPathEntity(PathEntity pathentity) { + this.pathEntity = pathentity; + } + + public Entity bT() { + return this.target; + } + + public void setTarget(Entity entity) { + this.target = entity; + } + + public boolean bU() { + return this.b(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)); + } + + public boolean b(int i, int j, int k) { + return this.br == -1.0F ? true : this.bq.e(i, j, k) < this.br * this.br; + } + + public void a(int i, int j, int k, int l) { + this.bq.b(i, j, k); + this.br = (float) l; + } + + public ChunkCoordinates bV() { + return this.bq; + } + + public float bW() { + return this.br; + } + + public void bX() { + this.br = -1.0F; + } + + public boolean bY() { + return this.br != -1.0F; + } + + protected void bL() { + super.bL(); + if (this.bN() && this.getLeashHolder() != null && this.getLeashHolder().world == this.world) { + Entity entity = this.getLeashHolder(); + + this.a((int) entity.locX, (int) entity.locY, (int) entity.locZ, 5); + float f = this.e(entity); + + if (this instanceof EntityTameableAnimal && ((EntityTameableAnimal) this).isSitting()) { + if (f > 10.0F) { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.unleash(true, true); + } + + return; + } + + if (!this.bt) { + this.goalSelector.a(2, this.bs); + this.getNavigation().a(false); + this.bt = true; + } + + this.o(f); + if (f > 4.0F) { + this.getNavigation().a(entity, 1.0D); + } + + if (f > 6.0F) { + double d0 = (entity.locX - this.locX) / (double) f; + double d1 = (entity.locY - this.locY) / (double) f; + double d2 = (entity.locZ - this.locZ) / (double) f; + + this.motX += d0 * Math.abs(d0) * 0.4D; + this.motY += d1 * Math.abs(d1) * 0.4D; + this.motZ += d2 * Math.abs(d2) * 0.4D; + } + + if (f > 10.0F) { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.unleash(true, true); + } + } else if (!this.bN() && this.bt) { + this.bt = false; + this.goalSelector.a(this.bs); + this.getNavigation().a(true); + this.bX(); + } + } + + protected void o(float f) {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityCreeper.java b/vspigot-server/src/main/java/net/minecraft/server/EntityCreeper.java new file mode 100644 index 0000000..53fd5f7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityCreeper.java @@ -0,0 +1,233 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.ExplosionPrimeEvent; +// CraftBukkit end + +public class EntityCreeper extends EntityMonster { + + private int bp; + private int fuseTicks; + private int maxFuseTicks = 30; + private int explosionRadius = 3; + private int record = -1; // CraftBukkit + + public EntityCreeper(World world) { + super(world); + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalSwell(this)); + this.goalSelector.a(3, new PathfinderGoalAvoidPlayer(this, EntityOcelot.class, 6.0F, 1.0D, 1.2D)); + this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, 1.0D, false)); + this.goalSelector.a(5, new PathfinderGoalRandomStroll(this, 0.8D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(6, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 0, true)); + this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false)); + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.d).setValue(0.25D); + } + + public boolean bk() { + return true; + } + + public int ax() { + return this.getGoalTarget() == null ? 3 : 3 + (int) (this.getHealth() - 1.0F); + } + + protected void b(float f) { + super.b(f); + this.fuseTicks = (int) ((float) this.fuseTicks + f * 1.5F); + if (this.fuseTicks > this.maxFuseTicks - 5) { + this.fuseTicks = this.maxFuseTicks - 5; + } + } + + protected void c() { + super.c(); + this.datawatcher.a(16, Byte.valueOf((byte) -1)); + this.datawatcher.a(17, Byte.valueOf((byte) 0)); + this.datawatcher.a(18, Byte.valueOf((byte) 0)); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.datawatcher.getByte(17) == 1) { + nbttagcompound.setBoolean("powered", true); + } + + nbttagcompound.setShort("Fuse", (short) this.maxFuseTicks); + nbttagcompound.setByte("ExplosionRadius", (byte) this.explosionRadius); + nbttagcompound.setBoolean("ignited", this.cc()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.datawatcher.watch(17, Byte.valueOf((byte) (nbttagcompound.getBoolean("powered") ? 1 : 0))); + if (nbttagcompound.hasKeyOfType("Fuse", 99)) { + this.maxFuseTicks = nbttagcompound.getShort("Fuse"); + } + + if (nbttagcompound.hasKeyOfType("ExplosionRadius", 99)) { + this.explosionRadius = nbttagcompound.getByte("ExplosionRadius"); + } + + if (nbttagcompound.getBoolean("ignited")) { + this.cd(); + } + } + + public void h() { + if (this.isAlive()) { + this.bp = this.fuseTicks; + if (this.cc()) { + this.a(1); + } + + int i = this.cb(); + + if (i > 0 && this.fuseTicks == 0) { + this.makeSound("creeper.primed", 1.0F, 0.5F); + } + + this.fuseTicks += i; + if (this.fuseTicks < 0) { + this.fuseTicks = 0; + } + + if (this.fuseTicks >= this.maxFuseTicks) { + this.fuseTicks = this.maxFuseTicks; + this.ce(); + } + } + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + + super.h(); + } + + protected String aT() { + return "mob.creeper.say"; + } + + protected String aU() { + return "mob.creeper.death"; + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // CraftBukkit - Moved to end + if (damagesource.getEntity() instanceof EntitySkeleton) { + int i = Item.getId(Items.RECORD_1); + int j = Item.getId(Items.RECORD_12); + int k = i + this.random.nextInt(j - i + 1); + + // CraftBukkit start - Store record for now, drop in dropDeathLoot + // this.a(Item.getById(k), 1); + this.record = k; + // CraftBukkit end + } + + super.die(damagesource); // CraftBukkit - Moved from above + } + + // CraftBukkit start - Whole method + protected void dropDeathLoot(boolean flag, int i) { + super.dropDeathLoot(flag, i); + + // Drop a music disc? + if (this.record != -1) { + this.a(Item.getById(this.record), 1); + this.record = -1; + } + } + // CraftBukkit end + + public boolean n(Entity entity) { + return true; + } + + public boolean isPowered() { + return this.datawatcher.getByte(17) == 1; + } + + protected Item getLoot() { + return Items.SULPHUR; + } + + public int cb() { + return this.datawatcher.getByte(16); + } + + public void a(int i) { + this.datawatcher.watch(16, Byte.valueOf((byte) i)); + } + + public void a(EntityLightning entitylightning) { + super.a(entitylightning); + // CraftBukkit start + if (CraftEventFactory.callCreeperPowerEvent(this, entitylightning, org.bukkit.event.entity.CreeperPowerEvent.PowerCause.LIGHTNING).isCancelled()) { + return; + } + + this.setPowered(true); + } + + public void setPowered(boolean powered) { + if (!powered) { + this.datawatcher.watch(17, Byte.valueOf((byte) 0)); + } else { + this.datawatcher.watch(17, Byte.valueOf((byte) 1)); + } + // CraftBukkit end + } + + protected boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() == Items.FLINT_AND_STEEL) { + this.world.makeSound(this.locX + 0.5D, this.locY + 0.5D, this.locZ + 0.5D, "fire.ignite", 1.0F, this.random.nextFloat() * 0.4F + 0.8F); + entityhuman.ba(); + if (!this.world.isStatic) { + this.cd(); + itemstack.damage(1, entityhuman); + return true; + } + } + + return super.a(entityhuman); + } + + private void ce() { + if (!this.world.isStatic) { + boolean flag = this.world.getGameRules().getBoolean("mobGriefing"); + + // CraftBukkit start + float radius = this.isPowered() ? 6.0F : 3.0F; + + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), radius, false); + this.world.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), flag); + this.die(); + } else { + this.fuseTicks = 0; + } + // CraftBukkit end + } + } + + public boolean cc() { + return this.datawatcher.getByte(18) != 0; + } + + public void cd() { + this.datawatcher.watch(18, Byte.valueOf((byte) 1)); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java b/vspigot-server/src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java new file mode 100644 index 0000000..ca4fffb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java @@ -0,0 +1,34 @@ +package net.minecraft.server; + +public class EntityDamageSourceIndirect extends EntityDamageSource { + + private Entity owner; + + public EntityDamageSourceIndirect(String s, Entity entity, Entity entity1) { + super(s, entity); + this.owner = entity1; + } + + public Entity i() { + return this.p; + } + + public Entity getEntity() { + return this.owner; + } + + public IChatBaseComponent getLocalizedDeathMessage(EntityLiving entityliving) { + IChatBaseComponent ichatbasecomponent = this.owner == null ? this.p.getScoreboardDisplayName() : this.owner.getScoreboardDisplayName(); + ItemStack itemstack = this.owner instanceof EntityLiving ? ((EntityLiving) this.owner).be() : null; + String s = "death.attack." + this.translationIndex; + String s1 = s + ".item"; + + return itemstack != null && itemstack.hasName() && LocaleI18n.c(s1) ? new ChatMessage(s1,entityliving.getScoreboardDisplayName(),ichatbasecomponent,itemstack.E()) : new ChatMessage(s,entityliving.getScoreboardDisplayName(),ichatbasecomponent); + } + + // CraftBukkit start + public Entity getProximateDamageSource() { + return super.getEntity(); + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityEgg.java b/vspigot-server/src/main/java/net/minecraft/server/EntityEgg.java new file mode 100644 index 0000000..f999ddd --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityEgg.java @@ -0,0 +1,68 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerEggThrowEvent; +// CraftBukkit end + +public class EntityEgg extends EntityProjectile { + + public EntityEgg(World world) { + super(world); + } + + public EntityEgg(World world, EntityLiving entityliving) { + super(world, entityliving); + } + + public EntityEgg(World world, double d0, double d1, double d2) { + super(world, d0, d1, d2); + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (movingobjectposition.entity != null) { + movingobjectposition.entity.damageEntity(DamageSource.projectile(this, this.getShooter()), 0.0F); + } + + // CraftBukkit start - Fire PlayerEggThrowEvent + boolean hatching = !this.world.isStatic && this.random.nextInt(8) == 0; + int numHatching = (this.random.nextInt(32) == 0) ? 4 : 1; + if (!hatching) { + numHatching = 0; + } + + EntityType hatchingType = EntityType.CHICKEN; + + Entity shooter = this.getShooter(); + if (shooter instanceof EntityPlayer) { + Player player = (shooter == null) ? null : (Player) shooter.getBukkitEntity(); + + PlayerEggThrowEvent event = new PlayerEggThrowEvent(player, (org.bukkit.entity.Egg) this.getBukkitEntity(), hatching, (byte) numHatching, hatchingType); + this.world.getServer().getPluginManager().callEvent(event); + + hatching = event.isHatching(); + numHatching = event.getNumHatches(); + hatchingType = event.getHatchingType(); + } + + if (hatching) { + for (int k = 0; k < numHatching; k++) { + org.bukkit.entity.Entity entity = world.getWorld().spawn(new org.bukkit.Location(world.getWorld(), this.locX, this.locY, this.locZ, this.yaw, 0.0F), hatchingType.getEntityClass(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); + if (entity instanceof Ageable) { + ((Ageable) entity).setBaby(); + } + } + } + // CraftBukkit end + + for (int j = 0; j < 8; ++j) { + this.world.addParticle("snowballpoof", this.locX, this.locY, this.locZ, 0.0D, 0.0D, 0.0D); + } + + if (!this.world.isStatic) { + this.die(); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityEnderCrystal.java b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderCrystal.java new file mode 100644 index 0000000..cb023e2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderCrystal.java @@ -0,0 +1,88 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.ExplosionPrimeEvent; +// CraftBukkit end + +public class EntityEnderCrystal extends Entity { + + public int a; + public int b; + + public EntityEnderCrystal(World world) { + super(world); + this.k = true; + this.a(2.0F, 2.0F); + this.height = this.length / 2.0F; + this.b = 5; + this.a = this.random.nextInt(100000); + } + + protected boolean g_() { + return false; + } + + protected void c() { + this.datawatcher.a(8, Integer.valueOf(this.b)); + } + + public void h() { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + ++this.a; + this.datawatcher.watch(8, Integer.valueOf(this.b)); + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (this.world.worldProvider instanceof WorldProviderTheEnd && this.world.getType(i, j, k) != Blocks.FIRE) { + // CraftBukkit start + if (!CraftEventFactory.callBlockIgniteEvent(this.world, i, j, k, this).isCancelled()) { + this.world.setTypeUpdate(i, j, k, Blocks.FIRE); + } + // CraftBukkit end + } + } + + protected void b(NBTTagCompound nbttagcompound) {} + + protected void a(NBTTagCompound nbttagcompound) {} + + public boolean R() { + return true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + if (!this.dead && !this.world.isStatic) { + // CraftBukkit start - All non-living entities need this + if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { + return false; + } + // CraftBukkit end + + this.b = 0; + if (this.b <= 0) { + this.die(); + if (!this.world.isStatic) { + // CraftBukkit start + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 6.0F, false); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.dead = false; + return false; + } + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), true); + // CraftBukkit end + } + } + } + + return true; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityEnderDragon.java b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderDragon.java new file mode 100644 index 0000000..c8f8331 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderDragon.java @@ -0,0 +1,665 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.util.BlockStateListPopulator; +import org.bukkit.event.entity.EntityCreatePortalEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.Bukkit; +// CraftBukkit end + +public class EntityEnderDragon extends EntityInsentient implements IComplex, IMonster { + + public double h; + public double i; + public double bm; + public double[][] bn = new double[64][3]; + public int bo = -1; + public EntityComplexPart[] children; + public EntityComplexPart bq; + public EntityComplexPart br; + public EntityComplexPart bs; + public EntityComplexPart bt; + public EntityComplexPart bu; + public EntityComplexPart bv; + public EntityComplexPart bw; + public float bx; + public float by; + public boolean bz; + public boolean bA; + private Entity bD; + public int bB; + public EntityEnderCrystal bC; + private Explosion explosionSource = new Explosion(null, this, Double.NaN, Double.NaN, Double.NaN, Float.NaN); // CraftBukkit - reusable source for CraftTNTPrimed.getSource() + + public EntityEnderDragon(World world) { + super(world); + this.children = new EntityComplexPart[] { this.bq = new EntityComplexPart(this, "head", 6.0F, 6.0F), this.br = new EntityComplexPart(this, "body", 8.0F, 8.0F), this.bs = new EntityComplexPart(this, "tail", 4.0F, 4.0F), this.bt = new EntityComplexPart(this, "tail", 4.0F, 4.0F), this.bu = new EntityComplexPart(this, "tail", 4.0F, 4.0F), this.bv = new EntityComplexPart(this, "wing", 4.0F, 4.0F), this.bw = new EntityComplexPart(this, "wing", 4.0F, 4.0F)}; + this.setHealth(this.getMaxHealth()); + this.a(16.0F, 8.0F); + this.X = true; + this.fireProof = true; + this.i = 100.0D; + this.ak = true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(200.0D); + } + + protected void c() { + super.c(); + } + + public double[] b(int i, float f) { + if (this.getHealth() <= 0.0F) { + f = 0.0F; + } + + f = 1.0F - f; + int j = this.bo - i * 1 & 63; + int k = this.bo - i * 1 - 1 & 63; + double[] adouble = new double[3]; + double d0 = this.bn[j][0]; + double d1 = MathHelper.g(this.bn[k][0] - d0); + + adouble[0] = d0 + d1 * (double) f; + d0 = this.bn[j][1]; + d1 = this.bn[k][1] - d0; + adouble[1] = d0 + d1 * (double) f; + adouble[2] = this.bn[j][2] + (this.bn[k][2] - this.bn[j][2]) * (double) f; + return adouble; + } + + public void e() { + float f; + float f1; + + if (this.world.isStatic) { + f = MathHelper.cos(this.by * 3.1415927F * 2.0F); + f1 = MathHelper.cos(this.bx * 3.1415927F * 2.0F); + if (f1 <= -0.3F && f >= -0.3F) { + this.world.a(this.locX, this.locY, this.locZ, "mob.enderdragon.wings", 5.0F, 0.8F + this.random.nextFloat() * 0.3F, false); + } + } + + this.bx = this.by; + float f2; + + if (this.getHealth() <= 0.0F) { + f = (this.random.nextFloat() - 0.5F) * 8.0F; + f1 = (this.random.nextFloat() - 0.5F) * 4.0F; + f2 = (this.random.nextFloat() - 0.5F) * 8.0F; + this.world.addParticle("largeexplode", this.locX + (double) f, this.locY + 2.0D + (double) f1, this.locZ + (double) f2, 0.0D, 0.0D, 0.0D); + } else { + this.bP(); + f = 0.2F / (MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ) * 10.0F + 1.0F); + f *= (float) Math.pow(2.0D, this.motY); + if (this.bA) { + this.by += f * 0.5F; + } else { + this.by += f; + } + + this.yaw = MathHelper.g(this.yaw); + if (this.bo < 0) { + for (int d05 = 0; d05 < this.bn.length; ++d05) { + this.bn[d05][0] = (double) this.yaw; + this.bn[d05][1] = this.locY; + } + } + + if (++this.bo == this.bn.length) { + this.bo = 0; + } + + this.bn[this.bo][0] = (double) this.yaw; + this.bn[this.bo][1] = this.locY; + double d0; + double d1; + double d2; + double d3; + float f3; + + if (this.world.isStatic) { + if (this.bg > 0) { + d0 = this.locX + (this.bh - this.locX) / (double) this.bg; + d1 = this.locY + (this.bi - this.locY) / (double) this.bg; + d2 = this.locZ + (this.bj - this.locZ) / (double) this.bg; + d3 = MathHelper.g(this.bk - (double) this.yaw); + this.yaw = (float) ((double) this.yaw + d3 / (double) this.bg); + this.pitch = (float) ((double) this.pitch + (this.bl - (double) this.pitch) / (double) this.bg); + --this.bg; + this.setPosition(d0, d1, d2); + this.b(this.yaw, this.pitch); + } + } else { + d0 = this.h - this.locX; + d1 = this.i - this.locY; + d2 = this.bm - this.locZ; + d3 = d0 * d0 + d1 * d1 + d2 * d2; + if (this.bD != null) { + this.h = this.bD.locX; + this.bm = this.bD.locZ; + double d4 = this.h - this.locX; + double d5 = this.bm - this.locZ; + double d6 = Math.sqrt(d4 * d4 + d5 * d5); + double d7 = 0.4000000059604645D + d6 / 80.0D - 1.0D; + + if (d7 > 10.0D) { + d7 = 10.0D; + } + + this.i = this.bD.boundingBox.b + d7; + } else { + this.h += this.random.nextGaussian() * 2.0D; + this.bm += this.random.nextGaussian() * 2.0D; + } + + if (this.bz || d3 < 100.0D || d3 > 22500.0D || this.positionChanged || this.F) { + this.bQ(); + } + + d1 /= (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + f3 = 0.6F; + if (d1 < (double) (-f3)) { + d1 = (double) (-f3); + } + + if (d1 > (double) f3) { + d1 = (double) f3; + } + + this.motY += d1 * 0.10000000149011612D; + this.yaw = MathHelper.g(this.yaw); + double d8 = 180.0D - Math.atan2(d0, d2) * 180.0D / 3.1415927410125732D; + double d9 = MathHelper.g(d8 - (double) this.yaw); + + if (d9 > 50.0D) { + d9 = 50.0D; + } + + if (d9 < -50.0D) { + d9 = -50.0D; + } + + Vec3D vec3d = Vec3D.a(this.h - this.locX, this.i - this.locY, this.bm - this.locZ).a(); + Vec3D vec3d1 = Vec3D.a((double) MathHelper.sin(this.yaw * 3.1415927F / 180.0F), this.motY, (double) (-MathHelper.cos(this.yaw * 3.1415927F / 180.0F))).a(); + float f4 = (float) (vec3d1.b(vec3d) + 0.5D) / 1.5F; + + if (f4 < 0.0F) { + f4 = 0.0F; + } + + this.bf *= 0.8F; + float f5 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ) * 1.0F + 1.0F; + double d10 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ) * 1.0D + 1.0D; + + if (d10 > 40.0D) { + d10 = 40.0D; + } + + this.bf = (float) ((double) this.bf + d9 * (0.699999988079071D / d10 / (double) f5)); + this.yaw += this.bf * 0.1F; + float f6 = (float) (2.0D / (d10 + 1.0D)); + float f7 = 0.06F; + + this.a(0.0F, -1.0F, f7 * (f4 * f6 + (1.0F - f6))); + if (this.bA) { + this.move(this.motX * 0.800000011920929D, this.motY * 0.800000011920929D, this.motZ * 0.800000011920929D); + } else { + this.move(this.motX, this.motY, this.motZ); + } + + Vec3D vec3d2 = Vec3D.a(this.motX, this.motY, this.motZ).a(); + float f8 = (float) (vec3d2.b(vec3d1) + 1.0D) / 2.0F; + + f8 = 0.8F + 0.15F * f8; + this.motX *= (double) f8; + this.motZ *= (double) f8; + this.motY *= 0.9100000262260437D; + } + + this.aM = this.yaw; + this.bq.width = this.bq.length = 3.0F; + this.bs.width = this.bs.length = 2.0F; + this.bt.width = this.bt.length = 2.0F; + this.bu.width = this.bu.length = 2.0F; + this.br.length = 3.0F; + this.br.width = 5.0F; + this.bv.length = 2.0F; + this.bv.width = 4.0F; + this.bw.length = 3.0F; + this.bw.width = 4.0F; + f1 = (float) (this.b(5, 1.0F)[1] - this.b(10, 1.0F)[1]) * 10.0F / 180.0F * 3.1415927F; + f2 = MathHelper.cos(f1); + float f9 = -MathHelper.sin(f1); + float f10 = this.yaw * 3.1415927F / 180.0F; + float f11 = MathHelper.sin(f10); + float f12 = MathHelper.cos(f10); + + this.br.h(); + this.br.setPositionRotation(this.locX + (double) (f11 * 0.5F), this.locY, this.locZ - (double) (f12 * 0.5F), 0.0F, 0.0F); + this.bv.h(); + this.bv.setPositionRotation(this.locX + (double) (f12 * 4.5F), this.locY + 2.0D, this.locZ + (double) (f11 * 4.5F), 0.0F, 0.0F); + this.bw.h(); + this.bw.setPositionRotation(this.locX - (double) (f12 * 4.5F), this.locY + 2.0D, this.locZ - (double) (f11 * 4.5F), 0.0F, 0.0F); + if (!this.world.isStatic && this.hurtTicks == 0) { + this.b(this.world.getEntities(this, this.bq.boundingBox.grow(1.0D, 1.0D, 1.0D))); + } + + double[] adouble = this.b(5, 1.0F); + double[] adouble1 = this.b(0, 1.0F); + + f3 = MathHelper.sin(this.yaw * 3.1415927F / 180.0F - this.bf * 0.01F); + float f13 = MathHelper.cos(this.yaw * 3.1415927F / 180.0F - this.bf * 0.01F); + + this.bq.h(); + this.bq.setPositionRotation(this.locX + (double) (f3 * 5.5F * f2), this.locY + (adouble1[1] - adouble[1]) * 1.0D + (double) (f9 * 5.5F), this.locZ - (double) (f13 * 5.5F * f2), 0.0F, 0.0F); + + for (int j = 0; j < 3; ++j) { + EntityComplexPart entitycomplexpart = null; + + if (j == 0) { + entitycomplexpart = this.bs; + } + + if (j == 1) { + entitycomplexpart = this.bt; + } + + if (j == 2) { + entitycomplexpart = this.bu; + } + + double[] adouble2 = this.b(12 + j * 2, 1.0F); + float f14 = this.yaw * 3.1415927F / 180.0F + this.b(adouble2[0] - adouble[0]) * 3.1415927F / 180.0F * 1.0F; + float f15 = MathHelper.sin(f14); + float f16 = MathHelper.cos(f14); + float f17 = 1.5F; + float f18 = (float) (j + 1) * 2.0F; + + entitycomplexpart.h(); + entitycomplexpart.setPositionRotation(this.locX - (double) ((f11 * f17 + f15 * f18) * f2), this.locY + (adouble2[1] - adouble[1]) * 1.0D - (double) ((f18 + f17) * f9) + 1.5D, this.locZ + (double) ((f12 * f17 + f16 * f18) * f2), 0.0F, 0.0F); + } + + if (!this.world.isStatic) { + this.bA = this.a(this.bq.boundingBox) | this.a(this.br.boundingBox); + } + } + } + + private void bP() { + if (this.bC != null) { + if (this.bC.dead) { + if (!this.world.isStatic) { + CraftEventFactory.entityDamage = this.bC; // CraftBukkit + this.a(this.bq, DamageSource.explosion((Explosion) null), 10.0F); + CraftEventFactory.entityDamage = null; // CraftBukkit + } + + this.bC = null; + } else if (this.ticksLived % 10 == 0 && this.getHealth() < this.getMaxHealth()) { + // CraftBukkit start + EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), 1.0D, EntityRegainHealthEvent.RegainReason.ENDER_CRYSTAL); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setHealth((float) (this.getHealth() + event.getAmount())); + } + // CraftBukkit end + } + } + + if (this.random.nextInt(10) == 0) { + float f = 32.0F; + List list = this.world.a(EntityEnderCrystal.class, this.boundingBox.grow((double) f, (double) f, (double) f)); + EntityEnderCrystal entityendercrystal = null; + double d0 = Double.MAX_VALUE; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityEnderCrystal entityendercrystal1 = (EntityEnderCrystal) iterator.next(); + double d1 = entityendercrystal1.f(this); + + if (d1 < d0) { + d0 = d1; + entityendercrystal = entityendercrystal1; + } + } + + this.bC = entityendercrystal; + } + } + + private void b(List list) { + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (entity instanceof EntityLiving) { + if (entity.damageEntity(DamageSource.mobAttack(this), 10.0F)) { + entity.g((double) (-MathHelper.sin(this.yaw * 3.1415927F / 180.0F) * 3.0F), 0.8D, (double) (MathHelper.cos(this.yaw * 3.1415927F / 180.0F) * (float) i * 3.0F)); + } + } + } + } + + private void bQ() { + this.bz = false; + if (this.random.nextInt(2) == 0 && !this.world.players.isEmpty()) { + // CraftBukkit start + Entity target = (Entity) this.world.players.get(this.random.nextInt(this.world.players.size())); + EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), target.getBukkitEntity(), EntityTargetEvent.TargetReason.RANDOM_TARGET); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.bD = null; + } else { + this.bD = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); + } + } + // CraftBukkit end + } else { + boolean flag = false; + + do { + this.h = 0.0D; + this.i = (double) (70.0F + this.random.nextFloat() * 50.0F); + this.bm = 0.0D; + this.h += (double) (this.random.nextFloat() * 120.0F - 60.0F); + this.bm += (double) (this.random.nextFloat() * 120.0F - 60.0F); + double d0 = this.locX - this.h; + double d1 = this.locY - this.i; + double d2 = this.locZ - this.bm; + + flag = d0 * d0 + d1 * d1 + d2 * d2 > 100.0D; + } while (!flag); + + this.bD = null; + } + } + + private float b(double d0) { + return (float) MathHelper.g(d0); + } + + private boolean a(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.b); + int k = MathHelper.floor(axisalignedbb.c); + int l = MathHelper.floor(axisalignedbb.d); + int i1 = MathHelper.floor(axisalignedbb.e); + int j1 = MathHelper.floor(axisalignedbb.f); + boolean flag = false; + boolean flag1 = false; + + // CraftBukkit start - Create a list to hold all the destroyed blocks + List destroyedBlocks = new java.util.ArrayList(); + org.bukkit.craftbukkit.CraftWorld craftWorld = this.world.getWorld(); + // CraftBukkit end + + for (int k1 = i; k1 <= l; ++k1) { + for (int l1 = j; l1 <= i1; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + Block block = this.world.getType(k1, l1, i2); + + if (block.getMaterial() != Material.AIR) { + if (block != Blocks.OBSIDIAN && block != Blocks.WHITESTONE && block != Blocks.BEDROCK && this.world.getGameRules().getBoolean("mobGriefing")) { + // CraftBukkit start - Add blocks to list rather than destroying them + // flag1 = this.world.setAir(k1, l1, i2) || flag1; + flag1 = true; + destroyedBlocks.add(craftWorld.getBlockAt(k1, l1, i2)); + // CraftBukkit end + } else { + flag = true; + } + } + } + } + } + + if (flag1) { + // CraftBukkit start - Set off an EntityExplodeEvent for the dragon exploding all these blocks + org.bukkit.entity.Entity bukkitEntity = this.getBukkitEntity(); + EntityExplodeEvent event = new EntityExplodeEvent(bukkitEntity, bukkitEntity.getLocation(), destroyedBlocks, 0F); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + // This flag literally means 'Dragon hit something hard' (Obsidian, White Stone or Bedrock) and will cause the dragon to slow down. + // We should consider adding an event extension for it, or perhaps returning true if the event is cancelled. + return flag; + } else if (event.getYield() == 0F) { + // Yield zero ==> no drops + for (org.bukkit.block.Block block : event.blockList()) { + this.world.setAir(block.getX(), block.getY(), block.getZ()); + } + } else { + for (org.bukkit.block.Block block : event.blockList()) { + org.bukkit.Material blockId = block.getType(); + if (blockId == org.bukkit.Material.AIR) { + continue; + } + + int blockX = block.getX(); + int blockY = block.getY(); + int blockZ = block.getZ(); + + Block nmsBlock = org.bukkit.craftbukkit.util.CraftMagicNumbers.getBlock(blockId); + if (nmsBlock.a(explosionSource)) { + nmsBlock.dropNaturally(this.world, blockX, blockY, blockZ, block.getData(), event.getYield(), 0); + } + nmsBlock.wasExploded(world, blockX, blockY, blockZ, explosionSource); + + this.world.setAir(blockX, blockY, blockZ); + } + } + // CraftBukkit end + + double d0 = axisalignedbb.a + (axisalignedbb.d - axisalignedbb.a) * (double) this.random.nextFloat(); + double d1 = axisalignedbb.b + (axisalignedbb.e - axisalignedbb.b) * (double) this.random.nextFloat(); + double d2 = axisalignedbb.c + (axisalignedbb.f - axisalignedbb.c) * (double) this.random.nextFloat(); + + this.world.addParticle("largeexplode", d0, d1, d2, 0.0D, 0.0D, 0.0D); + } + + return flag; + } + + public boolean a(EntityComplexPart entitycomplexpart, DamageSource damagesource, float f) { + if (entitycomplexpart != this.bq) { + f = f / 4.0F + 1.0F; + } + + float f1 = this.yaw * 3.1415927F / 180.0F; + float f2 = MathHelper.sin(f1); + float f3 = MathHelper.cos(f1); + + this.h = this.locX + (double) (f2 * 5.0F) + (double) ((this.random.nextFloat() - 0.5F) * 2.0F); + this.i = this.locY + (double) (this.random.nextFloat() * 3.0F) + 1.0D; + this.bm = this.locZ - (double) (f3 * 5.0F) + (double) ((this.random.nextFloat() - 0.5F) * 2.0F); + this.bD = null; + if (damagesource.getEntity() instanceof EntityHuman || damagesource.isExplosion()) { + this.dealDamage(damagesource, f); + } + + return true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + public boolean dealDamage(DamageSource damagesource, float f) { // CraftBukkit - protected -> public + return super.damageEntity(damagesource, f); + } + + protected void aF() { + if (this.dead) return; // CraftBukkit - can't kill what's already dead + ++this.bB; + if (this.bB >= 180 && this.bB <= 200) { + float f = (this.random.nextFloat() - 0.5F) * 8.0F; + float f1 = (this.random.nextFloat() - 0.5F) * 4.0F; + float f2 = (this.random.nextFloat() - 0.5F) * 8.0F; + + this.world.addParticle("hugeexplosion", this.locX + (double) f, this.locY + 2.0D + (double) f1, this.locZ + (double) f2, 0.0D, 0.0D, 0.0D); + } + + int i; + int j; + + if (!this.world.isStatic) { + if (this.bB > 150 && this.bB % 5 == 0) { + i = this.expToDrop / 12; // CraftBukkit - drop experience as dragon falls from sky. use experience drop from death event. This is now set in getExpReward() + + while (i > 0) { + j = EntityExperienceOrb.getOrbValue(i); + i -= j; + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j)); + } + } + + if (this.bB == 1) { + // CraftBukkit start - Use relative location for far away sounds + //this.world.b(1018, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; + for (EntityPlayer player : (List) this.world.players) { + double deltaX = this.locX - player.locX; + double deltaZ = this.locZ - player.locZ; + double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; + if ( world.spigotConfig.dragonDeathSoundRadius > 0 && distanceSquared > world.spigotConfig.dragonDeathSoundRadius * world.spigotConfig.dragonDeathSoundRadius ) continue; // Spigot + if (distanceSquared > viewDistance * viewDistance) { + double deltaLength = Math.sqrt(distanceSquared); + double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; + double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1018, (int) relativeX, (int) this.locY, (int) relativeZ, 0, true)); + } else { + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1018, (int) this.locX, (int) this.locY, (int) this.locZ, 0, true)); + } + } + // CraftBukkit end + } + } + + this.move(0.0D, 0.10000000149011612D, 0.0D); + this.aM = this.yaw += 20.0F; + if (this.bB == 200 && !this.world.isStatic) { + i = this.expToDrop - (10 * this.expToDrop / 12); // CraftBukkit - drop the remaining experience + + while (i > 0) { + j = EntityExperienceOrb.getOrbValue(i); + i -= j; + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j)); + } + + this.b(MathHelper.floor(this.locX), MathHelper.floor(this.locZ)); + this.die(); + } + } + + private void b(int i, int j) { + byte b0 = 64; + + BlockEnderPortal.a = true; + byte b1 = 4; + + // CraftBukkit start - Replace any "this.world" in the following with just "world"! + BlockStateListPopulator world = new BlockStateListPopulator(this.world.getWorld()); + + for (int k = b0 - 1; k <= b0 + 32; ++k) { + for (int l = i - b1; l <= i + b1; ++l) { + for (int i1 = j - b1; i1 <= j + b1; ++i1) { + double d0 = (double) (l - i); + double d1 = (double) (i1 - j); + double d2 = d0 * d0 + d1 * d1; + + if (d2 <= ((double) b1 - 0.5D) * ((double) b1 - 0.5D)) { + if (k < b0) { + if (d2 <= ((double) (b1 - 1) - 0.5D) * ((double) (b1 - 1) - 0.5D)) { + world.setTypeUpdate(l, k, i1, Blocks.BEDROCK); + } + } else if (k > b0) { + world.setTypeUpdate(l, k, i1, Blocks.AIR); + } else if (d2 > ((double) (b1 - 1) - 0.5D) * ((double) (b1 - 1) - 0.5D)) { + world.setTypeUpdate(l, k, i1, Blocks.BEDROCK); + } else { + world.setTypeUpdate(l, k, i1, Blocks.ENDER_PORTAL); + } + } + } + } + } + + world.setType(i, b0 + 0, j, Blocks.BEDROCK); + world.setType(i, b0 + 1, j, Blocks.BEDROCK); + world.setType(i, b0 + 2, j, Blocks.BEDROCK); + world.setTypeAndData(i - 1, b0 + 2, j, Blocks.TORCH, 2, 0); + world.setTypeAndData(i + 1, b0 + 2, j, Blocks.TORCH, 1, 0); + world.setTypeAndData(i, b0 + 2, j - 1, Blocks.TORCH, 4, 0); + world.setTypeAndData(i, b0 + 2, j + 1, Blocks.TORCH, 3, 0); + world.setType(i, b0 + 3, j, Blocks.BEDROCK); + world.setType(i, b0 + 4, j, Blocks.DRAGON_EGG); + + EntityCreatePortalEvent event = new EntityCreatePortalEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), java.util.Collections.unmodifiableList(world.getList()), org.bukkit.PortalType.ENDER); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + for (BlockState state : event.getBlocks()) { + state.update(true); + } + } else { + for (BlockState state : event.getBlocks()) { + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(state.getX(), state.getY(), state.getZ(), this.world); + for (Iterator it = this.world.players.iterator(); it.hasNext();) { + EntityHuman entity = (EntityHuman) it.next(); + if (entity instanceof EntityPlayer) { + ((EntityPlayer) entity).playerConnection.sendPacket(packet); + } + } + } + } + // CraftBukkit end + + BlockEnderPortal.a = false; + } + + protected void w() {} + + public Entity[] at() { + return this.children; + } + + public boolean R() { + return false; + } + + public World a() { + return this.world; + } + + protected String t() { + return "mob.enderdragon.growl"; + } + + protected String aT() { + return "mob.enderdragon.hit"; + } + + protected float bf() { + return 5.0F; + } + + // CraftBukkit start + public int getExpReward() { + // This value is equal to the amount of experience dropped while falling from the sky (10 * 1000) + // plus what is dropped when the dragon hits the ground (2000) + return 12000; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityEnderPearl.java b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderPearl.java new file mode 100644 index 0000000..9f56eb6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderPearl.java @@ -0,0 +1,203 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.player.PlayerPearlRefundEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.material.Gate; +import org.bukkit.material.Openable; +import org.bukkit.util.BlockIterator; +import org.bukkit.util.Vector; +import org.spigotmc.SpigotConfig; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +// CraftBukkit start + +public class EntityEnderPearl extends EntityProjectile { + + private Location lastValidTeleport; + private Item toRefundPearl = null; + private EntityLiving c; + + private static Set PROHIBITED_PEARL_BLOCKS = Sets.newHashSet(Block.getById(85), Block.getById(107)); + public static List pearlAbleType = Arrays.asList("STEP", "STAIR"); + public static List forwardTypes = Collections.singletonList(Material.ENDER_PORTAL_FRAME); + + public EntityEnderPearl(World world) { + super(world); + this.loadChunks = world.paperSpigotConfig.loadUnloadedEnderPearls; // PaperSpigot + } + + public EntityEnderPearl(World world, EntityLiving entityliving) { + super(world, entityliving); + this.c = entityliving; + this.loadChunks = world.paperSpigotConfig.loadUnloadedEnderPearls; // PaperSpigot + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (SpigotConfig.pearlThroughGatesAndTripwire) { + Block block = this.world.getType(movingobjectposition.b, movingobjectposition.c, movingobjectposition.d); + + if (block == Blocks.TRIPWIRE) { + return; + } else if (block == Blocks.FENCE_GATE) { + BlockIterator bi = null; + + try { + Vector l = new Vector(this.locX, this.locY, this.locZ); + Vector l2 = new Vector(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + Vector dir = new Vector(l2.getX() - l.getX(), l2.getY() - l.getY(), l2.getZ() - l.getZ()).normalize(); + bi = new BlockIterator(this.world.getWorld(), l, dir, 0, 1); + } catch (IllegalStateException ex) { + // ignore + } + + if (bi != null) { + boolean open = true; + boolean hasSolidBlock = false; + + while (bi.hasNext()) { + org.bukkit.block.Block b = bi.next(); + + if (b.getType().isSolid() && b.getType().isOccluding()) { + hasSolidBlock = true; + } + + if (b.getState().getData() instanceof Gate && !((Gate) b.getState().getData()).isOpen()) { + open = false; + break; + } + } + + if (open && !hasSolidBlock) { + return; + } + } + } + } + + + // PaperSpigot start - Remove entities in unloaded chunks + if (inUnloadedChunk && world.paperSpigotConfig.removeUnloadedEnderPearls) { + die(); + } + // PaperSpigot end + + for (int i = 0; i < 32; ++i) { + this.world.addParticle("portal", this.locX, this.locY + this.random.nextDouble() * 2.0D, this.locZ, this.random.nextGaussian(), 0.0D, this.random.nextGaussian()); + } + + if (!this.world.isStatic) { + + if (this.getShooter() != null && this.getShooter() instanceof EntityPlayer) { + EntityPlayer entityplayer = (EntityPlayer) this.getShooter(); + + if (entityplayer.playerConnection.b().isConnected() && entityplayer.world == this.world) { // MineHQ + // CraftBukkit start - Fire PlayerTeleportEvent + + if (this.lastValidTeleport != null) { + + org.bukkit.craftbukkit.entity.CraftPlayer player = entityplayer.getBukkitEntity(); + org.bukkit.Location location = this.lastValidTeleport; + location.setPitch(player.getLocation().getPitch()); + location.setYaw(player.getLocation().getYaw()); + + PlayerTeleportEvent teleEvent = new PlayerTeleportEvent(player, player.getLocation(), location, PlayerTeleportEvent.TeleportCause.ENDER_PEARL); + Bukkit.getPluginManager().callEvent(teleEvent); + + if (!teleEvent.isCancelled() && !entityplayer.playerConnection.isDisconnected()) { + if (this.getShooter().am()) { + this.getShooter().mount(null); + } + + entityplayer.playerConnection.teleport(teleEvent.getTo()); + this.getShooter().fallDistance = 0.0F; + CraftEventFactory.entityDamage = this; + this.getShooter().damageEntity(DamageSource.FALL, 5.0F); + CraftEventFactory.entityDamage = null; + } + // CraftBukkit end + } else { + Bukkit.getPluginManager().callEvent(new PlayerPearlRefundEvent(entityplayer.getBukkitEntity())); + } + } + } + + this.die(); + } + } + + @Override + public void h() { + EntityLiving shooter = this.getShooter(); + + if (shooter != null && !shooter.isAlive()) { + this.die(); + } else { + AxisAlignedBB newBoundingBox = AxisAlignedBB.a(this.locX - 0.3D, this.locY - 0.05D, this.locZ - 0.3D, this.locX + 0.3D, this.locY + 0.5D, this.locZ + 0.3D); + + if (!this.world.boundingBoxContainsMaterials(this.boundingBox.grow(0.25D, 0D, 0.25D), PROHIBITED_PEARL_BLOCKS) && this.world.getCubes(this, newBoundingBox).isEmpty()) { + this.lastValidTeleport = getBukkitEntity().getLocation(); + } + org.bukkit.block.Block block = this.world.getWorld().getBlockAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)); + Material typeHere = this.world.getWorld().getBlockAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)).getType(); + + if (pearlAbleType.stream().anyMatch(it -> typeHere.name().contains(it))) { + this.lastValidTeleport = getBukkitEntity().getLocation(); + } + + if (shooter != null && forwardTypes.stream().anyMatch(it -> block.getRelative(getDirection((EntityPlayer)shooter)).getType() == it)) { + this.lastValidTeleport = getBukkitEntity().getLocation(); + } + + if (typeHere == Material.FENCE_GATE) { + if (((Openable) block.getState().getData()).isOpen()) { + this.lastValidTeleport = getBukkitEntity().getLocation(); + } + } + + if (shooter != null) { + final org.bukkit.block.Block newTrap = block.getRelative(getDirection((EntityPlayer)shooter)).getRelative(BlockFace.DOWN); + + if (newTrap.getType() == Material.COBBLE_WALL || newTrap.getType() == Material.FENCE) { + this.lastValidTeleport = newTrap.getLocation(); + } + } + + super.h(); + } + } + + public static BlockFace getDirection(EntityPlayer entityPlayer) { + float yaw = entityPlayer.getBukkitEntity().getLocation().getYaw(); + if (yaw < 0) { + yaw += 360; + } + if (yaw >= 315 || yaw < 45) { + return BlockFace.SOUTH; + } else if (yaw < 135) { + return BlockFace.WEST; + } else if (yaw < 225) { + return BlockFace.NORTH; + } else if (yaw < 315) { + return BlockFace.EAST; + } + return BlockFace.NORTH; + } + + public Item getToRefundPearl() { + return this.toRefundPearl; + } + + public void setToRefundPearl(Item pearl) { + this.toRefundPearl = pearl; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityEnderman.java b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderman.java new file mode 100644 index 0000000..2372d18 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityEnderman.java @@ -0,0 +1,393 @@ +package net.minecraft.server; + +import java.util.UUID; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTeleportEvent; +// CraftBukkit end +import org.spigotmc.ProtocolData; // Spigot - protocol patch + +public class EntityEnderman extends EntityMonster { + + private static final UUID bp = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0"); + private static final AttributeModifier bq = (new AttributeModifier(bp, "Attacking speed boost", 6.199999809265137D, 0)).a(false); + private static boolean[] br = new boolean[256]; + private int bs; + private int bt; + private Entity bu; + private boolean bv; + + public EntityEnderman(World world) { + super(world); + this.a(0.6F, 2.9F); + this.W = 1.0F; + } + + @Override + public void h() { + super.h(); + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(40.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.30000001192092896D); + this.getAttributeInstance(GenericAttributes.e).setValue(7.0D); + } + + protected void c() { + super.c(); + this.datawatcher.a( 16, new ProtocolData.ByteShort( (short) 0 ) ); // Spigot - protocol patch, handle metadata change + this.datawatcher.a(17, new Byte((byte) 0)); + this.datawatcher.a(18, new Byte((byte) 0)); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setShort("carried", (short) Block.getId(this.getCarried())); + nbttagcompound.setShort("carriedData", (short) this.getCarriedData()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setCarried(Block.getById(nbttagcompound.getShort("carried"))); + this.setCarriedData(nbttagcompound.getShort("carriedData")); + } + + protected Entity findTarget() { + EntityHuman entityhuman = this.world.findNearbyVulnerablePlayer(this, 64.0D); + + if (entityhuman != null) { + if (this.f(entityhuman)) { + this.bv = true; + if (this.bt == 0) { + this.world.makeSound(entityhuman.locX, entityhuman.locY, entityhuman.locZ, "mob.endermen.stare", 1.0F, 1.0F); + } + + if (this.bt++ == 5) { + this.bt = 0; + this.a(true); + return entityhuman; + } + } else { + this.bt = 0; + } + } + + return null; + } + + private boolean f(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.armor[3]; + + if (itemstack != null && itemstack.getItem() == Item.getItemOf(Blocks.PUMPKIN)) { + return false; + } else { + Vec3D vec3d = entityhuman.j(1.0F).a(); + Vec3D vec3d1 = Vec3D.a(this.locX - entityhuman.locX, this.boundingBox.b + (double) (this.length / 2.0F) - (entityhuman.locY + (double) entityhuman.getHeadHeight()), this.locZ - entityhuman.locZ); + double d0 = vec3d1.b(); + + vec3d1 = vec3d1.a(); + double d1 = vec3d.b(vec3d1); + + return d1 > 1.0D - 0.025D / d0 && entityhuman.hasLineOfSight(this); + } + } + + public void e() { + if (this.L()) { + this.damageEntity(DamageSource.DROWN, 1.0F); + } + + if (this.bu != this.target) { + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.d); + + attributeinstance.b(bq); + if (this.target != null) { + attributeinstance.a(bq); + } + } + + this.bu = this.target; + int i; + + if (!this.world.isStatic && this.world.getGameRules().getBoolean("mobGriefing")) { + int j; + int k; + Block block; + + if (this.getCarried().getMaterial() == Material.AIR) { + if (this.random.nextInt(20) == 0) { + i = MathHelper.floor(this.locX - 2.0D + this.random.nextDouble() * 4.0D); + j = MathHelper.floor(this.locY + this.random.nextDouble() * 3.0D); + k = MathHelper.floor(this.locZ - 2.0D + this.random.nextDouble() * 4.0D); + block = this.world.getType(i, j, k); + if (br[Block.getId(block)]) { + // CraftBukkit start - Pickup event + if (!CraftEventFactory.callEntityChangeBlockEvent(this, this.world.getWorld().getBlockAt(i, j, k), org.bukkit.Material.AIR).isCancelled()) { + this.setCarried(block); + this.setCarriedData(this.world.getData(i, j, k)); + this.world.setTypeUpdate(i, j, k, Blocks.AIR); + } + // CraftBukkit end + } + } + } else if (this.random.nextInt(2000) == 0) { + i = MathHelper.floor(this.locX - 1.0D + this.random.nextDouble() * 2.0D); + j = MathHelper.floor(this.locY + this.random.nextDouble() * 2.0D); + k = MathHelper.floor(this.locZ - 1.0D + this.random.nextDouble() * 2.0D); + block = this.world.getType(i, j, k); + Block block1 = this.world.getType(i, j - 1, k); + + if (block.getMaterial() == Material.AIR && block1.getMaterial() != Material.AIR && block1.d()) { + // CraftBukkit start - Place event + if (!CraftEventFactory.callEntityChangeBlockEvent(this, i, j, k, this.getCarried(), this.getCarriedData()).isCancelled()) { + this.world.setTypeAndData(i, j, k, this.getCarried(), this.getCarriedData(), 3); + this.setCarried(Blocks.AIR); + } + // CraftBukkit end + } + } + } + + for (i = 0; i < 2; ++i) { + this.world.addParticle("portal", this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length - 0.25D, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, (this.random.nextDouble() - 0.5D) * 2.0D, -this.random.nextDouble(), (this.random.nextDouble() - 0.5D) * 2.0D); + } + + if (this.world.w() && !this.world.isStatic) { + float f = this.d(1.0F); + + if (f > 0.5F && this.world.i(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F) { + this.target = null; + this.a(false); + this.bv = false; + this.bZ(); + } + } + + if (this.L() || this.isBurning()) { + this.target = null; + this.a(false); + this.bv = false; + this.bZ(); + } + + if (this.cd() && !this.bv && this.random.nextInt(100) == 0) { + this.a(false); + } + + this.bc = false; + if (this.target != null) { + this.a(this.target, 100.0F, 100.0F); + } + + if (!this.world.isStatic && this.isAlive()) { + if (this.target != null) { + if (this.target instanceof EntityHuman && this.f((EntityHuman) this.target)) { + if (this.target.f((Entity) this) < 16.0D) { + this.bZ(); + } + + this.bs = 0; + } else if (this.target.f((Entity) this) > 256.0D && this.bs++ >= 30 && this.c(this.target)) { + this.bs = 0; + } + } else { + this.a(false); + this.bs = 0; + } + } + + super.e(); + } + + protected boolean bZ() { + double d0 = this.locX + (this.random.nextDouble() - 0.5D) * 64.0D; + double d1 = this.locY + (double) (this.random.nextInt(64) - 32); + double d2 = this.locZ + (this.random.nextDouble() - 0.5D) * 64.0D; + + return this.k(d0, d1, d2); + } + + protected boolean c(Entity entity) { + Vec3D vec3d = Vec3D.a(this.locX - entity.locX, this.boundingBox.b + (double) (this.length / 2.0F) - entity.locY + (double) entity.getHeadHeight(), this.locZ - entity.locZ); + + vec3d = vec3d.a(); + double d0 = 16.0D; + double d1 = this.locX + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.a * d0; + double d2 = this.locY + (double) (this.random.nextInt(16) - 8) - vec3d.b * d0; + double d3 = this.locZ + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.c * d0; + + return this.k(d1, d2, d3); + } + + protected boolean k(double d0, double d1, double d2) { + double d3 = this.locX; + double d4 = this.locY; + double d5 = this.locZ; + + this.locX = d0; + this.locY = d1; + this.locZ = d2; + boolean flag = false; + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (this.world.isLoaded(i, j, k)) { + boolean flag1 = false; + + while (!flag1 && j > 0) { + Block block = this.world.getType(i, j - 1, k); + + if (block.getMaterial().isSolid()) { + flag1 = true; + } else { + --this.locY; + --j; + } + } + + if (flag1) { + // CraftBukkit start - Teleport event + EntityTeleportEvent teleport = new EntityTeleportEvent(this.getBukkitEntity(), new Location(this.world.getWorld(), d3, d4, d5), new Location(this.world.getWorld(), this.locX, this.locY, this.locZ)); + this.world.getServer().getPluginManager().callEvent(teleport); + if (teleport.isCancelled()) { + return false; + } + + Location to = teleport.getTo(); + this.setPosition(to.getX(), to.getY(), to.getZ()); + // CraftBukkit end + + if (this.world.getCubes(this, this.boundingBox).isEmpty() && !this.world.containsLiquid(this.boundingBox)) { + flag = true; + } + } + } + + if (!flag) { + this.setPosition(d3, d4, d5); + return false; + } else { + short short1 = 128; + + for (int l = 0; l < short1; ++l) { + double d6 = (double) l / ((double) short1 - 1.0D); + float f = (this.random.nextFloat() - 0.5F) * 0.2F; + float f1 = (this.random.nextFloat() - 0.5F) * 0.2F; + float f2 = (this.random.nextFloat() - 0.5F) * 0.2F; + double d7 = d3 + (this.locX - d3) * d6 + (this.random.nextDouble() - 0.5D) * (double) this.width * 2.0D; + double d8 = d4 + (this.locY - d4) * d6 + this.random.nextDouble() * (double) this.length; + double d9 = d5 + (this.locZ - d5) * d6 + (this.random.nextDouble() - 0.5D) * (double) this.width * 2.0D; + + this.world.addParticle("portal", d7, d8, d9, (double) f, (double) f1, (double) f2); + } + + this.world.makeSound(d3, d4, d5, "mob.endermen.portal", 1.0F, 1.0F); + this.makeSound("mob.endermen.portal", 1.0F, 1.0F); + return true; + } + } + + protected String t() { + return this.cd() ? "mob.endermen.scream" : "mob.endermen.idle"; + } + + protected String aT() { + return "mob.endermen.hit"; + } + + protected String aU() { + return "mob.endermen.death"; + } + + protected Item getLoot() { + return Items.ENDER_PEARL; + } + + protected void dropDeathLoot(boolean flag, int i) { + Item item = this.getLoot(); + + if (item != null) { + int j = this.random.nextInt(2 + i); + + for (int k = 0; k < j; ++k) { + this.a(item, 1); + } + } + } + + public void setCarried(Block block) { + this.datawatcher.watch( 16, new ProtocolData.ByteShort( (short) Block.getId( block ) ) ); // Spigot - protocol patch, handle metadata change + } + + public Block getCarried() { + return Block.getById(this.datawatcher.getShort( 16 )); // Spigot - protocol patch, handle metadata change + } + + public void setCarriedData(int i) { + this.datawatcher.watch(17, Byte.valueOf((byte) (i & 255))); + } + + public int getCarriedData() { + return this.datawatcher.getByte(17); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + this.a(true); + if (damagesource instanceof EntityDamageSource && damagesource.getEntity() instanceof EntityHuman) { + this.bv = true; + } + + if (damagesource instanceof EntityDamageSourceIndirect) { + this.bv = false; + + for (int i = 0; i < 64; ++i) { + if (this.bZ()) { + return true; + } + } + + return false; + } else { + return super.damageEntity(damagesource, f); + } + } + } + + public boolean cd() { + return this.datawatcher.getByte(18) > 0; + } + + public void a(boolean flag) { + this.datawatcher.watch(18, Byte.valueOf((byte) (flag ? 1 : 0))); + } + + static { + br[Block.getId(Blocks.GRASS)] = true; + br[Block.getId(Blocks.DIRT)] = true; + br[Block.getId(Blocks.SAND)] = true; + br[Block.getId(Blocks.GRAVEL)] = true; + br[Block.getId(Blocks.YELLOW_FLOWER)] = true; + br[Block.getId(Blocks.RED_ROSE)] = true; + br[Block.getId(Blocks.BROWN_MUSHROOM)] = true; + br[Block.getId(Blocks.RED_MUSHROOM)] = true; + br[Block.getId(Blocks.TNT)] = true; + br[Block.getId(Blocks.CACTUS)] = true; + br[Block.getId(Blocks.CLAY)] = true; + br[Block.getId(Blocks.PUMPKIN)] = true; + br[Block.getId(Blocks.MELON)] = true; + br[Block.getId(Blocks.MYCEL)] = true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityExperienceOrb.java b/vspigot-server/src/main/java/net/minecraft/server/EntityExperienceOrb.java new file mode 100644 index 0000000..a015e1c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityExperienceOrb.java @@ -0,0 +1,199 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTargetEvent; +// CraftBukkit end + +public class EntityExperienceOrb extends Entity { + + public int a; + public int b; + public int c; + private int d = 5; + public int value; // CraftBukkit - private -> public + private EntityHuman targetPlayer; + private int targetTime; + + public EntityExperienceOrb(World world, double d0, double d1, double d2, int i) { + super(world); + this.a(0.5F, 0.5F); + this.height = this.length / 2.0F; + this.setPosition(d0, d1, d2); + this.yaw = (float) (Math.random() * 360.0D); + this.motX = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D) * 2.0F); + this.motY = (double) ((float) (Math.random() * 0.2D) * 2.0F); + this.motZ = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D) * 2.0F); + this.value = i; + } + + protected boolean g_() { + return false; + } + + public EntityExperienceOrb(World world) { + super(world); + this.a(0.25F, 0.25F); + this.height = this.length / 2.0F; + } + + protected void c() {} + + public void h() { + super.h(); + if (this.c > 0) { + --this.c; + } + + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.motY -= 0.029999999329447746D; + if (this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)).getMaterial() == Material.LAVA) { + this.motY = 0.20000000298023224D; + this.motX = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.motZ = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.makeSound("random.fizz", 0.4F, 2.0F + this.random.nextFloat() * 0.4F); + } + + this.j(this.locX, (this.boundingBox.b + this.boundingBox.e) / 2.0D, this.locZ); + double d0 = 8.0D; + + // Poweruser start + EntityHuman foundTarget = null; + if (this.targetTime < this.a - 20 + this.getId() % 100) { + if (this.targetPlayer == null || this.targetPlayer.f(this) > d0 * d0) { + foundTarget = this.world.findNearbyPlayer(this, d0); + if(foundTarget == null) { + this.targetPlayer = foundTarget; + } else if(foundTarget != null && !foundTarget.equals(this.targetPlayer)) { + // CraftBukkit start + EntityTargetEvent event = CraftEventFactory.callEntityTargetEvent(this, foundTarget, EntityTargetEvent.TargetReason.CLOSEST_PLAYER); + Entity target = event.getTarget() == null ? null : ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); + if(!event.isCancelled()) { + if(target == null) { + this.targetPlayer = null; + } else if(target instanceof EntityHuman) { + this.targetPlayer = (EntityHuman) target; + } + } + // CraftBukkit end + } + } + // Poweruser end + this.targetTime = this.a; + } + + if (this.targetPlayer != null) { + double d1 = (this.targetPlayer.locX - this.locX) / d0; + double d2 = (this.targetPlayer.locY + (double) this.targetPlayer.getHeadHeight() - this.locY) / d0; + double d3 = (this.targetPlayer.locZ - this.locZ) / d0; + double d4 = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3); + double d5 = 1.0D - d4; + + if (d5 > 0.0D) { + d5 *= d5; + this.motX += d1 / d4 * d5 * 0.1D; + this.motY += d2 / d4 * d5 * 0.1D; + this.motZ += d3 / d4 * d5 * 0.1D; + } + } + + this.move(this.motX, this.motY, this.motZ); + float f = 0.98F; + + if (this.onGround) { + f = this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.boundingBox.b) - 1, MathHelper.floor(this.locZ)).frictionFactor * 0.98F; + } + + this.motX *= (double) f; + this.motY *= 0.9800000190734863D; + this.motZ *= (double) f; + if (this.onGround) { + this.motY *= -0.8999999761581421D; + } + + ++this.a; + ++this.b; + if (this.b >= this.world.spigotConfig.expDespawnRate) { + this.die(); + } + } + + public boolean N() { + return this.world.a(this.boundingBox, Material.WATER, (Entity) this); + } + + protected void burn(int i) { + this.damageEntity(DamageSource.FIRE, (float) i); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + this.Q(); + this.d = (int) ((float) this.d - f); + if (this.d <= 0) { + this.die(); + } + + return false; + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("Health", (short) ((byte) this.d)); + nbttagcompound.setShort("Age", (short) this.b); + nbttagcompound.setShort("Value", (short) this.value); + } + + public void a(NBTTagCompound nbttagcompound) { + this.d = nbttagcompound.getShort("Health") & 255; + this.b = nbttagcompound.getShort("Age"); + this.value = nbttagcompound.getShort("Value"); + } + + public void b_(EntityHuman entityhuman) { + if (!this.world.isStatic) { + if (this.c == 0 && entityhuman.bt == 0) { + entityhuman.bt = 2; + this.world.makeSound(entityhuman, "random.orb", 0.1F, 0.5F * ((this.random.nextFloat() - this.random.nextFloat()) * 0.7F + 1.8F)); + entityhuman.receive(this, 1); + entityhuman.giveExp(CraftEventFactory.callPlayerExpChangeEvent(entityhuman, this.value).getAmount()); // CraftBukkit - this.value -> event.getAmount() + this.die(); + } + } + } + + public int e() { + return this.value; + } + + public static int getOrbValue(int i) { + // CraftBukkit start + if (i > 162670129) return i - 100000; + if (i > 81335063) return 81335063; + if (i > 40667527) return 40667527; + if (i > 20333759) return 20333759; + if (i > 10166857) return 10166857; + if (i > 5083423) return 5083423; + if (i > 2541701) return 2541701; + if (i > 1270849) return 1270849; + if (i > 635413) return 635413; + if (i > 317701) return 317701; + if (i > 158849) return 158849; + if (i > 79423) return 79423; + if (i > 39709) return 39709; + if (i > 19853) return 19853; + if (i > 9923) return 9923; + if (i > 4957) return 4957; + // CraftBukkit end + + return i >= 2477 ? 2477 : (i >= 1237 ? 1237 : (i >= 617 ? 617 : (i >= 307 ? 307 : (i >= 149 ? 149 : (i >= 73 ? 73 : (i >= 37 ? 37 : (i >= 17 ? 17 : (i >= 7 ? 7 : (i >= 3 ? 3 : 1))))))))); + } + + public boolean av() { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityFallingBlock.java b/vspigot-server/src/main/java/net/minecraft/server/EntityFallingBlock.java new file mode 100644 index 0000000..8f858a4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityFallingBlock.java @@ -0,0 +1,279 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityFallingBlock extends Entity { + + public Block id; // CraftBukkit - private -> public + public int data; + public int ticksLived; + public boolean dropItem; + private boolean f; + private boolean hurtEntities; + private int fallHurtMax; + private float fallHurtAmount; + public NBTTagCompound tileEntityData; + public org.bukkit.Location sourceLoc; // PaperSpigot + + // PaperSpigot start - Add FallingBlock and TNT source location API + public EntityFallingBlock(World world) { + this(null, world); + } + + public EntityFallingBlock(org.bukkit.Location loc, World world) { + super(world); + sourceLoc = loc; + // PaperSpigot end + this.dropItem = true; + this.fallHurtMax = 40; + this.fallHurtAmount = 2.0F; + this.loadChunks = world.paperSpigotConfig.loadUnloadedFallingBlocks; // PaperSpigot + } + + // PaperSpigot start - Add FallingBlock and TNT source location API + public EntityFallingBlock(org.bukkit.Location loc, World world, double d0, double d1, double d2, Block block) { + this(loc, world, d0, d1, d2, block, 0); + } + + public EntityFallingBlock(org.bukkit.Location loc, World world, double d0, double d1, double d2, Block block, int i) { + super(world); + sourceLoc = loc; + // PaperSpigot end + this.dropItem = true; + this.fallHurtMax = 40; + this.fallHurtAmount = 2.0F; + this.id = block; + this.data = i; + this.k = true; + this.a(0.98F, 0.98F); + this.height = this.length / 2.0F; + this.setPosition(d0, d1, d2); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + this.loadChunks = world.paperSpigotConfig.loadUnloadedFallingBlocks; // PaperSpigot + } + + protected boolean g_() { + return false; + } + + protected void c() {} + + public boolean R() { + return !this.dead; + } + + public void h() { + if (this.id.getMaterial() == Material.AIR) { + this.die(); + } else { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + ++this.ticksLived; + this.motY -= 0.03999999910593033D; + this.move(this.motX, this.motY, this.motZ); + // PaperSpigot start - Remove entities in unloaded chunks + if (this.inUnloadedChunk && world.paperSpigotConfig.removeUnloadedFallingBlocks) { + this.die(); + } + // PaperSpigot end + + // PaperSpigot start - Drop falling blocks above the specified height + if (this.world.paperSpigotConfig.fallingBlockHeightNerf != 0 && this.locY > this.world.paperSpigotConfig.fallingBlockHeightNerf) { + if (this.dropItem) { + this.a(new ItemStack(this.id, 1, this.id.getDropData(this.data)), 0.0F); + } + + this.die(); + } + // PaperSpigot end + + this.motX *= 0.9800000190734863D; + this.motY *= 0.9800000190734863D; + this.motZ *= 0.9800000190734863D; + if (!this.world.isStatic) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (this.ticksLived == 1) { + // CraftBukkit - compare data and call event + if (this.ticksLived != 1 || this.world.getType(i, j, k) != this.id || this.world.getData(i, j, k) != this.data || CraftEventFactory.callEntityChangeBlockEvent(this, i, j, k, Blocks.AIR, 0).isCancelled()) { + this.die(); + return; + } + + this.world.setAir(i, j, k); + world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, i, j, k); // Spigot + } + + if (this.onGround) { + this.motX *= 0.699999988079071D; + this.motZ *= 0.699999988079071D; + this.motY *= -0.5D; + if (this.world.getType(i, j, k) != Blocks.PISTON_MOVING) { + this.die(); + // CraftBukkit start - fire EntityChangeBlockEvent + if (!this.f && this.world.mayPlace(this.id, i, j, k, true, 1, (Entity) null, (ItemStack) null) && !BlockFalling.canFall(this.world, i, j - 1, k) /* mimic the false conditions of setTypeIdAndData */ && i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000 && j > 0 && j < 256 && !(this.world.getType(i, j, k) == this.id && this.world.getData(i, j, k) == this.data)) { + if (CraftEventFactory.callEntityChangeBlockEvent(this, i, j, k, this.id, this.data).isCancelled()) { + return; + } + this.world.setTypeAndData(i, j, k, this.id, this.data, 3); + // CraftBukkit end + world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, i, j, k); // Spigot + + if (this.id instanceof BlockFalling) { + ((BlockFalling) this.id).a(this.world, i, j, k, this.data); + } + + if (this.tileEntityData != null && this.id instanceof IContainer) { + TileEntity tileentity = this.world.getTileEntity(i, j, k); + + if (tileentity != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + tileentity.b(nbttagcompound); + Iterator iterator = this.tileEntityData.c().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + NBTBase nbtbase = this.tileEntityData.get(s); + + if (!s.equals("x") && !s.equals("y") && !s.equals("z")) { + nbttagcompound.set(s, nbtbase.clone()); + } + } + + tileentity.a(nbttagcompound); + tileentity.update(); + } + } + } else if (this.dropItem && !this.f) { + this.a(new ItemStack(this.id, 1, this.id.getDropData(this.data)), 0.0F); + } + } + } else if (this.ticksLived > 100 && !this.world.isStatic && (j < 1 || j > 256) || this.ticksLived > 600) { + if (this.dropItem) { + this.a(new ItemStack(this.id, 1, this.id.getDropData(this.data)), 0.0F); + } + + this.die(); + } + } + } + } + + protected void b(float f) { + if (this.hurtEntities) { + int i = MathHelper.f(f - 1.0F); + + if (i > 0) { + ArrayList arraylist = new ArrayList(this.world.getEntities(this, this.boundingBox)); + boolean flag = this.id == Blocks.ANVIL; + DamageSource damagesource = flag ? DamageSource.ANVIL : DamageSource.FALLING_BLOCK; + Iterator iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + CraftEventFactory.entityDamage = this; // CraftBukkit + entity.damageEntity(damagesource, (float) Math.min(MathHelper.d((float) i * this.fallHurtAmount), this.fallHurtMax)); + CraftEventFactory.entityDamage = null; // CraftBukkit + } + + if (flag && (double) this.random.nextFloat() < 0.05000000074505806D + (double) i * 0.05D) { + int j = this.data >> 2; + int k = this.data & 3; + + ++j; + if (j > 2) { + this.f = true; + } else { + this.data = k | j << 2; + } + } + } + } + } + + protected void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setByte("Tile", (byte) Block.getId(this.id)); + nbttagcompound.setInt("TileID", Block.getId(this.id)); + nbttagcompound.setByte("Data", (byte) this.data); + nbttagcompound.setByte("Time", (byte) this.ticksLived); + nbttagcompound.setBoolean("DropItem", this.dropItem); + nbttagcompound.setBoolean("HurtEntities", this.hurtEntities); + nbttagcompound.setFloat("FallHurtAmount", this.fallHurtAmount); + nbttagcompound.setInt("FallHurtMax", this.fallHurtMax); + if (this.tileEntityData != null) { + nbttagcompound.set("TileEntityData", this.tileEntityData); + } + // PaperSpigot start - Add FallingBlock and TNT source location API + if (sourceLoc != null) { + nbttagcompound.setInt("SourceLoc_x", sourceLoc.getBlockX()); + nbttagcompound.setInt("SourceLoc_y", sourceLoc.getBlockY()); + nbttagcompound.setInt("SourceLoc_z", sourceLoc.getBlockZ()); + } + // PaperSpigot end + } + + protected void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("TileID", 99)) { + this.id = Block.getById(nbttagcompound.getInt("TileID")); + } else { + this.id = Block.getById(nbttagcompound.getByte("Tile") & 255); + } + + this.data = nbttagcompound.getByte("Data") & 255; + this.ticksLived = nbttagcompound.getByte("Time") & 255; + if (nbttagcompound.hasKeyOfType("HurtEntities", 99)) { + this.hurtEntities = nbttagcompound.getBoolean("HurtEntities"); + this.fallHurtAmount = nbttagcompound.getFloat("FallHurtAmount"); + this.fallHurtMax = nbttagcompound.getInt("FallHurtMax"); + } else if (this.id == Blocks.ANVIL) { + this.hurtEntities = true; + } + + if (nbttagcompound.hasKeyOfType("DropItem", 99)) { + this.dropItem = nbttagcompound.getBoolean("DropItem"); + } + + if (nbttagcompound.hasKeyOfType("TileEntityData", 10)) { + this.tileEntityData = nbttagcompound.getCompound("TileEntityData"); + } + + if (this.id.getMaterial() == Material.AIR) { + this.id = Blocks.SAND; + } + // PaperSpigot start - Add FallingBlock and TNT source location API + if (nbttagcompound.hasKey("SourceLoc_x")) { + int srcX = nbttagcompound.getInt("SourceLoc_x"); + int srcY = nbttagcompound.getInt("SourceLoc_y"); + int srcZ = nbttagcompound.getInt("SourceLoc_z"); + sourceLoc = new org.bukkit.Location(world.getWorld(), srcX, srcY, srcZ); + } + // PaperSpigot end + } + + public void a(boolean flag) { + this.hurtEntities = flag; + } + + public void a(CrashReportSystemDetails crashreportsystemdetails) { + super.a(crashreportsystemdetails); + crashreportsystemdetails.a("Immitating block ID", Integer.valueOf(Block.getId(this.id))); + crashreportsystemdetails.a("Immitating block data", Integer.valueOf(this.data)); + } + + public Block f() { + return this.id; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityFireball.java b/vspigot-server/src/main/java/net/minecraft/server/EntityFireball.java new file mode 100644 index 0000000..663714d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityFireball.java @@ -0,0 +1,278 @@ +package net.minecraft.server; + +import java.util.List; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public abstract class EntityFireball extends Entity { + + private int e = -1; + private int f = -1; + private int g = -1; + private Block h; + private boolean i; + public EntityLiving shooter; + private int at; + private int au; + public double dirX; + public double dirY; + public double dirZ; + public float bukkitYield = 1; // CraftBukkit + public boolean isIncendiary = true; // CraftBukkit + + public EntityFireball(World world) { + super(world); + this.a(1.0F, 1.0F); + } + + protected void c() {} + + public EntityFireball(World world, double d0, double d1, double d2, double d3, double d4, double d5) { + super(world); + this.a(1.0F, 1.0F); + this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch); + this.setPosition(d0, d1, d2); + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + this.dirX = d3 / d6 * 0.1D; + this.dirY = d4 / d6 * 0.1D; + this.dirZ = d5 / d6 * 0.1D; + } + + public EntityFireball(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(world); + this.shooter = entityliving; + this.projectileSource = (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + this.a(1.0F, 1.0F); + this.setPositionRotation(entityliving.locX, entityliving.locY, entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.setPosition(this.locX, this.locY, this.locZ); + this.height = 0.0F; + this.motX = this.motY = this.motZ = 0.0D; + // CraftBukkit start - Added setDirection method + this.setDirection(d0, d1, d2); + } + + public void setDirection(double d0, double d1, double d2) { + // CraftBukkit end + d0 += this.random.nextGaussian() * 0.4D; + d1 += this.random.nextGaussian() * 0.4D; + d2 += this.random.nextGaussian() * 0.4D; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + this.dirX = d0 / d3 * 0.1D; + this.dirY = d1 / d3 * 0.1D; + this.dirZ = d2 / d3 * 0.1D; + } + + public void h() { + if (!this.world.isStatic && (this.shooter != null && this.shooter.dead || !this.world.isLoaded((int) this.locX, (int) this.locY, (int) this.locZ))) { + this.die(); + } else { + super.h(); + this.setOnFire(1); + if (this.i) { + if (this.world.getType(this.e, this.f, this.g) == this.h) { + ++this.at; + if (this.at == 600) { + this.die(); + } + + return; + } + + this.i = false; + this.motX *= (double) (this.random.nextFloat() * 0.2F); + this.motY *= (double) (this.random.nextFloat() * 0.2F); + this.motZ *= (double) (this.random.nextFloat() * 0.2F); + this.at = 0; + this.au = 0; + } else { + ++this.au; + } + + Vec3D vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.a(vec3d, vec3d1); + + vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + if (movingobjectposition != null) { + vec3d1 = Vec3D.a(movingobjectposition.pos.a, movingobjectposition.pos.b, movingobjectposition.pos.c); + } + + Entity entity = null; + List list = this.world.getEntities(this, this.boundingBox.a(this.motX, this.motY, this.motZ).grow(1.0D, 1.0D, 1.0D)); + double d0 = 0.0D; + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (entity1.R() && (!entity1.i(this.shooter) || this.au >= 25)) { + float f = 0.3F; + AxisAlignedBB axisalignedbb = entity1.boundingBox.grow((double) f, (double) f, (double) f); + MovingObjectPosition movingobjectposition1 = axisalignedbb.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { + double d1 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + if (movingobjectposition != null) { + this.a(movingobjectposition); + + // CraftBukkit start - Fire ProjectileHitEvent + if (this.dead) { + CraftEventFactory.callProjectileHitEvent(this); + } + // CraftBukkit end + } + + this.locX += this.motX; + this.locY += this.motY; + this.locZ += this.motZ; + float f1 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (Math.atan2(this.motZ, this.motX) * 180.0D / 3.1415927410125732D) + 90.0F; + + for (this.pitch = (float) (Math.atan2((double) f1, this.motY) * 180.0D / 3.1415927410125732D) - 90.0F; this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + float f2 = this.e(); + + if (this.M()) { + for (int j = 0; j < 4; ++j) { + float f3 = 0.25F; + + this.world.addParticle("bubble", this.locX - this.motX * (double) f3, this.locY - this.motY * (double) f3, this.locZ - this.motZ * (double) f3, this.motX, this.motY, this.motZ); + } + + f2 = 0.8F; + } + + this.motX += this.dirX; + this.motY += this.dirY; + this.motZ += this.dirZ; + this.motX *= (double) f2; + this.motY *= (double) f2; + this.motZ *= (double) f2; + this.world.addParticle("smoke", this.locX, this.locY + 0.5D, this.locZ, 0.0D, 0.0D, 0.0D); + this.setPosition(this.locX, this.locY, this.locZ); + } + } + + protected float e() { + return 0.95F; + } + + protected abstract void a(MovingObjectPosition movingobjectposition); + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("xTile", (short) this.e); + nbttagcompound.setShort("yTile", (short) this.f); + nbttagcompound.setShort("zTile", (short) this.g); + nbttagcompound.setByte("inTile", (byte) Block.getId(this.h)); + nbttagcompound.setByte("inGround", (byte) (this.i ? 1 : 0)); + // CraftBukkit - Fix direction being mismapped to invalid variables + nbttagcompound.set("power", this.a(new double[] { this.dirX, this.dirY, this.dirZ})); + // Spigot - Support vanilla's direction tag + nbttagcompound.set("direction", this.a(new double[] { this.motX, this.motY, this.motZ})); + } + + public void a(NBTTagCompound nbttagcompound) { + this.e = nbttagcompound.getShort("xTile"); + this.f = nbttagcompound.getShort("yTile"); + this.g = nbttagcompound.getShort("zTile"); + this.h = Block.getById(nbttagcompound.getByte("inTile") & 255); + this.i = nbttagcompound.getByte("inGround") == 1; + // CraftBukkit start - direction -> power + if (nbttagcompound.hasKeyOfType("power", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("power", 6); + + this.dirX = nbttaglist.d(0); + this.dirY = nbttaglist.d(1); + this.dirZ = nbttaglist.d(2); + // CraftBukkit end + } else if (nbttagcompound.hasKeyOfType("direction", 9)) { // Spigot - Support vanilla's direction tag + NBTTagList nbttaglist = nbttagcompound.getList("direction", 6); + + this.motX = nbttaglist.d(0); + this.motY = nbttaglist.d(1); + this.motZ = nbttaglist.d(2); + + } else { + this.die(); + } + } + + public boolean R() { + return true; + } + + public float af() { + return 1.0F; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + this.Q(); + if (damagesource.getEntity() != null) { + // CraftBukkit start + if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { + return false; + } + // CraftBukkit end + + Vec3D vec3d = damagesource.getEntity().ag(); + + if (vec3d != null) { + this.motX = vec3d.a; + this.motY = vec3d.b; + this.motZ = vec3d.c; + this.dirX = this.motX * 0.1D; + this.dirY = this.motY * 0.1D; + this.dirZ = this.motZ * 0.1D; + } + + if (damagesource.getEntity() instanceof EntityLiving) { + this.shooter = (EntityLiving) damagesource.getEntity(); + this.projectileSource = (org.bukkit.projectiles.ProjectileSource) this.shooter.getBukkitEntity(); + } + + return true; + } else { + return false; + } + } + } + + public float d(float f) { + return 1.0F; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityFireworks.java b/vspigot-server/src/main/java/net/minecraft/server/EntityFireworks.java new file mode 100644 index 0000000..b977cf7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityFireworks.java @@ -0,0 +1,130 @@ +package net.minecraft.server; + +public class EntityFireworks extends Entity { + + private int ticksFlown; + public int expectedLifespan; // CraftBukkit - private -> public + + // Spigot Start + @Override + public void inactiveTick() + { + this.ticksFlown += 19; + super.inactiveTick(); + } + // Spigot End + + public EntityFireworks(World world) { + super(world); + this.a(0.25F, 0.25F); + } + + protected void c() { + this.datawatcher.add(8, 5); + } + + public EntityFireworks(World world, double d0, double d1, double d2, ItemStack itemstack) { + super(world); + this.ticksFlown = 0; + this.a(0.25F, 0.25F); + this.setPosition(d0, d1, d2); + this.height = 0.0F; + int i = 1; + + if (itemstack != null && itemstack.hasTag()) { + this.datawatcher.watch(8, itemstack); + NBTTagCompound nbttagcompound = itemstack.getTag(); + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Fireworks"); + + if (nbttagcompound1 != null) { + i += nbttagcompound1.getByte("Flight"); + } + } + + this.motX = this.random.nextGaussian() * 0.001D; + this.motZ = this.random.nextGaussian() * 0.001D; + this.motY = 0.05D; + this.expectedLifespan = 10 * i + this.random.nextInt(6) + this.random.nextInt(7); + } + + public void h() { + this.S = this.locX; + this.T = this.locY; + this.U = this.locZ; + super.h(); + this.motX *= 1.15D; + this.motZ *= 1.15D; + this.motY += 0.04D; + this.move(this.motX, this.motY, this.motZ); + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (Math.atan2(this.motX, this.motZ) * 180.0D / 3.1415927410125732D); + + for (this.pitch = (float) (Math.atan2(this.motY, (double) f) * 180.0D / 3.1415927410125732D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + if (this.ticksFlown == 0) { + this.world.makeSound(this, "fireworks.launch", 3.0F, 1.0F); + } + + ++this.ticksFlown; + if (this.world.isStatic && this.ticksFlown % 2 < 2) { + this.world.addParticle("fireworksSpark", this.locX, this.locY - 0.3D, this.locZ, this.random.nextGaussian() * 0.05D, -this.motY * 0.5D, this.random.nextGaussian() * 0.05D); + } + + if (!this.world.isStatic && this.ticksFlown > this.expectedLifespan) { + this.world.broadcastEntityEffect(this, (byte) 17); + this.die(); + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("Life", this.ticksFlown); + nbttagcompound.setInt("LifeTime", this.expectedLifespan); + ItemStack itemstack = this.datawatcher.getItemStack(8); + + if (itemstack != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + itemstack.save(nbttagcompound1); + nbttagcompound.set("FireworksItem", nbttagcompound1); + } + } + + public void a(NBTTagCompound nbttagcompound) { + this.ticksFlown = nbttagcompound.getInt("Life"); + this.expectedLifespan = nbttagcompound.getInt("LifeTime"); + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("FireworksItem"); + + if (nbttagcompound1 != null) { + ItemStack itemstack = ItemStack.createStack(nbttagcompound1); + + if (itemstack != null) { + this.datawatcher.watch(8, itemstack); + } + } + } + + public float d(float f) { + return super.d(f); + } + + public boolean au() { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityFishingHook.java b/vspigot-server/src/main/java/net/minecraft/server/EntityFishingHook.java new file mode 100644 index 0000000..0fc1e85 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityFishingHook.java @@ -0,0 +1,476 @@ +package net.minecraft.server; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.entity.Fish; +import org.bukkit.event.player.PlayerFishEvent; +// CraftBukkit end + +public class EntityFishingHook extends Entity { + + private static final List d = Arrays.asList(new PossibleFishingResult[] { (new PossibleFishingResult(new ItemStack(Items.LEATHER_BOOTS), 10)).a(0.9F), new PossibleFishingResult(new ItemStack(Items.LEATHER), 10), new PossibleFishingResult(new ItemStack(Items.BONE), 10), new PossibleFishingResult(new ItemStack(Items.POTION), 10), new PossibleFishingResult(new ItemStack(Items.STRING), 5), (new PossibleFishingResult(new ItemStack(Items.FISHING_ROD), 2)).a(0.9F), new PossibleFishingResult(new ItemStack(Items.BOWL), 10), new PossibleFishingResult(new ItemStack(Items.STICK), 5), new PossibleFishingResult(new ItemStack(Items.INK_SACK, 10, 0), 1), new PossibleFishingResult(new ItemStack(Blocks.TRIPWIRE_SOURCE), 10), new PossibleFishingResult(new ItemStack(Items.ROTTEN_FLESH), 10)}); + private static final List e = Arrays.asList(new PossibleFishingResult[] { new PossibleFishingResult(new ItemStack(Blocks.WATER_LILY), 1), new PossibleFishingResult(new ItemStack(Items.NAME_TAG), 1), new PossibleFishingResult(new ItemStack(Items.SADDLE), 1), (new PossibleFishingResult(new ItemStack(Items.BOW), 1)).a(0.25F).a(), (new PossibleFishingResult(new ItemStack(Items.FISHING_ROD), 1)).a(0.25F).a(), (new PossibleFishingResult(new ItemStack(Items.BOOK), 1)).a()}); + private static final List f = Arrays.asList(new PossibleFishingResult[] { new PossibleFishingResult(new ItemStack(Items.RAW_FISH, 1, EnumFish.COD.a()), 60), new PossibleFishingResult(new ItemStack(Items.RAW_FISH, 1, EnumFish.SALMON.a()), 25), new PossibleFishingResult(new ItemStack(Items.RAW_FISH, 1, EnumFish.CLOWNFISH.a()), 2), new PossibleFishingResult(new ItemStack(Items.RAW_FISH, 1, EnumFish.PUFFERFISH.a()), 13)}); + private int g = -1; + private int h = -1; + private int i = -1; + private Block at; + private boolean au; + public int a; + public EntityHuman owner; + private int av; + private int aw; + private int ax; + private int ay; + private int az; + private float aA; + public Entity hooked; + private int aB; + private double aC; + private double aD; + private double aE; + private double aF; + private double aG; + + public EntityFishingHook(World world) { + super(world); + this.a(0.25F, 0.25F); + this.ak = true; + } + + public EntityFishingHook(World world, EntityHuman entityhuman) { + super(world); + this.ak = true; + this.owner = entityhuman; + this.owner.hookedFish = this; + this.a(0.25F, 0.25F); + this.setPositionRotation(entityhuman.locX, entityhuman.locY + 1.62D - (double) entityhuman.height, entityhuman.locZ, entityhuman.yaw, entityhuman.pitch); + this.locX -= (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * 0.16F); + this.locY -= 0.10000000149011612D; + this.locZ -= (double) (MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * 0.16F); + this.setPosition(this.locX, this.locY, this.locZ); + this.height = 0.0F; + float f = 0.4F; + + this.motX = (double) (-MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F) * f); + this.motZ = (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F) * f); + this.motY = (double) (-MathHelper.sin(this.pitch / 180.0F * 3.1415927F) * f); + this.c(this.motX, this.motY, this.motZ, 1.5F, 1.0F); + } + + protected void c() {} + + public void c(double d0, double d1, double d2, float f, float f1) { + float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d0 /= (double) f2; + d1 /= (double) f2; + d2 /= (double) f2; + d0 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d1 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d2 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d0 *= (double) f; + d1 *= (double) f; + d2 *= (double) f; + this.motX = d0; + this.motY = d1; + this.motZ = d2; + float f3 = MathHelper.sqrt(d0 * d0 + d2 * d2); + + this.lastYaw = this.yaw = (float) (Math.atan2(d0, d2) * 180.0D / 3.1415927410125732D); + this.lastPitch = this.pitch = (float) (Math.atan2(d1, (double) f3) * 180.0D / 3.1415927410125732D); + this.av = 0; + } + + public void h() { + super.h(); + if (this.aB > 0) { + double d0 = this.locX + (this.aC - this.locX) / (double) this.aB; + double d1 = this.locY + (this.aD - this.locY) / (double) this.aB; + double d2 = this.locZ + (this.aE - this.locZ) / (double) this.aB; + double d3 = MathHelper.g(this.aF - (double) this.yaw); + + this.yaw = (float) ((double) this.yaw + d3 / (double) this.aB); + this.pitch = (float) ((double) this.pitch + (this.aG - (double) this.pitch) / (double) this.aB); + --this.aB; + this.setPosition(d0, d1, d2); + this.b(this.yaw, this.pitch); + } else { + if (!this.world.isStatic) { + ItemStack itemstack = this.owner.bF(); + + if (this.owner.dead || !this.owner.isAlive() || itemstack == null || itemstack.getItem() != Items.FISHING_ROD || this.f(this.owner) > 1024.0D) { + this.die(); + this.owner.hookedFish = null; + return; + } + + if (this.hooked != null) { + if (!this.hooked.dead) { + this.locX = this.hooked.locX; + this.locY = this.hooked.boundingBox.b + (double) this.hooked.length * 0.8D; + this.locZ = this.hooked.locZ; + return; + } + + this.hooked = null; + } + } + + if (this.a > 0) { + --this.a; + } + + if (this.au) { + if (this.world.getType(this.g, this.h, this.i) == this.at) { + ++this.av; + if (this.av == 1200) { + this.die(); + } + + return; + } + + this.au = false; + this.motX *= (double) (this.random.nextFloat() * 0.2F); + this.motY *= (double) (this.random.nextFloat() * 0.2F); + this.motZ *= (double) (this.random.nextFloat() * 0.2F); + this.av = 0; + this.aw = 0; + } else { + ++this.aw; + } + + Vec3D vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.a(vec3d, vec3d1); + + vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + if (movingobjectposition != null) { + vec3d1 = Vec3D.a(movingobjectposition.pos.a, movingobjectposition.pos.b, movingobjectposition.pos.c); + } + + Entity entity = null; + List list = this.world.getEntities(this, this.boundingBox.a(this.motX, this.motY, this.motZ).grow(1.0D, 1.0D, 1.0D)); + double d4 = 0.0D; + + double d5; + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (entity1.R() && (entity1 != this.owner || this.aw >= 5)) { + float f = 0.3F; + AxisAlignedBB axisalignedbb = entity1.boundingBox.grow((double) f, (double) f, (double) f); + MovingObjectPosition movingobjectposition1 = axisalignedbb.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { + d5 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit - distance efficiency + if (d5 < d4 || d4 == 0.0D) { + entity = entity1; + d4 = d5; + } + } + } + } + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + if (movingobjectposition != null) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); // Craftbukkit - Call event + if (movingobjectposition.entity != null) { + if (movingobjectposition.entity.damageEntity(DamageSource.projectile(this, this.owner), 0.0F)) { + this.hooked = movingobjectposition.entity; + } + } else { + this.au = true; + } + } + + if (!this.au) { + this.move(this.motX, this.motY, this.motZ); + float f1 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (Math.atan2(this.motX, this.motZ) * 180.0D / 3.1415927410125732D); + + for (this.pitch = (float) (Math.atan2(this.motY, (double) f1) * 180.0D / 3.1415927410125732D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + float f2 = 0.92F; + + if (this.onGround || this.positionChanged) { + f2 = 0.5F; + } + + byte b0 = 5; + double d6 = 0.0D; + + for (int j = 0; j < b0; ++j) { + double d7 = this.boundingBox.b + (this.boundingBox.e - this.boundingBox.b) * (double) (j + 0) / (double) b0 - 0.125D + 0.125D; + double d8 = this.boundingBox.b + (this.boundingBox.e - this.boundingBox.b) * (double) (j + 1) / (double) b0 - 0.125D + 0.125D; + AxisAlignedBB axisalignedbb1 = AxisAlignedBB.a(this.boundingBox.a, d7, this.boundingBox.c, this.boundingBox.d, d8, this.boundingBox.f); + + if (this.world.b(axisalignedbb1, Material.WATER)) { + d6 += 1.0D / (double) b0; + } + } + + if (!this.world.isStatic && d6 > 0.0D) { + WorldServer worldserver = (WorldServer) this.world; + int k = 1; + + if (this.random.nextFloat() < 0.25F && this.world.isRainingAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY) + 1, MathHelper.floor(this.locZ))) { + k = 2; + } + + if (this.random.nextFloat() < 0.5F && !this.world.i(MathHelper.floor(this.locX), MathHelper.floor(this.locY) + 1, MathHelper.floor(this.locZ))) { + --k; + } + + if (this.ax > 0) { + --this.ax; + if (this.ax <= 0) { + this.ay = 0; + this.az = 0; + } + } else { + float f3; + double d9; + float f4; + float f5; + double d10; + double d11; + + if (this.az > 0) { + this.az -= k; + if (this.az <= 0) { + this.motY -= 0.20000000298023224D; + this.makeSound("random.splash", 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + f4 = (float) MathHelper.floor(this.boundingBox.b); + worldserver.a("bubble", this.locX, (double) (f4 + 1.0F), this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D); + worldserver.a("wake", this.locX, (double) (f4 + 1.0F), this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D); + this.ax = MathHelper.nextInt(this.random, 10, 30); + } else { + this.aA = (float) ((double) this.aA + this.random.nextGaussian() * 4.0D); + f4 = this.aA * 0.017453292F; + f5 = MathHelper.sin(f4); + f3 = MathHelper.cos(f4); + d9 = this.locX + (double) (f5 * (float) this.az * 0.1F); + d11 = (double) ((float) MathHelper.floor(this.boundingBox.b) + 1.0F); + d10 = this.locZ + (double) (f3 * (float) this.az * 0.1F); + if (this.random.nextFloat() < 0.15F) { + worldserver.a("bubble", d9, d11 - 0.10000000149011612D, d10, 1, (double) f5, 0.1D, (double) f3, 0.0D); + } + + float f6 = f5 * 0.04F; + float f7 = f3 * 0.04F; + + worldserver.a("wake", d9, d11, d10, 0, (double) f7, 0.01D, (double) (-f6), 1.0D); + worldserver.a("wake", d9, d11, d10, 0, (double) (-f7), 0.01D, (double) f6, 1.0D); + } + } else if (this.ay > 0) { + this.ay -= k; + f4 = 0.15F; + if (this.ay < 20) { + f4 = (float) ((double) f4 + (double) (20 - this.ay) * 0.05D); + } else if (this.ay < 40) { + f4 = (float) ((double) f4 + (double) (40 - this.ay) * 0.02D); + } else if (this.ay < 60) { + f4 = (float) ((double) f4 + (double) (60 - this.ay) * 0.01D); + } + + if (this.random.nextFloat() < f4) { + f5 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F; + f3 = MathHelper.a(this.random, 25.0F, 60.0F); + d9 = this.locX + (double) (MathHelper.sin(f5) * f3 * 0.1F); + d11 = (double) ((float) MathHelper.floor(this.boundingBox.b) + 1.0F); + d10 = this.locZ + (double) (MathHelper.cos(f5) * f3 * 0.1F); + worldserver.a("splash", d9, d11, d10, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D); + } + + if (this.ay <= 0) { + this.aA = MathHelper.a(this.random, 0.0F, 360.0F); + this.az = MathHelper.nextInt(this.random, 20, 80); + } + } else { + // PaperSpigot - Configurable fishing tick range + this.ay = MathHelper.nextInt(this.random, this.world.paperSpigotConfig.fishingMinTicks, this.world.paperSpigotConfig.fishingMaxTicks); + this.ay -= EnchantmentManager.getLureEnchantmentLevel(this.owner) * 20 * 5; + } + } + + if (this.ax > 0) { + this.motY -= (double) (this.random.nextFloat() * this.random.nextFloat() * this.random.nextFloat()) * 0.2D; + } + } + + d5 = d6 * 2.0D - 1.0D; + this.motY += 0.03999999910593033D * d5; + if (d6 > 0.0D) { + f2 = (float) ((double) f2 * 0.9D); + this.motY *= 0.8D; + } + + this.motX *= (double) f2; + this.motY *= (double) f2; + this.motZ *= (double) f2; + this.setPosition(this.locX, this.locY, this.locZ); + } + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("xTile", (short) this.g); + nbttagcompound.setShort("yTile", (short) this.h); + nbttagcompound.setShort("zTile", (short) this.i); + nbttagcompound.setByte("inTile", (byte) Block.getId(this.at)); + nbttagcompound.setByte("shake", (byte) this.a); + nbttagcompound.setByte("inGround", (byte) (this.au ? 1 : 0)); + } + + public void a(NBTTagCompound nbttagcompound) { + this.g = nbttagcompound.getShort("xTile"); + this.h = nbttagcompound.getShort("yTile"); + this.i = nbttagcompound.getShort("zTile"); + this.at = Block.getById(nbttagcompound.getByte("inTile") & 255); + this.a = nbttagcompound.getByte("shake") & 255; + this.au = nbttagcompound.getByte("inGround") == 1; + } + + public int e() { + if (this.world.isStatic) { + return 0; + } else { + byte b0 = 0; + + if (this.hooked != null) { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), this.hooked.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + + double d0 = this.owner.locX - this.locX; + double d1 = this.owner.locY - this.locY; + double d2 = this.owner.locZ - this.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + double d4 = 0.1D; + + this.hooked.motX += d0 * d4; + this.hooked.motY += d1 * d4 + (double) MathHelper.sqrt(d3) * 0.08D; + this.hooked.motZ += d2 * d4; + b0 = 3; + } else if (this.ax > 0) { + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY, this.locZ, this.f()); + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), entityitem.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH); + playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + + double d5 = this.owner.locX - this.locX; + double d6 = this.owner.locY - this.locY; + double d7 = this.owner.locZ - this.locZ; + double d8 = (double) MathHelper.sqrt(d5 * d5 + d6 * d6 + d7 * d7); + double d9 = 0.1D; + + entityitem.motX = d5 * d9; + entityitem.motY = d6 * d9 + (double) MathHelper.sqrt(d8) * 0.08D; + entityitem.motZ = d7 * d9; + this.world.addEntity(entityitem); + // CraftBukkit - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop() + this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop())); + b0 = 1; + } + + if (this.au) { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + + b0 = 2; + } + + // CraftBukkit start + if (b0 == 0) { + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + if (playerFishEvent.isCancelled()) { + return 0; + } + } + // CraftBukkit end + + this.die(); + this.owner.hookedFish = null; + return b0; + } + } + + private ItemStack f() { + float f = this.world.random.nextFloat(); + int i = EnchantmentManager.getLuckEnchantmentLevel(this.owner); + int j = EnchantmentManager.getLureEnchantmentLevel(this.owner); + float f1 = 0.1F - (float) i * 0.025F - (float) j * 0.01F; + float f2 = 0.05F + (float) i * 0.01F - (float) j * 0.01F; + + f1 = MathHelper.a(f1, 0.0F, 1.0F); + f2 = MathHelper.a(f2, 0.0F, 1.0F); + if (f < f1) { + this.owner.a(StatisticList.A, 1); + return ((PossibleFishingResult) WeightedRandom.a(this.random, (Collection) d)).a(this.random); + } else { + f -= f1; + if (f < f2) { + this.owner.a(StatisticList.B, 1); + return ((PossibleFishingResult) WeightedRandom.a(this.random, (Collection) e)).a(this.random); + } else { + float f3 = f - f2; + + this.owner.a(StatisticList.z, 1); + return ((PossibleFishingResult) WeightedRandom.a(this.random, (Collection) EntityFishingHook.f)).a(this.random); // CraftBukkit - fix static reference to fish list + } + } + } + + public void die() { + super.die(); + if (this.owner != null) { + this.owner.hookedFish = null; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityGhast.java b/vspigot-server/src/main/java/net/minecraft/server/EntityGhast.java new file mode 100644 index 0000000..a2e6f5f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityGhast.java @@ -0,0 +1,244 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.event.entity.EntityTargetEvent; +// CraftBukkit end + +public class EntityGhast extends EntityFlying implements IMonster { + + public int h; + public double i; + public double bm; + public double bn; + private Entity target; + private int br; + public int bo; + public int bp; + private int explosionPower = 1; + + public EntityGhast(World world) { + super(world); + this.a(4.0F, 4.0F); + this.fireProof = true; + this.b = 5; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else if ("fireball".equals(damagesource.p()) && damagesource.getEntity() instanceof EntityHuman) { + super.damageEntity(damagesource, 1000.0F); + ((EntityHuman) damagesource.getEntity()).a((Statistic) AchievementList.z); + return true; + } else { + return super.damageEntity(damagesource, f); + } + } + + protected void c() { + super.c(); + this.datawatcher.a(16, Byte.valueOf((byte) 0)); + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + } + + protected void bq() { + if (!this.world.isStatic && this.world.difficulty == EnumDifficulty.PEACEFUL) { + this.die(); + } + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + + this.w(); + this.bo = this.bp; + double d0 = this.i - this.locX; + double d1 = this.bm - this.locY; + double d2 = this.bn - this.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d3 < 1.0D || d3 > 3600.0D) { + this.i = this.locX + (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 16.0F); + this.bm = this.locY + (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 16.0F); + this.bn = this.locZ + (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 16.0F); + } + + if (this.h-- <= 0) { + this.h += this.random.nextInt(5) + 2; + d3 = (double) MathHelper.sqrt(d3); + if (this.a(this.i, this.bm, this.bn, d3)) { + this.motX += d0 / d3 * 0.1D; + this.motY += d1 / d3 * 0.1D; + this.motZ += d2 / d3 * 0.1D; + } else { + this.i = this.locX; + this.bm = this.locY; + this.bn = this.locZ; + } + } + + if (this.target != null && this.target.dead) { + // CraftBukkit start - fire EntityTargetEvent + EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), null, EntityTargetEvent.TargetReason.TARGET_DIED); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.target = null; + } else { + this.target = ((CraftEntity) event.getTarget()).getHandle(); + } + } + // CraftBukkit end + } + + if (this.target == null || this.br-- <= 0) { + // CraftBukkit start - fire EntityTargetEvent + Entity target = this.world.findNearbyVulnerablePlayer(this, 100.0D); + if (target != null) { + EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), target.getBukkitEntity(), EntityTargetEvent.TargetReason.CLOSEST_PLAYER); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.target = null; + } else { + this.target = ((CraftEntity) event.getTarget()).getHandle(); + } + } + } + // CraftBukkit end + + if (this.target != null) { + this.br = 20; + } + } + + double d4 = 64.0D; + + if (this.target != null && this.target.f((Entity) this) < d4 * d4) { + double d5 = this.target.locX - this.locX; + double d6 = this.target.boundingBox.b + (double) (this.target.length / 2.0F) - (this.locY + (double) (this.length / 2.0F)); + double d7 = this.target.locZ - this.locZ; + + this.aM = this.yaw = -((float) Math.atan2(d5, d7)) * 180.0F / 3.1415927F; + if (this.hasLineOfSight(this.target)) { + if (this.bp == 10) { + this.world.a((EntityHuman) null, 1007, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + } + + ++this.bp; + if (this.bp == 20) { + this.world.a((EntityHuman) null, 1008, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + EntityLargeFireball entitylargefireball = new EntityLargeFireball(this.world, this, d5, d6, d7); + + // CraftBukkit - set bukkitYield when setting explosionpower + entitylargefireball.bukkitYield = entitylargefireball.yield = this.explosionPower; + double d8 = 4.0D; + Vec3D vec3d = this.j(1.0F); + + entitylargefireball.locX = this.locX + vec3d.a * d8; + entitylargefireball.locY = this.locY + (double) (this.length / 2.0F) + 0.5D; + entitylargefireball.locZ = this.locZ + vec3d.c * d8; + this.world.addEntity(entitylargefireball); + this.bp = -40; + } + } else if (this.bp > 0) { + --this.bp; + } + } else { + this.aM = this.yaw = -((float) Math.atan2(this.motX, this.motZ)) * 180.0F / 3.1415927F; + if (this.bp > 0) { + --this.bp; + } + } + + if (!this.world.isStatic) { + byte b0 = this.datawatcher.getByte(16); + byte b1 = (byte) (this.bp > 10 ? 1 : 0); + + if (b0 != b1) { + this.datawatcher.watch(16, Byte.valueOf(b1)); + } + } + } + + private boolean a(double d0, double d1, double d2, double d3) { + double d4 = (this.i - this.locX) / d3; + double d5 = (this.bm - this.locY) / d3; + double d6 = (this.bn - this.locZ) / d3; + AxisAlignedBB axisalignedbb = this.boundingBox.clone(); + + for (int i = 1; (double) i < d3; ++i) { + axisalignedbb.d(d4, d5, d6); + if (!this.world.getCubes(this, axisalignedbb).isEmpty()) { + return false; + } + } + + return true; + } + + protected String t() { + return "mob.ghast.moan"; + } + + protected String aT() { + return "mob.ghast.scream"; + } + + protected String aU() { + return "mob.ghast.death"; + } + + protected Item getLoot() { + return Items.SULPHUR; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(2) + this.random.nextInt(1 + i); + + int k; + + for (k = 0; k < j; ++k) { + this.a(Items.GHAST_TEAR, 1); + } + + j = this.random.nextInt(3) + this.random.nextInt(1 + i); + + for (k = 0; k < j; ++k) { + this.a(Items.SULPHUR, 1); + } + } + + protected float bf() { + return 10.0F; + } + + public boolean canSpawn() { + // MineHQ - Add mobsEnabled check. + return this.random.nextInt(20) == 0 && super.canSpawn() && this.world.difficulty != EnumDifficulty.PEACEFUL && this.world.spigotConfig.mobsEnabled; + } + + public int bB() { + return 1; + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("ExplosionPower", this.explosionPower); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("ExplosionPower", 99)) { + this.explosionPower = nbttagcompound.getInt("ExplosionPower"); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityHanging.java b/vspigot-server/src/main/java/net/minecraft/server/EntityHanging.java new file mode 100644 index 0000000..106a15b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityHanging.java @@ -0,0 +1,340 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import org.bukkit.entity.Hanging; +import org.bukkit.entity.Painting; +import org.bukkit.event.hanging.HangingBreakEvent; +import org.bukkit.event.painting.PaintingBreakEvent; +// CraftBukkit end + +public abstract class EntityHanging extends Entity { + + private int e; + public int direction; + public int x; + public int y; + public int z; + + public EntityHanging(World world) { + super(world); + this.height = 0.0F; + this.a(0.5F, 0.5F); + } + + public EntityHanging(World world, int i, int j, int k, int l) { + this(world); + this.x = i; + this.y = j; + this.z = k; + } + + protected void c() {} + + public void setDirection(int i) { + this.direction = i; + this.lastYaw = this.yaw = (float) (i * 90); + float f = (float) this.f(); + float f1 = (float) this.i(); + float f2 = (float) this.f(); + + if (i != 2 && i != 0) { + f = 0.5F; + } else { + f2 = 0.5F; + this.yaw = this.lastYaw = (float) (Direction.f[i] * 90); + } + + f /= 32.0F; + f1 /= 32.0F; + f2 /= 32.0F; + float f3 = (float) this.x + 0.5F; + float f4 = (float) this.y + 0.5F; + float f5 = (float) this.z + 0.5F; + float f6 = 0.5625F; + + if (i == 2) { + f5 -= f6; + } + + if (i == 1) { + f3 -= f6; + } + + if (i == 0) { + f5 += f6; + } + + if (i == 3) { + f3 += f6; + } + + if (i == 2) { + f3 -= this.c(this.f()); + } + + if (i == 1) { + f5 += this.c(this.f()); + } + + if (i == 0) { + f3 += this.c(this.f()); + } + + if (i == 3) { + f5 -= this.c(this.f()); + } + + f4 += this.c(this.i()); + this.setPosition((double) f3, (double) f4, (double) f5); + float f7 = -0.03125F; + + this.boundingBox.b((double) (f3 - f - f7), (double) (f4 - f1 - f7), (double) (f5 - f2 - f7), (double) (f3 + f + f7), (double) (f4 + f1 + f7), (double) (f5 + f2 + f7)); + } + + private float c(int i) { + return i == 32 ? 0.5F : (i == 64 ? 0.5F : 0.0F); + } + + public void h() { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + if (this.e++ == this.world.spigotConfig.hangingTickFrequency && !this.world.isStatic) { // Spigot - 100 -> this.world.spigotConfig.hangingTickFrequency + this.e = 0; + if (!this.dead && !this.survives()) { + // CraftBukkit start - fire break events + Material material = this.world.getType((int) this.locX, (int) this.locY, (int) this.locZ).getMaterial(); + HangingBreakEvent.RemoveCause cause; + + if (!material.equals(Material.AIR)) { + // TODO: This feels insufficient to catch 100% of suffocation cases + cause = HangingBreakEvent.RemoveCause.OBSTRUCTION; + } else { + cause = HangingBreakEvent.RemoveCause.PHYSICS; + } + + HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), cause); + this.world.getServer().getPluginManager().callEvent(event); + + PaintingBreakEvent paintingEvent = null; + if (this instanceof EntityPainting) { + // Fire old painting event until it can be removed + paintingEvent = new PaintingBreakEvent((Painting) this.getBukkitEntity(), PaintingBreakEvent.RemoveCause.valueOf(cause.name())); + paintingEvent.setCancelled(event.isCancelled()); + this.world.getServer().getPluginManager().callEvent(paintingEvent); + } + + if (dead || event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { + return; + } + // CraftBukkit end + + this.die(); + this.b((Entity) null); + } + } + } + + public boolean survives() { + if (!this.world.getCubes(this, this.boundingBox).isEmpty()) { + return false; + } else { + int i = Math.max(1, this.f() / 16); + int j = Math.max(1, this.i() / 16); + int k = this.x; + int l = this.y; + int i1 = this.z; + + if (this.direction == 2) { + k = MathHelper.floor(this.locX - (double) ((float) this.f() / 32.0F)); + } + + if (this.direction == 1) { + i1 = MathHelper.floor(this.locZ - (double) ((float) this.f() / 32.0F)); + } + + if (this.direction == 0) { + k = MathHelper.floor(this.locX - (double) ((float) this.f() / 32.0F)); + } + + if (this.direction == 3) { + i1 = MathHelper.floor(this.locZ - (double) ((float) this.f() / 32.0F)); + } + + l = MathHelper.floor(this.locY - (double) ((float) this.i() / 32.0F)); + + for (int j1 = 0; j1 < i; ++j1) { + for (int k1 = 0; k1 < j; ++k1) { + Material material; + + if (this.direction != 2 && this.direction != 0) { + material = this.world.getType(this.x, l + k1, i1 + j1).getMaterial(); + } else { + material = this.world.getType(k + j1, l + k1, this.z).getMaterial(); + } + + if (!material.isBuildable()) { + return false; + } + } + } + + List list = this.world.getEntities(this, this.boundingBox); + Iterator iterator = list.iterator(); + + Entity entity; + + do { + if (!iterator.hasNext()) { + return true; + } + + entity = (Entity) iterator.next(); + } while (!(entity instanceof EntityHanging)); + + return false; + } + } + + public boolean R() { + return true; + } + + public boolean j(Entity entity) { + return entity instanceof EntityHuman ? this.damageEntity(DamageSource.playerAttack((EntityHuman) entity), 0.0F) : false; + } + + public void i(int i) { + this.world.X(); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + if (!this.dead && !this.world.isStatic) { + // CraftBukkit start - fire break events + HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.DEFAULT); + PaintingBreakEvent paintingEvent = null; + if (damagesource.getEntity() != null) { + event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((Hanging) this.getBukkitEntity(), damagesource.getEntity() == null ? null : damagesource.getEntity().getBukkitEntity()); + + if (this instanceof EntityPainting) { + // Fire old painting event until it can be removed + paintingEvent = new org.bukkit.event.painting.PaintingBreakByEntityEvent((Painting) this.getBukkitEntity(), damagesource.getEntity() == null ? null : damagesource.getEntity().getBukkitEntity()); + } + } else if (damagesource.isExplosion()) { + event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.EXPLOSION); + } + + this.world.getServer().getPluginManager().callEvent(event); + + if (paintingEvent != null) { + paintingEvent.setCancelled(event.isCancelled()); + this.world.getServer().getPluginManager().callEvent(paintingEvent); + } + + if (this.dead || event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { + return true; + } + // CraftBukkit end + + this.die(); + this.Q(); + this.b(damagesource.getEntity()); + } + + return true; + } + } + + public void move(double d0, double d1, double d2) { + if (!this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { + if (this.dead) return; // CraftBukkit + + // CraftBukkit start - fire break events + // TODO - Does this need its own cause? Seems to only be triggered by pistons + HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.PHYSICS); + this.world.getServer().getPluginManager().callEvent(event); + + if (this.dead || event.isCancelled()) { + return; + } + // CraftBukkit end + + this.die(); + this.b((Entity) null); + } + } + + public void g(double d0, double d1, double d2) { + if (false && !this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { // CraftBukkit - not needed + this.die(); + this.b((Entity) null); + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setByte("Direction", (byte) this.direction); + nbttagcompound.setInt("TileX", this.x); + nbttagcompound.setInt("TileY", this.y); + nbttagcompound.setInt("TileZ", this.z); + switch (this.direction) { + case 0: + nbttagcompound.setByte("Dir", (byte) 2); + break; + + case 1: + nbttagcompound.setByte("Dir", (byte) 1); + break; + + case 2: + nbttagcompound.setByte("Dir", (byte) 0); + break; + + case 3: + nbttagcompound.setByte("Dir", (byte) 3); + } + } + + public void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("Direction", 99)) { + this.direction = nbttagcompound.getByte("Direction"); + } else { + switch (nbttagcompound.getByte("Dir")) { + case 0: + this.direction = 2; + break; + + case 1: + this.direction = 1; + break; + + case 2: + this.direction = 0; + break; + + case 3: + this.direction = 3; + } + } + + this.x = nbttagcompound.getInt("TileX"); + this.y = nbttagcompound.getInt("TileY"); + this.z = nbttagcompound.getInt("TileZ"); + this.setDirection(this.direction); + } + + public abstract int f(); + + public abstract int i(); + + public abstract void b(Entity entity); + + protected boolean V() { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityHorse.java b/vspigot-server/src/main/java/net/minecraft/server/EntityHorse.java new file mode 100644 index 0000000..52cd498 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityHorse.java @@ -0,0 +1,1240 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; // CraftBukkit + +public class EntityHorse extends EntityAnimal implements IInventoryListener { + + private static final IEntitySelector bu = new EntitySelectorHorse(); + public static final IAttribute attributeJumpStrength = (new AttributeRanged("horse.jumpStrength", 0.7D, 0.0D, 2.0D)).a("Jump Strength").a(true); // CraftBukkit - private -> public + private static final String[] bw = new String[] { null, "textures/entity/horse/armor/horse_armor_iron.png", "textures/entity/horse/armor/horse_armor_gold.png", "textures/entity/horse/armor/horse_armor_diamond.png"}; + private static final String[] bx = new String[] { "", "meo", "goo", "dio"}; + private static final int[] by = new int[] { 0, 5, 7, 11}; + private static final String[] bz = new String[] { "textures/entity/horse/horse_white.png", "textures/entity/horse/horse_creamy.png", "textures/entity/horse/horse_chestnut.png", "textures/entity/horse/horse_brown.png", "textures/entity/horse/horse_black.png", "textures/entity/horse/horse_gray.png", "textures/entity/horse/horse_darkbrown.png"}; + private static final String[] bA = new String[] { "hwh", "hcr", "hch", "hbr", "hbl", "hgr", "hdb"}; + private static final String[] bB = new String[] { null, "textures/entity/horse/horse_markings_white.png", "textures/entity/horse/horse_markings_whitefield.png", "textures/entity/horse/horse_markings_whitedots.png", "textures/entity/horse/horse_markings_blackdots.png"}; + private static final String[] bC = new String[] { "", "wo_", "wmo", "wdo", "bdo"}; + private int bD; + private int bE; + private int bF; + public int bp; + public int bq; + protected boolean br; + public InventoryHorseChest inventoryChest; // CraftBukkit - private -> public + private boolean bH; + protected int bs; + protected float bt; + private boolean bI; + private float bJ; + private float bK; + private float bL; + private float bM; + private float bN; + private float bO; + private int bP; + private String bQ; + private String[] bR = new String[3]; + public int maxDomestication = 100; // CraftBukkit - store max domestication value + + public EntityHorse(World world) { + super(world); + this.a(1.4F, 1.6F); + this.fireProof = false; + this.setHasChest(false); + this.getNavigation().a(true); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.2D)); + this.goalSelector.a(1, new PathfinderGoalTame(this, 1.2D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalRandomStroll(this, 0.7D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.loadChest(); + } + + protected void c() { + super.c(); + this.datawatcher.a(16, Integer.valueOf(0)); + this.datawatcher.a(19, Byte.valueOf((byte) 0)); + this.datawatcher.a(20, Integer.valueOf(0)); + this.datawatcher.a(21, String.valueOf("")); + this.datawatcher.a(22, Integer.valueOf(0)); + } + + public void setType(int i) { + this.datawatcher.watch(19, Byte.valueOf((byte) i)); + this.cP(); + } + + public int getType() { + return this.datawatcher.getByte(19); + } + + public void setVariant(int i) { + this.datawatcher.watch(20, Integer.valueOf(i)); + this.cP(); + } + + public int getVariant() { + return this.datawatcher.getInt(20); + } + + public String getName() { + if (this.hasCustomName()) { + return this.getCustomName(); + } else { + int i = this.getType(); + + switch (i) { + case 0: + default: + return LocaleI18n.get("entity.horse.name"); + + case 1: + return LocaleI18n.get("entity.donkey.name"); + + case 2: + return LocaleI18n.get("entity.mule.name"); + + case 3: + return LocaleI18n.get("entity.zombiehorse.name"); + + case 4: + return LocaleI18n.get("entity.skeletonhorse.name"); + } + } + } + + private boolean x(int i) { + return (this.datawatcher.getInt(16) & i) != 0; + } + + private void b(int i, boolean flag) { + int j = this.datawatcher.getInt(16); + + if (flag) { + this.datawatcher.watch(16, Integer.valueOf(j | i)); + } else { + this.datawatcher.watch(16, Integer.valueOf(j & ~i)); + } + } + + public boolean cb() { + return !this.isBaby(); + } + + public boolean isTame() { + return this.x(2); + } + + public boolean cg() { + return this.cb(); + } + + public String getOwnerUUID() { + return this.datawatcher.getString(21); + } + + public void setOwnerUUID(String s) { + this.datawatcher.watch(21, s); + } + + public float ci() { + int i = this.getAge(); + + return i >= 0 ? 1.0F : 0.5F + (float) (-24000 - i) / -24000.0F * 0.5F; + } + + public void a(boolean flag) { + if (flag) { + this.a(this.ci()); + } else { + this.a(1.0F); + } + } + + public boolean cj() { + return this.br; + } + + public void setTame(boolean flag) { + this.b(2, flag); + } + + public void j(boolean flag) { + this.br = flag; + } + + public boolean bM() { + // PaperSpigot start - Configurable undead horse leashing + if (this.world.paperSpigotConfig.allowUndeadHorseLeashing) { + return super.bM(); + } else { + return !this.cE() && super.bM(); + } + // PaperSpigot end + } + + protected void o(float f) { + if (f > 6.0F && this.cm()) { + this.o(false); + } + } + + public boolean hasChest() { + return this.x(8); + } + + public int cl() { + return this.datawatcher.getInt(22); + } + + private int e(ItemStack itemstack) { + if (itemstack == null) { + return 0; + } else { + Item item = itemstack.getItem(); + + return item == Items.HORSE_ARMOR_IRON ? 1 : (item == Items.HORSE_ARMOR_GOLD ? 2 : (item == Items.HORSE_ARMOR_DIAMOND ? 3 : 0)); + } + } + + public boolean cm() { + return this.x(32); + } + + public boolean cn() { + return this.x(64); + } + + public boolean co() { + return this.x(16); + } + + public boolean cp() { + return this.bH; + } + + public void d(ItemStack itemstack) { + this.datawatcher.watch(22, Integer.valueOf(this.e(itemstack))); + this.cP(); + } + + public void k(boolean flag) { + this.b(16, flag); + } + + public void setHasChest(boolean flag) { + this.b(8, flag); + } + + public void m(boolean flag) { + this.bH = flag; + } + + public void n(boolean flag) { + this.b(4, flag); + } + + public int getTemper() { + return this.bs; + } + + public void setTemper(int i) { + this.bs = i; + } + + public int v(int i) { + int j = MathHelper.a(this.getTemper() + i, 0, this.getMaxDomestication()); + + this.setTemper(j); + return j; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + Entity entity = damagesource.getEntity(); + + return this.passenger != null && this.passenger.equals(entity) ? false : super.damageEntity(damagesource, f); + } + + public int aV() { + return by[this.cl()]; + } + + public boolean S() { + return this.passenger == null; + } + + public boolean cr() { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locZ); + + this.world.getBiome(i, j); + return true; + } + + public void cs() { + if (!this.world.isStatic && this.hasChest()) { + this.a(Item.getItemOf(Blocks.CHEST), 1); + this.setHasChest(false); + } + } + + private void cL() { + this.cS(); + this.world.makeSound(this, "eating", 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + } + + protected void b(float f) { + if (f > 1.0F) { + this.makeSound("mob.horse.land", 0.4F, 1.0F); + } + + int i = MathHelper.f(f * 0.5F - 3.0F); + + if (i > 0) { + this.damageEntity(DamageSource.FALL, (float) i); + if (this.passenger != null) { + this.passenger.damageEntity(DamageSource.FALL, (float) i); + } + + Block block = this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.locY - 0.2D - (double) this.lastYaw), MathHelper.floor(this.locZ)); + + if (block.getMaterial() != Material.AIR) { + StepSound stepsound = block.stepSound; + + this.world.makeSound(this, stepsound.getStepSound(), stepsound.getVolume1() * 0.5F, stepsound.getVolume2() * 0.75F); + } + } + } + + private int cM() { + int i = this.getType(); + + return this.hasChest() /* && (i == 1 || i == 2) */ ? 17 : 2; // CraftBukkit - Remove type check + } + + public void loadChest() { // CraftBukkit - private -> public + InventoryHorseChest inventoryhorsechest = this.inventoryChest; + + this.inventoryChest = new InventoryHorseChest("HorseChest", this.cM(), this); // CraftBukkit - add this horse + this.inventoryChest.a(this.getName()); + if (inventoryhorsechest != null) { + inventoryhorsechest.b(this); + int i = Math.min(inventoryhorsechest.getSize(), this.inventoryChest.getSize()); + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = inventoryhorsechest.getItem(j); + + if (itemstack != null) { + this.inventoryChest.setItem(j, itemstack.cloneItemStack()); + } + } + + inventoryhorsechest = null; + } + + this.inventoryChest.a(this); + this.cO(); + } + + private void cO() { + if (!this.world.isStatic) { + this.n(this.inventoryChest.getItem(0) != null); + if (this.cB()) { + this.d(this.inventoryChest.getItem(1)); + } + } + } + + public void a(InventorySubcontainer inventorysubcontainer) { + int i = this.cl(); + boolean flag = this.cu(); + + this.cO(); + if (this.ticksLived > 20) { + if (i == 0 && i != this.cl()) { + this.makeSound("mob.horse.armor", 0.5F, 1.0F); + } else if (i != this.cl()) { + this.makeSound("mob.horse.armor", 0.5F, 1.0F); + } + + if (!flag && this.cu()) { + this.makeSound("mob.horse.leather", 0.5F, 1.0F); + } + } + } + + public boolean canSpawn() { + this.cr(); + return super.canSpawn(); + } + + protected EntityHorse a(Entity entity, double d0) { + double d1 = Double.MAX_VALUE; + Entity entity1 = null; + List list = this.world.getEntities(entity, entity.boundingBox.a(d0, d0, d0), bu); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity2 = (Entity) iterator.next(); + double d2 = entity2.e(entity.locX, entity.locY, entity.locZ); + + if (d2 < d1) { + entity1 = entity2; + d1 = d2; + } + } + + return (EntityHorse) entity1; + } + + public double getJumpStrength() { + return this.getAttributeInstance(attributeJumpStrength).getValue(); + } + + protected String aU() { + this.cS(); + int i = this.getType(); + + return i == 3 ? "mob.horse.zombie.death" : (i == 4 ? "mob.horse.skeleton.death" : (i != 1 && i != 2 ? "mob.horse.death" : "mob.horse.donkey.death")); + } + + protected Item getLoot() { + boolean flag = this.random.nextInt(4) == 0; + int i = this.getType(); + + return i == 4 ? Items.BONE : (i == 3 ? (flag ? Item.getById(0) : Items.ROTTEN_FLESH) : Items.LEATHER); + } + + protected String aT() { + this.cS(); + if (this.random.nextInt(3) == 0) { + this.cU(); + } + + int i = this.getType(); + + return i == 3 ? "mob.horse.zombie.hit" : (i == 4 ? "mob.horse.skeleton.hit" : (i != 1 && i != 2 ? "mob.horse.hit" : "mob.horse.donkey.hit")); + } + + public boolean cu() { + return this.x(4); + } + + protected String t() { + this.cS(); + if (this.random.nextInt(10) == 0 && !this.bh()) { + this.cU(); + } + + int i = this.getType(); + + return i == 3 ? "mob.horse.zombie.idle" : (i == 4 ? "mob.horse.skeleton.idle" : (i != 1 && i != 2 ? "mob.horse.idle" : "mob.horse.donkey.idle")); + } + + protected String cv() { + this.cS(); + this.cU(); + int i = this.getType(); + + return i != 3 && i != 4 ? (i != 1 && i != 2 ? "mob.horse.angry" : "mob.horse.donkey.angry") : null; + } + + protected void a(int i, int j, int k, Block block) { + StepSound stepsound = block.stepSound; + + if (this.world.getType(i, j + 1, k) == Blocks.SNOW) { + stepsound = Blocks.SNOW.stepSound; + } + + if (!block.getMaterial().isLiquid()) { + int l = this.getType(); + + if (this.passenger != null && l != 1 && l != 2) { + ++this.bP; + if (this.bP > 5 && this.bP % 3 == 0) { + this.makeSound("mob.horse.gallop", stepsound.getVolume1() * 0.15F, stepsound.getVolume2()); + if (l == 0 && this.random.nextInt(10) == 0) { + this.makeSound("mob.horse.breathe", stepsound.getVolume1() * 0.6F, stepsound.getVolume2()); + } + } else if (this.bP <= 5) { + this.makeSound("mob.horse.wood", stepsound.getVolume1() * 0.15F, stepsound.getVolume2()); + } + } else if (stepsound == Block.f) { + this.makeSound("mob.horse.wood", stepsound.getVolume1() * 0.15F, stepsound.getVolume2()); + } else { + this.makeSound("mob.horse.soft", stepsound.getVolume1() * 0.15F, stepsound.getVolume2()); + } + } + } + + protected void aD() { + super.aD(); + this.getAttributeMap().b(attributeJumpStrength); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(53.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.22499999403953552D); + } + + public int bB() { + return 6; + } + + public int getMaxDomestication() { + return this.maxDomestication; // CraftBukkit - return stored max domestication instead of 100 + } + + protected float bf() { + return 0.8F; + } + + public int q() { + return 400; + } + + private void cP() { + this.bQ = null; + } + + public void g(EntityHuman entityhuman) { + if (!this.world.isStatic && (this.passenger == null || this.passenger == entityhuman) && this.isTame()) { + this.inventoryChest.a(this.getName()); + entityhuman.openHorseInventory(this, this.inventoryChest); + } + } + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() == Items.MONSTER_EGG) { + return super.a(entityhuman); + } else if (!this.isTame() && this.cE()) { + return false; + } else if (this.isTame() && this.cb() && entityhuman.isSneaking()) { + this.g(entityhuman); + return true; + } else if (this.cg() && this.passenger != null) { + return super.a(entityhuman); + } else { + if (itemstack != null) { + boolean flag = false; + + if (this.cB()) { + byte b0 = -1; + + if (itemstack.getItem() == Items.HORSE_ARMOR_IRON) { + b0 = 1; + } else if (itemstack.getItem() == Items.HORSE_ARMOR_GOLD) { + b0 = 2; + } else if (itemstack.getItem() == Items.HORSE_ARMOR_DIAMOND) { + b0 = 3; + } + + if (b0 >= 0) { + if (!this.isTame()) { + this.cJ(); + return true; + } + + this.g(entityhuman); + return true; + } + } + + if (!flag && !this.cE()) { + float f = 0.0F; + short short1 = 0; + byte b1 = 0; + + if (itemstack.getItem() == Items.WHEAT) { + f = 2.0F; + short1 = 60; + b1 = 3; + } else if (itemstack.getItem() == Items.SUGAR) { + f = 1.0F; + short1 = 30; + b1 = 3; + } else if (itemstack.getItem() == Items.BREAD) { + f = 7.0F; + short1 = 180; + b1 = 3; + } else if (Block.a(itemstack.getItem()) == Blocks.HAY_BLOCK) { + f = 20.0F; + short1 = 180; + } else if (itemstack.getItem() == Items.APPLE) { + f = 3.0F; + short1 = 60; + b1 = 3; + } else if (itemstack.getItem() == Items.CARROT_GOLDEN) { + f = 4.0F; + short1 = 60; + b1 = 5; + if (this.isTame() && this.getAge() == 0) { + flag = true; + this.f(entityhuman); + } + } else if (itemstack.getItem() == Items.GOLDEN_APPLE) { + f = 10.0F; + short1 = 240; + b1 = 10; + if (this.isTame() && this.getAge() == 0) { + flag = true; + this.f(entityhuman); + } + } + + if (this.getHealth() < this.getMaxHealth() && f > 0.0F) { + this.heal(f, RegainReason.EATING); // CraftBukkit + flag = true; + } + + if (!this.cb() && short1 > 0) { + this.a(short1); + flag = true; + } + + if (b1 > 0 && (flag || !this.isTame()) && b1 < this.getMaxDomestication()) { + flag = true; + this.v(b1); + } + + if (flag) { + this.cL(); + } + } + + if (!this.isTame() && !flag) { + if (itemstack != null && itemstack.a(entityhuman, (EntityLiving) this)) { + return true; + } + + this.cJ(); + return true; + } + + if (!flag && this.cC() && !this.hasChest() && itemstack.getItem() == Item.getItemOf(Blocks.CHEST)) { + this.setHasChest(true); + this.makeSound("mob.chickenplop", 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + flag = true; + this.loadChest(); + } + + if (!flag && this.cg() && !this.cu() && itemstack.getItem() == Items.SADDLE) { + this.g(entityhuman); + return true; + } + + if (flag) { + if (!entityhuman.abilities.canInstantlyBuild && --itemstack.count <= 0) { // EMC + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + + return true; + } + } + + if (this.cg() && this.passenger == null) { + if (itemstack != null && itemstack.a(entityhuman, (EntityLiving) this)) { + return true; + } else { + this.i(entityhuman); + return true; + } + } else { + return super.a(entityhuman); + } + } + } + + private void i(EntityHuman entityhuman) { + entityhuman.yaw = this.yaw; + entityhuman.pitch = this.pitch; + this.o(false); + this.p(false); + if (!this.world.isStatic) { + entityhuman.mount(this); + } + } + + public boolean cB() { + return this.getType() == 0; + } + + public boolean cC() { + int i = this.getType(); + + return i == 2 || i == 1; + } + + protected boolean bh() { + return this.passenger != null && this.cu() ? true : this.cm() || this.cn(); + } + + public boolean cE() { + int i = this.getType(); + + return i == 3 || i == 4; + } + + public boolean cF() { + return this.cE() || this.getType() == 2; + } + + public boolean c(ItemStack itemstack) { + return false; + } + + private void cR() { + this.bp = 1; + } + + public void die(DamageSource damagesource) { + super.die(damagesource); + /* CraftBukkit start - Handle chest dropping in dropDeathLoot below + if (!this.world.isStatic) { + this.dropChest(); + } + // CraftBukkit end */ + } + + // CraftBukkit start - Add method + protected void dropDeathLoot(boolean flag, int i) { + super.dropDeathLoot(flag, i); + + // Moved from die method above + if (!this.world.isStatic) { + this.dropChest(); + } + } + // CraftBukkit end + + public void e() { + if (this.random.nextInt(200) == 0) { + this.cR(); + } + + super.e(); + if (!this.world.isStatic) { + if (this.random.nextInt(900) == 0 && this.deathTicks == 0) { + this.heal(1.0F, RegainReason.REGEN); // CraftBukkit + } + + if (!this.cm() && this.passenger == null && this.random.nextInt(300) == 0 && this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.locY) - 1, MathHelper.floor(this.locZ)) == Blocks.GRASS) { + this.o(true); + } + + if (this.cm() && ++this.bD > 50) { + this.bD = 0; + this.o(false); + } + + if (this.co() && !this.cb() && !this.cm()) { + EntityHorse entityhorse = this.a(this, 16.0D); + + if (entityhorse != null && this.f(entityhorse) > 4.0D) { + PathEntity pathentity = this.world.findPath(this, entityhorse, 16.0F, true, false, false, true); + + this.setPathEntity(pathentity); + } + } + } + } + + public void h() { + super.h(); + if (this.world.isStatic && this.datawatcher.a()) { + this.datawatcher.e(); + this.cP(); + } + + if (this.bE > 0 && ++this.bE > 30) { + this.bE = 0; + this.b(128, false); + } + + if (!this.world.isStatic && this.bF > 0 && ++this.bF > 20) { + this.bF = 0; + this.p(false); + } + + if (this.bp > 0 && ++this.bp > 8) { + this.bp = 0; + } + + if (this.bq > 0) { + ++this.bq; + if (this.bq > 300) { + this.bq = 0; + } + } + + this.bK = this.bJ; + if (this.cm()) { + this.bJ += (1.0F - this.bJ) * 0.4F + 0.05F; + if (this.bJ > 1.0F) { + this.bJ = 1.0F; + } + } else { + this.bJ += (0.0F - this.bJ) * 0.4F - 0.05F; + if (this.bJ < 0.0F) { + this.bJ = 0.0F; + } + } + + this.bM = this.bL; + if (this.cn()) { + this.bK = this.bJ = 0.0F; + this.bL += (1.0F - this.bL) * 0.4F + 0.05F; + if (this.bL > 1.0F) { + this.bL = 1.0F; + } + } else { + this.bI = false; + this.bL += (0.8F * this.bL * this.bL * this.bL - this.bL) * 0.6F - 0.05F; + if (this.bL < 0.0F) { + this.bL = 0.0F; + } + } + + this.bO = this.bN; + if (this.x(128)) { + this.bN += (1.0F - this.bN) * 0.7F + 0.05F; + if (this.bN > 1.0F) { + this.bN = 1.0F; + } + } else { + this.bN += (0.0F - this.bN) * 0.7F - 0.05F; + if (this.bN < 0.0F) { + this.bN = 0.0F; + } + } + } + + private void cS() { + if (!this.world.isStatic) { + this.bE = 1; + this.b(128, true); + } + } + + private boolean cT() { + return this.passenger == null && this.vehicle == null && this.isTame() && this.cb() && !this.cF() && this.getHealth() >= this.getMaxHealth(); + } + + public void e(boolean flag) { + this.b(32, flag); + } + + public void o(boolean flag) { + this.e(flag); + } + + public void p(boolean flag) { + if (flag) { + this.o(false); + } + + this.b(64, flag); + } + + private void cU() { + if (!this.world.isStatic) { + this.bF = 1; + this.p(true); + } + } + + public void cJ() { + this.cU(); + String s = this.cv(); + + if (s != null) { + this.makeSound(s, this.bf(), this.bg()); + } + } + + public void dropChest() { + this.a(this, this.inventoryChest); + this.cs(); + } + + private void a(Entity entity, InventoryHorseChest inventoryhorsechest) { + if (inventoryhorsechest != null && !this.world.isStatic) { + for (int i = 0; i < inventoryhorsechest.getSize(); ++i) { + ItemStack itemstack = inventoryhorsechest.getItem(i); + + if (itemstack != null) { + this.a(itemstack, 0.0F); + } + } + } + } + + public boolean h(EntityHuman entityhuman) { + this.setOwnerUUID(entityhuman.getUniqueID().toString()); + this.setTame(true); + return true; + } + + public void e(float f, float f1) { + if (this.passenger != null && this.passenger instanceof EntityLiving && this.cu()) { + this.lastYaw = this.yaw = this.passenger.yaw; + this.pitch = this.passenger.pitch * 0.5F; + this.b(this.yaw, this.pitch); + this.aO = this.aM = this.yaw; + f = ((EntityLiving) this.passenger).bd * 0.5F; + f1 = ((EntityLiving) this.passenger).be; + if (f1 <= 0.0F) { + f1 *= 0.25F; + this.bP = 0; + } + + if (this.onGround && this.bt == 0.0F && this.cn() && !this.bI) { + f = 0.0F; + f1 = 0.0F; + } + + if (this.bt > 0.0F && !this.cj() && this.onGround) { + this.motY = this.getJumpStrength() * (double) this.bt; + if (this.hasEffect(MobEffectList.JUMP)) { + this.motY += (double) ((float) (this.getEffect(MobEffectList.JUMP).getAmplifier() + 1) * 0.1F); + } + + this.j(true); + this.al = true; + if (f1 > 0.0F) { + float f2 = MathHelper.sin(this.yaw * 3.1415927F / 180.0F); + float f3 = MathHelper.cos(this.yaw * 3.1415927F / 180.0F); + + this.motX += (double) (-0.4F * f2 * this.bt); + this.motZ += (double) (0.4F * f3 * this.bt); + this.makeSound("mob.horse.jump", 0.4F, 1.0F); + } + + this.bt = 0.0F; + } + + this.W = 1.0F; + this.aQ = this.bl() * 0.1F; + if (!this.world.isStatic) { + this.i((float) this.getAttributeInstance(GenericAttributes.d).getValue()); + super.e(f, f1); + } + + if (this.onGround) { + this.bt = 0.0F; + this.j(false); + } + + this.aE = this.aF; + double d0 = this.locX - this.lastX; + double d1 = this.locZ - this.lastZ; + float f4 = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F; + + if (f4 > 1.0F) { + f4 = 1.0F; + } + + this.aF += (f4 - this.aF) * 0.4F; + this.aG += this.aF; + } else { + this.W = 0.5F; + this.aQ = 0.02F; + super.e(f, f1); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("EatingHaystack", this.cm()); + nbttagcompound.setBoolean("ChestedHorse", this.hasChest()); + nbttagcompound.setBoolean("HasReproduced", this.cp()); + nbttagcompound.setBoolean("Bred", this.co()); + nbttagcompound.setInt("Type", this.getType()); + nbttagcompound.setInt("Variant", this.getVariant()); + nbttagcompound.setInt("Temper", this.getTemper()); + nbttagcompound.setBoolean("Tame", this.isTame()); + nbttagcompound.setString("OwnerUUID", this.getOwnerUUID()); + nbttagcompound.setInt("Bukkit.MaxDomestication", this.maxDomestication); // CraftBukkit + if (this.hasChest()) { + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 2; i < this.inventoryChest.getSize(); ++i) { + ItemStack itemstack = this.inventoryChest.getItem(i); + + if (itemstack != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + itemstack.save(nbttagcompound1); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + } + + if (this.inventoryChest.getItem(1) != null) { + nbttagcompound.set("ArmorItem", this.inventoryChest.getItem(1).save(new NBTTagCompound())); + } + + if (this.inventoryChest.getItem(0) != null) { + nbttagcompound.set("SaddleItem", this.inventoryChest.getItem(0).save(new NBTTagCompound())); + } + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.o(nbttagcompound.getBoolean("EatingHaystack")); + this.k(nbttagcompound.getBoolean("Bred")); + this.setHasChest(nbttagcompound.getBoolean("ChestedHorse")); + this.m(nbttagcompound.getBoolean("HasReproduced")); + this.setType(nbttagcompound.getInt("Type")); + this.setVariant(nbttagcompound.getInt("Variant")); + this.setTemper(nbttagcompound.getInt("Temper")); + this.setTame(nbttagcompound.getBoolean("Tame")); + if (nbttagcompound.hasKeyOfType("OwnerUUID", 8)) { + this.setOwnerUUID(nbttagcompound.getString("OwnerUUID")); + } + // Spigot start + else if (nbttagcompound.hasKey("OwnerName")) { + String owner = nbttagcompound.getString("OwnerName"); + if (owner != null && !owner.isEmpty()) { + this.setOwnerUUID(NameReferencingFileConverter.a(owner)); + } + } + // Spigot end + // CraftBukkit start + if (nbttagcompound.hasKey("Bukkit.MaxDomestication")) { + this.maxDomestication = nbttagcompound.getInt("Bukkit.MaxDomestication"); + } + // CraftBukkit end + AttributeInstance attributeinstance = this.getAttributeMap().a("Speed"); + + if (attributeinstance != null) { + this.getAttributeInstance(GenericAttributes.d).setValue(attributeinstance.b() * 0.25D); + } + + if (this.hasChest()) { + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.loadChest(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + int j = nbttagcompound1.getByte("Slot") & 255; + + if (j >= 2 && j < this.inventoryChest.getSize()) { + this.inventoryChest.setItem(j, ItemStack.createStack(nbttagcompound1)); + } + } + } + + ItemStack itemstack; + + if (nbttagcompound.hasKeyOfType("ArmorItem", 10)) { + itemstack = ItemStack.createStack(nbttagcompound.getCompound("ArmorItem")); + if (itemstack != null && a(itemstack.getItem())) { + this.inventoryChest.setItem(1, itemstack); + } + } + + if (nbttagcompound.hasKeyOfType("SaddleItem", 10)) { + itemstack = ItemStack.createStack(nbttagcompound.getCompound("SaddleItem")); + if (itemstack != null && itemstack.getItem() == Items.SADDLE) { + this.inventoryChest.setItem(0, itemstack); + } + } else if (nbttagcompound.getBoolean("Saddle")) { + this.inventoryChest.setItem(0, new ItemStack(Items.SADDLE)); + } + + this.cO(); + } + + public boolean mate(EntityAnimal entityanimal) { + if (entityanimal == this) { + return false; + } else if (entityanimal.getClass() != this.getClass()) { + return false; + } else { + EntityHorse entityhorse = (EntityHorse) entityanimal; + + if (this.cT() && entityhorse.cT()) { + int i = this.getType(); + int j = entityhorse.getType(); + + return i == j || i == 0 && j == 1 || i == 1 && j == 0; + } else { + return false; + } + } + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + EntityHorse entityhorse = (EntityHorse) entityageable; + EntityHorse entityhorse1 = new EntityHorse(this.world); + int i = this.getType(); + int j = entityhorse.getType(); + int k = 0; + + if (i == j) { + k = i; + } else if (i == 0 && j == 1 || i == 1 && j == 0) { + k = 2; + } + + if (k == 0) { + int l = this.random.nextInt(9); + int i1; + + if (l < 4) { + i1 = this.getVariant() & 255; + } else if (l < 8) { + i1 = entityhorse.getVariant() & 255; + } else { + i1 = this.random.nextInt(7); + } + + int j1 = this.random.nextInt(5); + + if (j1 < 2) { + i1 |= this.getVariant() & '\uff00'; + } else if (j1 < 4) { + i1 |= entityhorse.getVariant() & '\uff00'; + } else { + i1 |= this.random.nextInt(5) << 8 & '\uff00'; + } + + entityhorse1.setVariant(i1); + } + + entityhorse1.setType(k); + double d0 = this.getAttributeInstance(GenericAttributes.maxHealth).b() + entityageable.getAttributeInstance(GenericAttributes.maxHealth).b() + (double) this.cV(); + + entityhorse1.getAttributeInstance(GenericAttributes.maxHealth).setValue(d0 / 3.0D); + double d1 = this.getAttributeInstance(attributeJumpStrength).b() + entityageable.getAttributeInstance(attributeJumpStrength).b() + this.cW(); + + entityhorse1.getAttributeInstance(attributeJumpStrength).setValue(d1 / 3.0D); + double d2 = this.getAttributeInstance(GenericAttributes.d).b() + entityageable.getAttributeInstance(GenericAttributes.d).b() + this.cX(); + + entityhorse1.getAttributeInstance(GenericAttributes.d).setValue(d2 / 3.0D); + return entityhorse1; + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + Object object = super.prepare(groupdataentity); + boolean flag = false; + int i = 0; + int j; + + if (object instanceof GroupDataHorse) { + j = ((GroupDataHorse) object).a; + i = ((GroupDataHorse) object).b & 255 | this.random.nextInt(5) << 8; + } else { + if (this.random.nextInt(10) == 0) { + j = 1; + } else { + int k = this.random.nextInt(7); + int l = this.random.nextInt(5); + + j = 0; + i = k | l << 8; + } + + object = new GroupDataHorse(j, i); + } + + this.setType(j); + this.setVariant(i); + if (this.random.nextInt(5) == 0) { + this.setAge(-24000); + } + + if (j != 4 && j != 3) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) this.cV()); + if (j == 0) { + this.getAttributeInstance(GenericAttributes.d).setValue(this.cX()); + } else { + this.getAttributeInstance(GenericAttributes.d).setValue(0.17499999701976776D); + } + } else { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(15.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.20000000298023224D); + } + + if (j != 2 && j != 1) { + this.getAttributeInstance(attributeJumpStrength).setValue(this.cW()); + } else { + this.getAttributeInstance(attributeJumpStrength).setValue(0.5D); + } + + this.setHealth(this.getMaxHealth()); + return (GroupDataEntity) object; + } + + protected boolean bk() { + return true; + } + + public void w(int i) { + if (this.cu()) { + // CraftBukkit start - fire HorseJumpEvent, use event power + if (i < 0) { + i = 0; + } + + float power; + if (i >= 90) { + power = 1.0F; + } else { + power = 0.4F + 0.4F * (float) i / 90.0F; + } + + org.bukkit.event.entity.HorseJumpEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callHorseJumpEvent(this, power); + if (!event.isCancelled()) { + this.bI = true; + this.cU(); + this.bt = event.getPower(); + } + // CraftBukkit end + } + } + + public void ac() { + super.ac(); + if (this.bM > 0.0F) { + float f = MathHelper.sin(this.aM * 3.1415927F / 180.0F); + float f1 = MathHelper.cos(this.aM * 3.1415927F / 180.0F); + float f2 = 0.7F * this.bM; + float f3 = 0.15F * this.bM; + + this.passenger.setPosition(this.locX + (double) (f2 * f), this.locY + this.ad() + this.passenger.ad() + (double) f3, this.locZ - (double) (f2 * f1)); + if (this.passenger instanceof EntityLiving) { + ((EntityLiving) this.passenger).aM = this.aM; + } + } + } + + private float cV() { + return 15.0F + (float) this.random.nextInt(8) + (float) this.random.nextInt(9); + } + + private double cW() { + return 0.4000000059604645D + this.random.nextDouble() * 0.2D + this.random.nextDouble() * 0.2D + this.random.nextDouble() * 0.2D; + } + + private double cX() { + return (0.44999998807907104D + this.random.nextDouble() * 0.3D + this.random.nextDouble() * 0.3D + this.random.nextDouble() * 0.3D) * 0.25D; + } + + public static boolean a(Item item) { + return item == Items.HORSE_ARMOR_IRON || item == Items.HORSE_ARMOR_GOLD || item == Items.HORSE_ARMOR_DIAMOND; + } + + public boolean h_() { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityHuman.java b/vspigot-server/src/main/java/net/minecraft/server/EntityHuman.java new file mode 100644 index 0000000..db315f3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityHuman.java @@ -0,0 +1,1679 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import net.valorhcf.knockback.Knockback; +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.entity.CraftItem; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.inventory.EquipmentSetEvent; +import org.bukkit.event.player.*; +// CraftBukkit end +import org.bukkit.util.Vector; +import org.spigotmc.ProtocolData; // Spigot - protocol patch +import org.spigotmc.SpigotConfig; + +public abstract class EntityHuman extends EntityLiving implements ICommandListener { + + public PlayerInventory inventory = new PlayerInventory(this); + private InventoryEnderChest enderChest = new InventoryEnderChest(); + public Container defaultContainer; + public Container activeContainer; + protected FoodMetaData foodData = new FoodMetaData(this); // CraftBukkit - add "this" to constructor + protected int bq; + public float br; + public float bs; + public int bt; + public double bu; + public double bv; + public double bw; + public double bx; + public double by; + public double bz; + // CraftBukkit start + public boolean sleeping; // protected -> public + public boolean fauxSleeping; + public String spawnWorld = ""; + public boolean affectsSpawning = true; // PaperSpigot + + @Override + public CraftHumanEntity getBukkitEntity() { + return (CraftHumanEntity) super.getBukkitEntity(); + } + // CraftBukkit end + + public ChunkCoordinates bB; + public int sleepTicks; // CraftBukkit - private -> public + public float bC; + public float bD; + private ChunkCoordinates c; + private boolean d; + private ChunkCoordinates e; + public PlayerAbilities abilities = new PlayerAbilities(); + public int oldLevel = -1; // CraftBukkit - add field + public int expLevel; + public int expTotal; + public float exp; + private ItemStack f; + private int g; + protected float bI = 0.1F; + protected float bJ = 0.02F; + private int h; + private final GameProfile i; + public EntityFishingHook hookedFish; + + public EntityHuman(World world, GameProfile gameprofile) { + super(world); + this.uniqueID = a(gameprofile); + this.i = gameprofile; + this.defaultContainer = new ContainerPlayer(this.inventory, !world.isStatic, this); + this.activeContainer = this.defaultContainer; + this.height = 1.62F; + ChunkCoordinates chunkcoordinates = world.getSpawn(); + + this.setPositionRotation((double) chunkcoordinates.x + 0.5D, (double) (chunkcoordinates.y + 1), (double) chunkcoordinates.z + 0.5D, world.getWorldData().getSpawnYaw(), world.getWorldData().getSpawnPitch()); // Poweruser + this.aZ = 180.0F; + this.maxFireTicks = 20; + } + + protected void aD() { + super.aD(); + this.getAttributeMap().b(GenericAttributes.e).setValue(1.0D); + } + + protected void c() { + super.c(); + this.datawatcher.a(16, new ProtocolData.DualByte((byte) 0, (byte) 0)); // Spigot - protocol patch, handle metadata usage change (show cape -> collisions) + this.datawatcher.a(17, Float.valueOf(0.0F)); + this.datawatcher.a(18, Integer.valueOf(0)); + this.datawatcher.a(10, new ProtocolData.HiddenByte((byte) 0)); // Spigot - protocol patch, handle new metadata value + } + + public boolean by() { + return this.f != null; + } + + public void bA() { + if (this.f != null) { + this.f.b(this.world, this, this.g); + } + + this.bB(); + } + + public void bB() { + this.f = null; + this.g = 0; + if (!this.world.isStatic) { + this.e(false); + } + } + + public boolean isBlocking() { + return this.by() && this.f.getItem().d(this.f) == EnumAnimation.BLOCK; + } + + public void h() { + if (this.f != null) { + ItemStack itemstack = this.inventory.getItemInHand(); + + if (itemstack == this.f) { + if (this.g <= 25 && this.g % 4 == 0) { + this.c(itemstack, 5); + } + + if (--this.g == 0 && !this.world.isStatic) { + this.p(); + } + } else { + this.bB(); + } + } + + if (this.bt > 0) { + --this.bt; + } + + if (this.isSleeping()) { + ++this.sleepTicks; + if (this.sleepTicks > 100) { + this.sleepTicks = 100; + } + + if (!this.world.isStatic) { + if (!this.j()) { + this.a(true, true, false); + } else if (this.world.w()) { + this.a(false, true, true); + } + } + } else if (this.sleepTicks > 0) { + ++this.sleepTicks; + if (this.sleepTicks >= 110) { + this.sleepTicks = 0; + } + } + + super.h(); + if (!this.world.isStatic && this.activeContainer != null && !this.activeContainer.a(this)) { + this.closeInventory(); + this.activeContainer = this.defaultContainer; + } + + if (this.isBurning() && this.abilities.isInvulnerable) { + this.extinguish(); + } + + this.bu = this.bx; + this.bv = this.by; + this.bw = this.bz; + double d0 = this.locX - this.bx; + double d1 = this.locY - this.by; + double d2 = this.locZ - this.bz; + double d3 = 10.0D; + + if (d0 > d3) { + this.bu = this.bx = this.locX; + } + + if (d2 > d3) { + this.bw = this.bz = this.locZ; + } + + if (d1 > d3) { + this.bv = this.by = this.locY; + } + + if (d0 < -d3) { + this.bu = this.bx = this.locX; + } + + if (d2 < -d3) { + this.bw = this.bz = this.locZ; + } + + if (d1 < -d3) { + this.bv = this.by = this.locY; + } + + this.bx += d0 * 0.25D; + this.bz += d2 * 0.25D; + this.by += d1 * 0.25D; + if (this.vehicle == null) { + this.e = null; + } + + if (!this.world.isStatic) { + this.foodData.a(this); + this.a(StatisticList.g, 1); + } + } + + public int D() { + return this.abilities.isInvulnerable ? 0 : 80; + } + + protected String H() { + return "game.player.swim"; + } + + protected String O() { + return "game.player.swim.splash"; + } + + public int ai() { + return 10; + } + + public void makeSound(String s, float f, float f1) { + this.world.a(this, s, f, f1); + } + + protected void c(ItemStack itemstack, int i) { + if (itemstack.o() == EnumAnimation.DRINK) { + this.makeSound("random.drink", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + + if (itemstack.o() == EnumAnimation.EAT) { + for (int j = 0; j < i; ++j) { + Vec3D vec3d = Vec3D.a(((double) this.random.nextFloat() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, 0.0D); + + vec3d.a(-this.pitch * 3.1415927F / 180.0F); + vec3d.b(-this.yaw * 3.1415927F / 180.0F); + Vec3D vec3d1 = Vec3D.a(((double) this.random.nextFloat() - 0.5D) * 0.3D, (double) (-this.random.nextFloat()) * 0.6D - 0.3D, 0.6D); + + vec3d1.a(-this.pitch * 3.1415927F / 180.0F); + vec3d1.b(-this.yaw * 3.1415927F / 180.0F); + vec3d1 = vec3d1.add(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ); + String s = "iconcrack_" + Item.getId(itemstack.getItem()); + + if (itemstack.usesData()) { + s = s + "_" + itemstack.getData(); + } + + this.world.addParticle(s, vec3d1.a, vec3d1.b, vec3d1.c, vec3d.a, vec3d.b + 0.05D, vec3d.c); + } + + this.makeSound("random.eat", 0.5F + 0.5F * (float) this.random.nextInt(2), (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + } + } + + protected void p() { + if (this.f != null) { + this.c(this.f, 16); + int i = this.f.count; + + // CraftBukkit start - fire PlayerItemConsumeEvent + org.bukkit.inventory.ItemStack craftItem = CraftItemStack.asBukkitCopy(this.f); + PlayerItemConsumeEvent event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + // Update client + if (this instanceof EntityPlayer) { + ((EntityPlayer) this).playerConnection.sendPacket(new PacketPlayOutSetSlot((byte) 0, activeContainer.getSlot((IInventory) this.inventory, this.inventory.itemInHandIndex).index, this.f)); + // Spigot Start + ((EntityPlayer) this).getBukkitEntity().updateInventory(); + ((EntityPlayer) this).getBukkitEntity().updateScaledHealth(); + // Spigot End + } + this.g = this.f.n(); // Poweruser + return; + } + + // Plugin modified the item, process it but don't remove it + if (!craftItem.equals(event.getItem())) { + CraftItemStack.asNMSCopy(event.getItem()).b(this.world, this); + + // Update client + if (this instanceof EntityPlayer) { + ((EntityPlayer) this).playerConnection.sendPacket(new PacketPlayOutSetSlot((byte) 0, activeContainer.getSlot((IInventory) this.inventory, this.inventory.itemInHandIndex).index, this.f)); + } + return; + } + // CraftBukkit end + + ItemStack itemstack = this.f.b(this.world, this); + + if (itemstack != this.f || itemstack != null && itemstack.count != i) { + this.inventory.items[this.inventory.itemInHandIndex] = itemstack; + if (itemstack.count == 0) { + this.inventory.items[this.inventory.itemInHandIndex] = null; + } + } + + this.bB(); + } + } + + protected boolean bh() { + return this.getHealth() <= 0.0F || this.isSleeping(); + } + + // CraftBukkit - protected -> public + public void closeInventory() { + this.activeContainer = this.defaultContainer; + } + + public void mount(Entity entity) { + // CraftBukkit start - mirror Entity mount changes + this.setPassengerOf(entity); + } + + public void setPassengerOf(Entity entity) { + // CraftBukkit end + if (this.vehicle != null && entity == null) { + world.getServer().getPluginManager().callEvent(new org.spigotmc.event.entity.EntityDismountEvent(this.getBukkitEntity(), this.vehicle.getBukkitEntity())); // Spigot + // CraftBukkit start - use parent method instead to correctly fire VehicleExitEvent + Entity originalVehicle = this.vehicle; + // First statement moved down, second statement handled in parent method. + /* + if (!this.world.isStatic) { + this.m(this.vehicle); + } + + if (this.vehicle != null) { + this.vehicle.passenger = null; + } + + this.vehicle = null; + */ + super.setPassengerOf(entity); + if (!this.world.isStatic && this.vehicle == null) { + this.m(originalVehicle); + } + // CraftBukkit end + } else { + super.setPassengerOf(entity); // CraftBukkit - call new parent + } + } + + public void ab() { + if (!this.world.isStatic && this.isSneaking()) { + this.mount((Entity) null); + this.setSneaking(false); + } else { + double d0 = this.locX; + double d1 = this.locY; + double d2 = this.locZ; + float f = this.yaw; + float f1 = this.pitch; + + super.ab(); + this.br = this.bs; + this.bs = 0.0F; + this.l(this.locX - d0, this.locY - d1, this.locZ - d2); + if (this.vehicle instanceof EntityPig) { + this.pitch = f1; + this.yaw = f; + this.aM = ((EntityPig) this.vehicle).aM; + } + } + } + + protected void bq() { + super.bq(); + this.bb(); + } + + public void e() { + if (this.bq > 0) { + --this.bq; + } + + if (this.world.difficulty == EnumDifficulty.PEACEFUL && this.getHealth() < this.getMaxHealth() && this.world.getGameRules().getBoolean("naturalRegeneration") && this.ticksLived % 20 * 12 == 0) { + // CraftBukkit - added regain reason of "REGEN" for filtering purposes. + this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); + } + + this.inventory.k(); + this.br = this.bs; + super.e(); + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.d); + + if (!this.world.isStatic) { + attributeinstance.setValue((double) this.abilities.b()); + } + + this.aQ = this.bJ; + if (this.isSprinting()) { + this.aQ = (float) ((double) this.aQ + (double) this.bJ * 0.3D); + } + + this.i((float) attributeinstance.getValue()); + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + // CraftBukkit - Math -> TrigMath + float f1 = (float) org.bukkit.craftbukkit.TrigMath.atan(-this.motY * 0.20000000298023224D) * 15.0F; + + if (f > 0.1F) { + f = 0.1F; + } + + if (!this.onGround || this.getHealth() <= 0.0F) { + f = 0.0F; + } + + if (this.onGround || this.getHealth() <= 0.0F) { + f1 = 0.0F; + } + + this.bs += (f - this.bs) * 0.4F; + this.aJ += (f1 - this.aJ) * 0.8F; + if (this.getHealth() > 0.0F) { + AxisAlignedBB axisalignedbb = null; + + if (this.vehicle != null && !this.vehicle.dead) { + axisalignedbb = this.boundingBox.a(this.vehicle.boundingBox).grow(1.0D, 0.0D, 1.0D); + } else { + axisalignedbb = this.boundingBox.grow(1.0D, 0.5D, 1.0D); + } + + List list = this.world.getEntities(this, axisalignedbb); + + if (list != null && this.S()) { // Spigot: Add this.S() condition (second !this.isDead near bottom of EntityLiving) + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (!entity.dead) { + this.d(entity); + } + } + } + } + } + + private void d(Entity entity) { + entity.b_(this); + } + + public int getScore() { + return this.datawatcher.getInt(18); + } + + public void setScore(int i) { + this.datawatcher.watch(18, Integer.valueOf(i)); + } + + public void addScore(int i) { + int j = this.getScore(); + + this.datawatcher.watch(18, Integer.valueOf(j + i)); + } + + public void die(DamageSource damagesource) { + super.die(damagesource); + this.a(0.2F, 0.2F); + this.setPosition(this.locX, this.locY, this.locZ); + this.motY = 0.10000000149011612D; + if (this.getName().equals("Notch")) { + this.a(new ItemStack(Items.APPLE, 1), true, false); + } + + if (!this.world.getGameRules().getBoolean("keepInventory")) { + this.inventory.m(); + } + + if (damagesource != null) { + this.motX = (double) (-MathHelper.cos((this.az + this.yaw) * 3.1415927F / 180.0F) * 0.1F); + this.motZ = (double) (-MathHelper.sin((this.az + this.yaw) * 3.1415927F / 180.0F) * 0.1F); + } else { + this.motX = this.motZ = 0.0D; + } + + this.height = 0.1F; + this.a(StatisticList.v, 1); + } + + protected String aT() { + return "game.player.hurt"; + } + + protected String aU() { + return "game.player.die"; + } + + public void b(Entity entity, int i) { + this.addScore(i); + // CraftBukkit - Get our scores instead + Collection collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.e, this.getName(), new java.util.ArrayList()); + + if (entity instanceof EntityHuman) { + this.a(StatisticList.y, 1); + // CraftBukkit - Get our scores instead + this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.d, this.getName(), collection); + } else { + this.a(StatisticList.w, 1); + } + + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); // CraftBukkit - Use our scores instead + + scoreboardscore.incrementScore(); + } + } + + public EntityItem a(boolean flag) { + // Called only when dropped by Q or CTRL-Q + return this.a(this.inventory.splitStack(this.inventory.itemInHandIndex, flag && this.inventory.getItemInHand() != null ? this.inventory.getItemInHand().count : 1), false, true); + } + + public EntityItem drop(ItemStack itemstack, boolean flag) { + return this.a(itemstack, false, false); + } + + public EntityItem a(ItemStack itemstack, boolean flag, boolean flag1) { + if (itemstack == null) { + return null; + } else if (itemstack.count <= 0) { // EMC + return null; + } else { + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY - 0.30000001192092896D + (double) this.getHeadHeight(), this.locZ, itemstack); + + entityitem.pickupDelay = 40; + if (flag1) { + entityitem.b(this.getName()); + } + + float f = 0.1F; + float f1; + + if (flag) { + f1 = this.random.nextFloat() * 0.5F; + float f2 = this.random.nextFloat() * 3.1415927F * 2.0F; + + entityitem.motX = (double) (-MathHelper.sin(f2) * f1); + entityitem.motZ = (double) (MathHelper.cos(f2) * f1); + entityitem.motY = 0.20000000298023224D; + } else { + f = 0.3F; + entityitem.motX = (double) (-MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F) * f); + entityitem.motZ = (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F) * f); + entityitem.motY = (double) (-MathHelper.sin(this.pitch / 180.0F * 3.1415927F) * f + 0.1F); + f = 0.02F; + f1 = this.random.nextFloat() * 3.1415927F * 2.0F; + f *= this.random.nextFloat(); + entityitem.motX += Math.cos((double) f1) * (double) f; + entityitem.motY += (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F); + entityitem.motZ += Math.sin((double) f1) * (double) f; + } + + // CraftBukkit start - fire PlayerDropItemEvent + Player player = (Player) this.getBukkitEntity(); + CraftItem drop = new CraftItem(this.world.getServer(), entityitem); + + PlayerDropItemEvent event = new PlayerDropItemEvent(player, drop); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + org.bukkit.inventory.ItemStack cur = player.getInventory().getItemInHand(); + if (flag1 && (cur == null || cur.getAmount() == 0)) { + // The complete stack was dropped + player.getInventory().setItemInHand(drop.getItemStack()); + } else if (flag1 && cur.isSimilar(drop.getItemStack()) && drop.getItemStack().getAmount() == 1) { + // Only one item is dropped + cur.setAmount(cur.getAmount() + 1); + player.getInventory().setItemInHand(cur); + } else { + // Fallback + player.getInventory().addItem(drop.getItemStack()); + } + return null; + } + // CraftBukkit end + + this.a(entityitem); + this.a(StatisticList.s, 1); + return entityitem; + } + } + + protected void a(EntityItem entityitem) { + this.world.addEntity(entityitem); + } + + public float a(Block block, boolean flag) { + float f = this.inventory.a(block); + + if (f > 1.0F) { + int i = EnchantmentManager.getDigSpeedEnchantmentLevel(this); + ItemStack itemstack = this.inventory.getItemInHand(); + + if (i > 0 && itemstack != null) { + float f1 = (float) (i * i + 1); + + if (!itemstack.b(block) && f <= 1.0F) { + f += f1 * 0.08F; + } else { + f += f1; + } + } + } + + if (this.hasEffect(MobEffectList.FASTER_DIG)) { + f *= 1.0F + (float) (this.getEffect(MobEffectList.FASTER_DIG).getAmplifier() + 1) * 0.2F; + } + + if (this.hasEffect(MobEffectList.SLOWER_DIG)) { + f *= 1.0F - (float) (this.getEffect(MobEffectList.SLOWER_DIG).getAmplifier() + 1) * 0.2F; + } + + if (this.a(Material.WATER) && !EnchantmentManager.hasWaterWorkerEnchantment(this)) { + f /= 5.0F; + } + + if (!this.onGround) { + f /= 5.0F; + } + + return f; + } + + public boolean a(Block block) { + return this.inventory.b(block); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.uniqueID = a(this.i); + NBTTagList nbttaglist = nbttagcompound.getList("Inventory", 10); + + this.inventory.b(nbttaglist); + this.inventory.itemInHandIndex = nbttagcompound.getInt("SelectedItemSlot"); + this.sleeping = nbttagcompound.getBoolean("Sleeping"); + this.sleepTicks = nbttagcompound.getShort("SleepTimer"); + this.exp = nbttagcompound.getFloat("XpP"); + this.expLevel = nbttagcompound.getInt("XpLevel"); + this.expTotal = nbttagcompound.getInt("XpTotal"); + this.setScore(nbttagcompound.getInt("Score")); + if (this.sleeping) { + this.bB = new ChunkCoordinates(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)); + this.a(true, true, false); + } + + // CraftBukkit start + this.spawnWorld = nbttagcompound.getString("SpawnWorld"); + if ("".equals(spawnWorld)) { + this.spawnWorld = this.world.getServer().getWorlds().get(0).getName(); + } + // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("SpawnX", 99) && nbttagcompound.hasKeyOfType("SpawnY", 99) && nbttagcompound.hasKeyOfType("SpawnZ", 99)) { + this.c = new ChunkCoordinates(nbttagcompound.getInt("SpawnX"), nbttagcompound.getInt("SpawnY"), nbttagcompound.getInt("SpawnZ")); + this.d = nbttagcompound.getBoolean("SpawnForced"); + } + + this.foodData.a(nbttagcompound); + this.abilities.b(nbttagcompound); + if (nbttagcompound.hasKeyOfType("EnderItems", 9)) { + NBTTagList nbttaglist1 = nbttagcompound.getList("EnderItems", 10); + + this.enderChest.a(nbttaglist1); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.set("Inventory", this.inventory.a(new NBTTagList())); + nbttagcompound.setInt("SelectedItemSlot", this.inventory.itemInHandIndex); + nbttagcompound.setBoolean("Sleeping", this.sleeping); + nbttagcompound.setShort("SleepTimer", (short) this.sleepTicks); + nbttagcompound.setFloat("XpP", this.exp); + nbttagcompound.setInt("XpLevel", this.expLevel); + nbttagcompound.setInt("XpTotal", this.expTotal); + nbttagcompound.setInt("Score", this.getScore()); + if (this.c != null) { + nbttagcompound.setInt("SpawnX", this.c.x); + nbttagcompound.setInt("SpawnY", this.c.y); + nbttagcompound.setInt("SpawnZ", this.c.z); + nbttagcompound.setBoolean("SpawnForced", this.d); + nbttagcompound.setString("SpawnWorld", spawnWorld); // CraftBukkit - fixes bed spawns for multiworld worlds + } + + this.foodData.b(nbttagcompound); + this.abilities.a(nbttagcompound); + nbttagcompound.set("EnderItems", this.enderChest.h()); + } + + public void openContainer(IInventory iinventory) { + } + + public void openHopper(TileEntityHopper tileentityhopper) { + } + + public void openMinecartHopper(EntityMinecartHopper entityminecarthopper) { + } + + public void openHorseInventory(EntityHorse entityhorse, IInventory iinventory) { + } + + public void startEnchanting(int i, int j, int k, String s) { + } + + public void openAnvil(int i, int j, int k) { + } + + public void startCrafting(int i, int j, int k) { + } + + public float getHeadHeight() { + return 0.12F; + } + + protected void e_() { + this.height = 1.62F; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else if (this.abilities.isInvulnerable && !damagesource.ignoresInvulnerability()) { + return false; + } else { + this.aU = 0; + if (this.getHealth() <= 0.0F) { + return false; + } else { + if (this.isSleeping() && !this.world.isStatic) { + this.a(true, true, false); + } + + if (damagesource.r()) { + if (this.world.difficulty == EnumDifficulty.PEACEFUL) { + return false; // CraftBukkit - f = 0.0f -> return false + } + + if (this.world.difficulty == EnumDifficulty.EASY) { + f = f / 2.0F + 1.0F; + } + + if (this.world.difficulty == EnumDifficulty.HARD) { + f = f * 3.0F / 2.0F; + } + } + + if (false && f == 0.0F) { // CraftBukkit - Don't filter out 0 damage + return false; + } else { + Entity entity = damagesource.getEntity(); + + if (entity instanceof EntityArrow && ((EntityArrow) entity).shooter != null) { + entity = ((EntityArrow) entity).shooter; + } + + this.a(StatisticList.u, Math.round(f * 10.0F)); + return super.damageEntity(damagesource, f); + } + } + } + } + + public boolean a(EntityHuman entityhuman) { + // CraftBukkit start - Change to check OTHER player's scoreboard team according to API + // To summarize this method's logic, it's "Can parameter hurt this" + org.bukkit.scoreboard.Team team; + if (entityhuman instanceof EntityPlayer) { + EntityPlayer thatPlayer = (EntityPlayer) entityhuman; + team = thatPlayer.getBukkitEntity().getScoreboard().getPlayerTeam(thatPlayer.getBukkitEntity()); + if (team == null || team.allowFriendlyFire()) { + return true; + } + } else { + // This should never be called, but is implemented anyway + org.bukkit.OfflinePlayer thisPlayer = entityhuman.world.getServer().getOfflinePlayer(entityhuman.getName()); + team = entityhuman.world.getServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(thisPlayer); + if (team == null || team.allowFriendlyFire()) { + return true; + } + } + + if (this instanceof EntityPlayer) { + return !team.hasPlayer(((EntityPlayer) this).getBukkitEntity()); + } + return !team.hasPlayer(this.world.getServer().getOfflinePlayer(this.getName())); + // CraftBukkit end + } + + protected void damageArmor(float f) { + this.inventory.a(f); + } + + public int aV() { + return this.inventory.l(); + } + + public float bE() { + int i = 0; + ItemStack[] aitemstack = this.inventory.armor; + int j = aitemstack.length; + + for (int k = 0; k < j; ++k) { + ItemStack itemstack = aitemstack[k]; + + if (itemstack != null) { + ++i; + } + } + + return (float) i / (float) this.inventory.armor.length; + } + + // CraftBukkit start + protected boolean d(DamageSource damagesource, float f) { // void -> boolean + if (true) { + return super.d(damagesource, f); + } + // CraftBukkit end + if (!this.isInvulnerable()) { + if (!damagesource.ignoresArmor() && this.isBlocking() && f > 0.0F) { + f = (1.0F + f) * this.world.paperSpigotConfig.playerBlockingDamageMultiplier; // PaperSpigot - Configurable damage multiplier for blocking + } + + f = this.applyArmorModifier(damagesource, f); + f = this.applyMagicModifier(damagesource, f); + float f1 = f; + + f = Math.max(f - this.getAbsorptionHearts(), 0.0F); + this.setAbsorptionHearts(this.getAbsorptionHearts() - (f1 - f)); + if (f != 0.0F) { + this.applyExhaustion(damagesource.getExhaustionCost()); + float f2 = this.getHealth(); + + this.setHealth(this.getHealth() - f); + this.aW().a(damagesource, f2, f); + } + } + return false; // CraftBukkit + } + + public void openFurnace(TileEntityFurnace tileentityfurnace) { + } + + public void openDispenser(TileEntityDispenser tileentitydispenser) { + } + + public void a(TileEntity tileentity) { + } + + public void a(CommandBlockListenerAbstract commandblocklistenerabstract) { + } + + public void openBrewingStand(TileEntityBrewingStand tileentitybrewingstand) { + } + + public void openBeacon(TileEntityBeacon tileentitybeacon) { + } + + public void openTrade(IMerchant imerchant, String s) { + } + + public void b(ItemStack itemstack) { + } + + public boolean q(Entity entity) { + ItemStack itemstack = this.bF(); + ItemStack itemstack1 = itemstack != null ? itemstack.cloneItemStack() : null; + + if (!entity.c(this)) { + if (itemstack != null && entity instanceof EntityLiving) { + if (this.abilities.canInstantlyBuild) { + itemstack = itemstack1; + } + + if (itemstack.a(this, (EntityLiving) entity)) { + // CraftBukkit - bypass infinite items; <= 0 -> == 0 + if (itemstack.count == 0 && !this.abilities.canInstantlyBuild) { + this.bG(); + } + + return true; + } + } + + return false; + } else { + if (itemstack != null && itemstack == this.bF()) { + if (itemstack.count <= 0 && !this.abilities.canInstantlyBuild) { + this.bG(); + } else if (itemstack.count < itemstack1.count && this.abilities.canInstantlyBuild) { + itemstack.count = itemstack1.count; + } + } + + return true; + } + } + + public ItemStack bF() { + return this.inventory.getItemInHand(); + } + + public void bG() { + this.inventory.setItem(this.inventory.itemInHandIndex, (ItemStack) null); + } + + public double ad() { + return (double) (this.height - 0.5F); + } + + public void attack(final Entity entity) { + if (entity.av() && !entity.j(this)) { + float f = (float)this.getAttributeInstance(GenericAttributes.e).getValue(); + int i = 0; + float f2 = 0.0f; + if (entity instanceof EntityLiving) { + f2 = EnchantmentManager.a(this, (EntityLiving)entity); + i += EnchantmentManager.getKnockbackEnchantmentLevel(this, (EntityLiving)entity); + } + if (this.isSprinting()) { + ++i; + } + if (f > 0.0f || f2 > 0.0f) { + final boolean flag = !this.world.paperSpigotConfig.disablePlayerCrits && this.fallDistance > 0.0f && !this.onGround && !this.h_() && !this.M() && !this.hasEffect(MobEffectList.BLINDNESS) && this.vehicle == null && entity instanceof EntityLiving; + if (flag && f > 0.0f) { + f *= 1.5f; + } + f += f2; + boolean flag2 = false; + final int j = EnchantmentManager.getFireAspectEnchantmentLevel(this); + if (entity instanceof EntityLiving && j > 0 && !entity.isBurning()) { + final EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 1); + Bukkit.getPluginManager().callEvent(combustEvent); + if (!combustEvent.isCancelled()) { + flag2 = true; + entity.setOnFire(combustEvent.getDuration()); + } + } + + final double victimMotX = entity.motX; + final double victimMotY = entity.motY; + final double victimMotZ = entity.motZ; + final boolean flag3 = entity.damageEntity(DamageSource.playerAttack(this), f); + + if (flag3) { + if (i > 0) { + if (SpigotConfig.hcf) { + entity.g((-MathHelper.sin(this.yaw * 3.1415927F / 180.0F) * i * 0.5F), 0.1D, (MathHelper.cos(this.yaw * 3.1415927F / 180.0F) * i * 0.5F)); + } else { + Knockback knockback = getKnockback(); + entity.g((-MathHelper.sin(this.yaw * 3.1415927F / 180.0F) * (float) i + * knockback.getExtraHorizontal()), knockback.getExtraVertical(), + (MathHelper.cos(this.yaw * 3.1415927F / 180.0F) * + (float) i * knockback.getExtraHorizontal())); + } + + this.motX *= 0.6D; + this.motZ *= 0.6D; + + if (SpigotConfig.wtap) { + this.setSprinting(false); + } + } + + // Kohi start + // If the attack caused knockback, send the new velocity to the victim's client immediately, + // and undo the change. Otherwise, if movement packets from the victim are processed before + // the end of the tick, then friction may reduce the velocity considerably before it's sent + // to the client, particularly if the victim was standing on the ground when those packets + // were generated. And because this glitch is also likely to make server-side velocity very + // inconsistent, we simply reverse the knockback after sending it so that KB, like most other + // things, doesn't affect server velocity at all. + if (entity instanceof EntityPlayer && entity.velocityChanged) { + EntityPlayer attackedPlayer = (EntityPlayer) entity; + PlayerVelocityEvent event = new PlayerVelocityEvent(attackedPlayer.getBukkitEntity(), attackedPlayer.getBukkitEntity().getVelocity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + attackedPlayer.getBukkitEntity().setVelocityDirect(event.getVelocity()); + attackedPlayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(attackedPlayer)); + } + + attackedPlayer.velocityChanged = false; + attackedPlayer.motX = victimMotX; + attackedPlayer.motY = victimMotY; + attackedPlayer.motZ = victimMotZ; + } + // Kohi end + + if (flag) { + this.b(entity); + } + if (f2 > 0.0f) { + this.c(entity); + } + if (f >= 18.0f) { + this.a(AchievementList.F); + } + this.l(entity); + if (entity instanceof EntityLiving) { + EnchantmentManager.a((EntityLiving)entity, (Entity)this); + } + EnchantmentManager.b(this, entity); + final ItemStack itemstack = this.bF(); + Object object = entity; + if (entity instanceof EntityComplexPart) { + final IComplex icomplex = ((EntityComplexPart)entity).owner; + if (icomplex != null && icomplex instanceof EntityLiving) { + object = icomplex; + } + } + if (itemstack != null && object instanceof EntityLiving) { + itemstack.a((EntityLiving)object, this); + if (itemstack.count == 0) { + this.bG(); + } + } + if (entity instanceof EntityLiving) { + this.a(StatisticList.t, Math.round(f * 10.0f)); + if (j > 0) { + final EntityCombustByEntityEvent combustEvent2 = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), j * 4); + Bukkit.getPluginManager().callEvent(combustEvent2); + if (!combustEvent2.isCancelled()) { + entity.setOnFire(combustEvent2.getDuration()); + } + } + } + this.applyExhaustion(this.world.spigotConfig.combatExhaustion); + } + else if (flag2) { + entity.extinguish(); + } + } + } + } + + public void b(Entity entity) { + } + + public void c(Entity entity) { + } + + public void die() { + super.die(); + this.defaultContainer.b(this); + if (this.activeContainer != null) { + this.activeContainer.b(this); + } + } + + public boolean inBlock() { + return !this.sleeping && super.inBlock(); + } + + public GameProfile getProfile() { + return this.i; + } + + public EnumBedResult a(int i, int j, int k) { + if (!this.world.isStatic) { + if (this.isSleeping() || !this.isAlive()) { + return EnumBedResult.OTHER_PROBLEM; + } + + if (!this.world.worldProvider.d()) { + return EnumBedResult.NOT_POSSIBLE_HERE; + } + + if (this.world.w()) { + return EnumBedResult.NOT_POSSIBLE_NOW; + } + + if (Math.abs(this.locX - (double) i) > 3.0D || Math.abs(this.locY - (double) j) > 2.0D || Math.abs(this.locZ - (double) k) > 3.0D) { + return EnumBedResult.TOO_FAR_AWAY; + } + + double d0 = 8.0D; + double d1 = 5.0D; + List list = this.world.a(EntityMonster.class, AxisAlignedBB.a((double) i - d0, (double) j - d1, (double) k - d0, (double) i + d0, (double) j + d1, (double) k + d0)); + + if (!list.isEmpty()) { + return EnumBedResult.NOT_SAFE; + } + } + + if (this.am()) { + this.mount((Entity) null); + } + + // CraftBukkit start - fire PlayerBedEnterEvent + if (this.getBukkitEntity() instanceof Player) { + Player player = (Player) this.getBukkitEntity(); + org.bukkit.block.Block bed = this.world.getWorld().getBlockAt(i, j, k); + + PlayerBedEnterEvent event = new PlayerBedEnterEvent(player, bed); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return EnumBedResult.OTHER_PROBLEM; + } + } + // CraftBukkit end + + this.a(0.2F, 0.2F); + this.height = 0.2F; + if (this.world.isLoaded(i, j, k)) { + int l = this.world.getData(i, j, k); + int i1 = BlockBed.l(l); + float f = 0.5F; + float f1 = 0.5F; + + switch (i1) { + case 0: + f1 = 0.9F; + break; + + case 1: + f = 0.1F; + break; + + case 2: + f1 = 0.1F; + break; + + case 3: + f = 0.9F; + } + + this.w(i1); + this.setPosition((double) ((float) i + f), (double) ((float) j + 0.9375F), (double) ((float) k + f1)); + } else { + this.setPosition((double) ((float) i + 0.5F), (double) ((float) j + 0.9375F), (double) ((float) k + 0.5F)); + } + + this.sleeping = true; + this.sleepTicks = 0; + this.bB = new ChunkCoordinates(i, j, k); + this.motX = this.motZ = this.motY = 0.0D; + if (!this.world.isStatic) { + this.world.everyoneSleeping(); + } + + return EnumBedResult.OK; + } + + private void w(int i) { + this.bC = 0.0F; + this.bD = 0.0F; + switch (i) { + case 0: + this.bD = -1.8F; + break; + + case 1: + this.bC = 1.8F; + break; + + case 2: + this.bD = 1.8F; + break; + + case 3: + this.bC = -1.8F; + } + } + + public void a(boolean flag, boolean flag1, boolean flag2) { + this.a(0.6F, 1.8F); + this.e_(); + ChunkCoordinates chunkcoordinates = this.bB; + ChunkCoordinates chunkcoordinates1 = this.bB; + + if (chunkcoordinates != null && this.world.getType(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z) == Blocks.BED) { + BlockBed.a(this.world, chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z, false); + chunkcoordinates1 = BlockBed.a(this.world, chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z, 0); + if (chunkcoordinates1 == null) { + chunkcoordinates1 = new ChunkCoordinates(chunkcoordinates.x, chunkcoordinates.y + 1, chunkcoordinates.z); + } + + this.setPosition((double) ((float) chunkcoordinates1.x + 0.5F), (double) ((float) chunkcoordinates1.y + this.height + 0.1F), (double) ((float) chunkcoordinates1.z + 0.5F)); + } + + this.sleeping = false; + if (!this.world.isStatic && flag1) { + this.world.everyoneSleeping(); + } + + // CraftBukkit start - fire PlayerBedLeaveEvent + if (this.getBukkitEntity() instanceof Player) { + Player player = (Player) this.getBukkitEntity(); + + org.bukkit.block.Block bed; + if (chunkcoordinates != null) { + bed = this.world.getWorld().getBlockAt(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z); + } else { + bed = this.world.getWorld().getBlockAt(player.getLocation()); + } + + PlayerBedLeaveEvent event = new PlayerBedLeaveEvent(player, bed); + this.world.getServer().getPluginManager().callEvent(event); + } + // CraftBukkit end + + if (flag) { + this.sleepTicks = 0; + } else { + this.sleepTicks = 100; + } + + if (flag2) { + this.setRespawnPosition(this.bB, false); + } + } + + private boolean j() { + return this.world.getType(this.bB.x, this.bB.y, this.bB.z) == Blocks.BED; + } + + public static ChunkCoordinates getBed(World world, ChunkCoordinates chunkcoordinates, boolean flag) { + IChunkProvider ichunkprovider = world.L(); + + ichunkprovider.getChunkAt(chunkcoordinates.x - 3 >> 4, chunkcoordinates.z - 3 >> 4); + ichunkprovider.getChunkAt(chunkcoordinates.x + 3 >> 4, chunkcoordinates.z - 3 >> 4); + ichunkprovider.getChunkAt(chunkcoordinates.x - 3 >> 4, chunkcoordinates.z + 3 >> 4); + ichunkprovider.getChunkAt(chunkcoordinates.x + 3 >> 4, chunkcoordinates.z + 3 >> 4); + if (world.getType(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z) == Blocks.BED) { + ChunkCoordinates chunkcoordinates1 = BlockBed.a(world, chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z, 0); + + return chunkcoordinates1; + } else { + Material material = world.getType(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z).getMaterial(); + Material material1 = world.getType(chunkcoordinates.x, chunkcoordinates.y + 1, chunkcoordinates.z).getMaterial(); + boolean flag1 = !material.isBuildable() && !material.isLiquid(); + boolean flag2 = !material1.isBuildable() && !material1.isLiquid(); + + return flag && flag1 && flag2 ? chunkcoordinates : null; + } + } + + public boolean isSleeping() { + return this.sleeping; + } + + public boolean isDeeplySleeping() { + return this.sleeping && this.sleepTicks >= 100; + } + + // Spigot start - protocol patch, handle metadata usage change (show cape -> collisions) + protected void b(int i, boolean flag, int version) { + ProtocolData.DualByte db = this.datawatcher.getDualByte(16); + byte b0 = version >= 16 ? db.value2 : db.value; + if (flag) { + b0 = (byte) (b0 | 1 << i); + } else { + b0 = (byte) (b0 & ~(1 << i)); + } + if (version >= 16) { + db.value2 = b0; + } else { + db.value = b0; + } + this.datawatcher.watch(16, db); + } + // Spigot end + + public void b(IChatBaseComponent ichatbasecomponent) { + } + + public ChunkCoordinates getBed() { + return this.c; + } + + public boolean isRespawnForced() { + return this.d; + } + + public void setRespawnPosition(ChunkCoordinates chunkcoordinates, boolean flag) { + if (chunkcoordinates != null) { + this.c = new ChunkCoordinates(chunkcoordinates); + this.d = flag; + this.spawnWorld = this.world.worldData.getName(); // CraftBukkit + } else { + this.c = null; + this.d = false; + this.spawnWorld = ""; // CraftBukkit + } + } + + public void a(Statistic statistic) { + this.a(statistic, 1); + } + + public void a(Statistic statistic, int i) { + } + + public void bj() { + super.bj(); + this.a(StatisticList.r, 1); + + if (this.isSprinting()) { + this.applyExhaustion(world.spigotConfig.sprintExhaustion); // Spigot - Change to use configurable value + } else { + this.applyExhaustion(world.spigotConfig.walkExhaustion); // Spigot - Change to use configurable value + } + } + + public void e(float f, float f1) { + double d0 = this.locX; + double d1 = this.locY; + double d2 = this.locZ; + World world = this.world; + + if (this.abilities.isFlying && this.vehicle == null) { + double d3 = this.motY; + float f2 = this.aQ; + + this.aQ = this.abilities.a(); + super.e(f, f1); + this.motY = d3 * 0.6D; + this.aQ = f2; + } else { + super.e(f, f1); + } + + // Kohi - don't check if world changed + if (this.world == world) { + this.checkMovement(this.locX - d0, this.locY - d1, this.locZ - d2); + } + } + + public float bl() { + return (float) this.getAttributeInstance(GenericAttributes.d).getValue(); + } + + public void checkMovement(double d0, double d1, double d2) { + if (this.vehicle == null) { + int i; + + if (this.a(Material.WATER)) { // in water + i = Math.round(MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); + if (i > 0) { + this.a(StatisticList.m, i); + this.applyExhaustion(world.paperSpigotConfig.playerSwimmingExhaustion * (float) i * 0.01F); // PaperSpigot - Configurable swimming exhaustion + } + } else if (this.M()) { // in water + i = Math.round(MathHelper.sqrt(d0 * d0 + d2 * d2) * 100.0F); + if (i > 0) { + this.a(StatisticList.i, i); + this.applyExhaustion(world.paperSpigotConfig.playerSwimmingExhaustion * (float) i * 0.01F); // PaperSpigot - Configurable swimming (diving) exhaustion + } + } else if (this.h_()) { // on ladder or vine + if (d1 > 0.0D) { + this.a(StatisticList.k, (int) Math.round(d1 * 100.0D)); + } + } else if (this.onGround) { // on ground + i = Math.round(MathHelper.sqrt(d0 * d0 + d2 * d2) * 100.0F); + if (i > 0) { + this.a(StatisticList.h, i); + if (this.isSprinting()) { + this.applyExhaustion(0.099999994F * (float) i * 0.01F); + } else { + this.applyExhaustion(0.01F * (float) i * 0.01F); + } + } + } else { + i = Math.round(MathHelper.sqrt(d0 * d0 + d2 * d2) * 100.0F); + if (i > 25) { + this.a(StatisticList.l, i); + } + } + } + } + + private void l(double d0, double d1, double d2) { + if (this.vehicle != null) { + int i = Math.round(MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); + + if (i > 0) { + if (this.vehicle instanceof EntityMinecartAbstract) { + this.a(StatisticList.n, i); + if (this.e == null) { + this.e = new ChunkCoordinates(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)); + } else if ((double) this.e.e(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)) >= 1000000.0D) { + this.a((Statistic) AchievementList.q, 1); + } + } else if (this.vehicle instanceof EntityBoat) { + this.a(StatisticList.o, i); + } else if (this.vehicle instanceof EntityPig) { + this.a(StatisticList.p, i); + } else if (this.vehicle instanceof EntityHorse) { + this.a(StatisticList.q, i); + } + } + } + } + + protected void b(float f) { + if (!this.abilities.canFly) { + if (f >= 2.0F) { + this.a(StatisticList.j, (int) Math.round((double) f * 100.0D)); + } + + super.b(f); + } + } + + protected String o(int i) { + return i > 4 ? "game.player.hurt.fall.big" : "game.player.hurt.fall.small"; + } + + public void a(EntityLiving entityliving) { + if (entityliving instanceof IMonster) { + this.a((Statistic) AchievementList.s); + } + + int i = EntityTypes.a(entityliving); + MonsterEggInfo monsteregginfo = (MonsterEggInfo) EntityTypes.eggInfo.get(Integer.valueOf(i)); + + if (monsteregginfo != null) { + this.a(monsteregginfo.killEntityStatistic, 1); + } + } + + public void as() { + if (!this.abilities.isFlying) { + super.as(); + } + } + + public ItemStack r(int i) { + return this.inventory.d(i); + } + + public void giveExp(int i) { + this.addScore(i); + int j = Integer.MAX_VALUE - this.expTotal; + + if (i > j) { + i = j; + } + + this.exp += (float) i / (float) this.getExpToLevel(); + + for (this.expTotal += i; this.exp >= 1.0F; this.exp /= (float) this.getExpToLevel()) { + this.exp = (this.exp - 1.0F) * (float) this.getExpToLevel(); + this.levelDown(1); + } + } + + public void levelDown(int i) { + this.expLevel += i; + if (this.expLevel < 0) { + this.expLevel = 0; + this.exp = 0.0F; + this.expTotal = 0; + } + + if (i > 0 && this.expLevel % 5 == 0 && (float) this.h < (float) this.ticksLived - 100.0F) { + float f = this.expLevel > 30 ? 1.0F : (float) this.expLevel / 30.0F; + + this.world.makeSound(this, "random.levelup", f * 0.75F, 1.0F); + this.h = this.ticksLived; + } + } + + public int getExpToLevel() { + return this.expLevel >= 30 ? 62 + (this.expLevel - 30) * 7 : (this.expLevel >= 15 ? 17 + (this.expLevel - 15) * 3 : 17); + } + + public void applyExhaustion(float f) { + if (!this.abilities.isInvulnerable) { + if (!this.world.isStatic) { + this.foodData.a(f); + } + } + } + + public FoodMetaData getFoodData() { + return this.foodData; + } + + public boolean g(boolean flag) { + return (flag || this.foodData.c()) && !this.abilities.isInvulnerable; + } + + public boolean bR() { + return this.getHealth() > 0.0F && this.getHealth() < this.getMaxHealth(); + } + + public void a(ItemStack itemstack, int i) { + if (itemstack != this.f) { + this.f = itemstack; + this.g = i; + if (!this.world.isStatic) { + this.e(true); + } + } + } + + public boolean d(int i, int j, int k) { + if (this.abilities.mayBuild) { + return true; + } else { + Block block = this.world.getType(i, j, k); + + if (block.getMaterial() != Material.AIR) { + if (block.getMaterial().q()) { + return true; + } + + if (this.bF() != null) { + ItemStack itemstack = this.bF(); + + if (itemstack.b(block) || itemstack.a(block) > 1.0F) { + return true; + } + } + } + + return false; + } + } + + public boolean a(int i, int j, int k, int l, ItemStack itemstack) { + return this.abilities.mayBuild ? true : (itemstack != null ? itemstack.z() : false); + } + + protected int getExpValue(EntityHuman entityhuman) { + if (this.world.getGameRules().getBoolean("keepInventory")) { + return 0; + } else { + int i = this.expLevel * 7; + + return i > 100 ? 100 : i; + } + } + + protected boolean alwaysGivesExp() { + return true; + } + + public void copyTo(EntityHuman entityhuman, boolean flag) { + if (flag) { + this.inventory.b(entityhuman.inventory); + this.setHealth(entityhuman.getHealth()); + this.foodData = entityhuman.foodData; + this.expLevel = entityhuman.expLevel; + this.expTotal = entityhuman.expTotal; + this.exp = entityhuman.exp; + this.setScore(entityhuman.getScore()); + this.aq = entityhuman.aq; + } else if (this.world.getGameRules().getBoolean("keepInventory")) { + this.inventory.b(entityhuman.inventory); + this.expLevel = entityhuman.expLevel; + this.expTotal = entityhuman.expTotal; + this.exp = entityhuman.exp; + this.setScore(entityhuman.getScore()); + } + + this.enderChest = entityhuman.enderChest; + } + + protected boolean g_() { + return !this.abilities.isFlying; + } + + public void updateAbilities() { + } + + public void a(EnumGamemode enumgamemode) { + } + + public String getName() { + return this.i.getName(); + } + + public World getWorld() { + return this.world; + } + + public InventoryEnderChest getEnderChest() { + return this.enderChest; + } + + public ItemStack getEquipment(int i) { + return i == 0 ? this.inventory.getItemInHand() : this.inventory.armor[i - 1]; + } + + public ItemStack be() { + return this.inventory.getItemInHand(); + } + + public void setEquipment(int i, ItemStack itemstack) { + ItemStack previous = this.inventory.armor[i]; + if (!Objects.equals(previous, itemstack)) { + if (previous != null && EquipmentSetEvent.getHandlerList().getRegisteredListeners().length != 0) { + previous = previous.cloneItemStack(); + } + + this.inventory.armor[i] = itemstack; + Bukkit.getPluginManager().callEvent(new EquipmentSetEvent(getBukkitEntity(), i, CraftItemStack.asBukkitCopy(previous), CraftItemStack.asBukkitCopy(itemstack))); + } + } + + public ItemStack[] getEquipment() { + return this.inventory.armor; + } + + public boolean aC() { + return !this.abilities.isFlying; + } + + public Scoreboard getScoreboard() { + return this.world.getScoreboard(); + } + + public ScoreboardTeamBase getScoreboardTeam() { + return this.getScoreboard().getPlayerTeam(this.getName()); + } + + public IChatBaseComponent getScoreboardDisplayName() { + // CraftBukkit - todo: fun + ChatComponentText chatcomponenttext = new ChatComponentText(ScoreboardTeam.getPlayerDisplayName(this.getScoreboardTeam(), this.getName())); + + chatcomponenttext.getChatModifier().setChatClickable(new ChatClickable(EnumClickAction.SUGGEST_COMMAND, "/msg " + this.getName() + " ")); + return chatcomponenttext; + } + + public void setAbsorptionHearts(float f) { + if (f < 0.0F) { + f = 0.0F; + } + + float previous = getAbsorptionHearts(); // MineHQ + + this.getDataWatcher().watch(17, Float.valueOf(f)); + + // MineHQ start + if (previous != f) { + Bukkit.getPluginManager().callEvent(new PlayerHealthChangeEvent(((CraftPlayer) getBukkitEntity()), getHealth(), getHealth())); + } + // MineHQ end + } + + public float getAbsorptionHearts() { + return this.getDataWatcher().getFloat(17); + } + + public static UUID a(GameProfile gameprofile) { + UUID uuid = gameprofile.getId(); + + if (uuid == null) { + uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + gameprofile.getName()).getBytes(Charsets.UTF_8)); + } + + return uuid; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityInsentient.java b/vspigot-server/src/main/java/net/minecraft/server/EntityInsentient.java new file mode 100644 index 0000000..33afe8b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityInsentient.java @@ -0,0 +1,951 @@ +package net.minecraft.server; + +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityUnleashEvent; +import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason; +// CraftBukkit end + +// Poweruser start +import org.bukkit.craftbukkit.SpigotTimings; +import net.valorhcf.pathsearch.AsyncNavigation; +// Poweruser end + +public abstract class EntityInsentient extends EntityLiving { + + public int a_; + protected int b; + private ControllerLook lookController; + private ControllerMove moveController; + private ControllerJump bm; + private EntityAIBodyControl bn; + private Navigation navigation; + protected final PathfinderGoalSelector goalSelector; + protected final PathfinderGoalSelector targetSelector; + private WeakReference goalTarget = new WeakReference(null); // Spigot Update - 20140921a + private EntitySenses bq; + private ItemStack[] equipment = new ItemStack[5]; + public float[] dropChances = new float[5]; // CraftBukkit - protected -> public + public boolean canPickUpLoot; // CraftBukkit - private -> public + public boolean persistent = !isTypeNotPersistent(); // CraftBukkit - private -> public + protected float f; + private Entity bu; + protected int g; + private boolean bv; + private Entity bw; + private NBTTagCompound bx; + + public EntityInsentient(World world) { + super(world); + this.goalSelector = new PathfinderGoalSelector(world != null && world.methodProfiler != null ? world.methodProfiler : null); + this.targetSelector = new PathfinderGoalSelector(world != null && world.methodProfiler != null ? world.methodProfiler : null); + this.lookController = new ControllerLook(this); + this.moveController = new ControllerMove(this); + this.bm = new ControllerJump(this); + this.bn = new EntityAIBodyControl(this); + this.navigation = new AsyncNavigation(this, world); // Poweruser + this.bq = new EntitySenses(this); + + for (int i = 0; i < this.dropChances.length; ++i) { + this.dropChances[i] = 0.085F; + } + } + + protected void aD() { + super.aD(); + this.getAttributeMap().b(GenericAttributes.b).setValue(16.0D); + } + + public ControllerLook getControllerLook() { + return this.lookController; + } + + public ControllerMove getControllerMove() { + return this.moveController; + } + + public ControllerJump getControllerJump() { + return this.bm; + } + + public Navigation getNavigation() { + return this.navigation; + } + + public EntitySenses getEntitySenses() { + return this.bq; + } + + public EntityLiving getGoalTarget() { + return this.goalTarget.get(); + } + + public void setGoalTarget(EntityLiving entityliving) { + this.goalTarget = new WeakReference(entityliving); + } + + public boolean a(Class oclass) { + return EntityCreeper.class != oclass && EntityGhast.class != oclass; + } + + public void p() {} + + protected void c() { + super.c(); + this.datawatcher.a(11, Byte.valueOf((byte) 0)); + this.datawatcher.a(10, ""); + // Spigot start - protocol patch + this.datawatcher.a(3, Byte.valueOf((byte) 0)); + this.datawatcher.a(2, ""); + // Spigot end + } + + public int q() { + return 80; + } + + public void r() { + String s = this.t(); + + if (s != null) { + this.makeSound(s, this.bf(), this.bg()); + } + } + + public void C() { + SpigotTimings.timerEntityInsentient_C.startTiming(); // Poweruser + super.C(); + this.world.methodProfiler.a("mobBaseTick"); + if (this.isAlive() && this.random.nextInt(1000) < this.a_++) { + this.a_ = -this.q(); + this.r(); + } + + this.world.methodProfiler.b(); + SpigotTimings.timerEntityInsentient_C.stopTiming(); // Poweruser + } + + protected int getExpValue(EntityHuman entityhuman) { + if (this.b > 0) { + int i = this.b; + ItemStack[] aitemstack = this.getEquipment(); + + for (int j = 0; j < aitemstack.length; ++j) { + if (aitemstack[j] != null && this.dropChances[j] <= 1.0F) { + i += 1 + this.random.nextInt(3); + } + } + + return i; + } else { + return this.b; + } + } + + public void s() { + for (int i = 0; i < 20; ++i) { + double d0 = this.random.nextGaussian() * 0.02D; + double d1 = this.random.nextGaussian() * 0.02D; + double d2 = this.random.nextGaussian() * 0.02D; + double d3 = 10.0D; + + this.world.addParticle("explode", this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width - d0 * d3, this.locY + (double) (this.random.nextFloat() * this.length) - d1 * d3, this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width - d2 * d3, d0, d1, d2); + } + } + + public void h() { + super.h(); + if (!this.world.isStatic) { + this.bL(); + } + } + + protected float f(float f, float f1) { + if (this.bk()) { + this.bn.a(); + return f1; + } else { + return super.f(f, f1); + } + } + + protected String t() { + return null; + } + + protected Item getLoot() { + return Item.getById(0); + } + + protected void dropDeathLoot(boolean flag, int i) { + Item item = this.getLoot(); + + if (item != null) { + int j = this.random.nextInt(3); + + if (i > 0) { + j += this.random.nextInt(i + 1); + } + + for (int k = 0; k < j; ++k) { + this.a(item, 1); + } + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("CanPickUpLoot", this.bJ()); + nbttagcompound.setBoolean("PersistenceRequired", this.persistent); + NBTTagList nbttaglist = new NBTTagList(); + + NBTTagCompound nbttagcompound1; + + for (int i = 0; i < this.equipment.length; ++i) { + nbttagcompound1 = new NBTTagCompound(); + if (this.equipment[i] != null) { + this.equipment[i].save(nbttagcompound1); + } + + nbttaglist.add(nbttagcompound1); + } + + nbttagcompound.set("Equipment", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + + for (int j = 0; j < this.dropChances.length; ++j) { + nbttaglist1.add(new NBTTagFloat(this.dropChances[j])); + } + + nbttagcompound.set("DropChances", nbttaglist1); + nbttagcompound.setString("CustomName", this.getCustomName()); + nbttagcompound.setBoolean("CustomNameVisible", this.getCustomNameVisible()); + nbttagcompound.setBoolean("Leashed", this.bv); + if (this.bw != null) { + nbttagcompound1 = new NBTTagCompound(); + if (this.bw instanceof EntityLiving) { + nbttagcompound1.setLong("UUIDMost", this.bw.getUniqueID().getMostSignificantBits()); + nbttagcompound1.setLong("UUIDLeast", this.bw.getUniqueID().getLeastSignificantBits()); + } else if (this.bw instanceof EntityHanging) { + EntityHanging entityhanging = (EntityHanging) this.bw; + + nbttagcompound1.setInt("X", entityhanging.x); + nbttagcompound1.setInt("Y", entityhanging.y); + nbttagcompound1.setInt("Z", entityhanging.z); + } + + nbttagcompound.set("Leash", nbttagcompound1); + } + + // Poweruser start + else if(this.bx != null) { + nbttagcompound.set("Leash", this.bx); + } + // Poweruser end + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + + // CraftBukkit start - If looting or persistence is false only use it if it was set after we started using it + boolean data = nbttagcompound.getBoolean("CanPickUpLoot"); + if (isLevelAtLeast(nbttagcompound, 1) || data) { + this.canPickUpLoot = data; + } + + data = nbttagcompound.getBoolean("PersistenceRequired"); + if (isLevelAtLeast(nbttagcompound, 1) || data) { + this.persistent = data; + } + // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("CustomName", 8) && nbttagcompound.getString("CustomName").length() > 0) { + this.setCustomName(nbttagcompound.getString("CustomName")); + } + + this.setCustomNameVisible(nbttagcompound.getBoolean("CustomNameVisible")); + NBTTagList nbttaglist; + int i; + + if (nbttagcompound.hasKeyOfType("Equipment", 9)) { + nbttaglist = nbttagcompound.getList("Equipment", 10); + + for (i = 0; i < this.equipment.length; ++i) { + this.equipment[i] = ItemStack.createStack(nbttaglist.get(i)); + } + } + + if (nbttagcompound.hasKeyOfType("DropChances", 9)) { + nbttaglist = nbttagcompound.getList("DropChances", 5); + + for (i = 0; i < nbttaglist.size(); ++i) { + this.dropChances[i] = nbttaglist.e(i); + } + } + + this.bv = nbttagcompound.getBoolean("Leashed"); + if (this.bv && nbttagcompound.hasKeyOfType("Leash", 10)) { + this.bx = nbttagcompound.getCompound("Leash"); + } + } + + public void n(float f) { + this.be = f; + } + + public void i(float f) { + super.i(f); + this.n(f); + } + + public void e() { + super.e(); + this.world.methodProfiler.a("looting"); + if (!this.world.isStatic && this.bJ() && !this.aT && this.world.getGameRules().getBoolean("mobGriefing")) { + List list = this.world.a(EntityItem.class, this.boundingBox.grow(1.0D, 0.0D, 1.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityItem entityitem = (EntityItem) iterator.next(); + + if (!entityitem.dead && entityitem.getItemStack() != null) { + ItemStack itemstack = entityitem.getItemStack(); + int i = b(itemstack); + + if (i > -1) { + boolean flag = true; + ItemStack itemstack1 = this.getEquipment(i); + + if (itemstack1 != null) { + if (i == 0) { + if (itemstack.getItem() instanceof ItemSword && !(itemstack1.getItem() instanceof ItemSword)) { + flag = true; + } else if (itemstack.getItem() instanceof ItemSword && itemstack1.getItem() instanceof ItemSword) { + ItemSword itemsword = (ItemSword) itemstack.getItem(); + ItemSword itemsword1 = (ItemSword) itemstack1.getItem(); + + if (itemsword.i() == itemsword1.i()) { + flag = itemstack.getData() > itemstack1.getData() || itemstack.hasTag() && !itemstack1.hasTag(); + } else { + flag = itemsword.i() > itemsword1.i(); + } + } else { + flag = false; + } + } else if (itemstack.getItem() instanceof ItemArmor && !(itemstack1.getItem() instanceof ItemArmor)) { + flag = true; + } else if (itemstack.getItem() instanceof ItemArmor && itemstack1.getItem() instanceof ItemArmor) { + ItemArmor itemarmor = (ItemArmor) itemstack.getItem(); + ItemArmor itemarmor1 = (ItemArmor) itemstack1.getItem(); + + if (itemarmor.c == itemarmor1.c) { + flag = itemstack.getData() > itemstack1.getData() || itemstack.hasTag() && !itemstack1.hasTag(); + } else { + flag = itemarmor.c > itemarmor1.c; + } + } else { + flag = false; + } + } + + if (flag) { + if (itemstack1 != null && this.random.nextFloat() - 0.1F < this.dropChances[i]) { + this.a(itemstack1, 0.0F); + } + + if (itemstack.getItem() == Items.DIAMOND && entityitem.j() != null) { + EntityHuman entityhuman = this.world.a(entityitem.j()); + + if (entityhuman != null) { + entityhuman.a((Statistic) AchievementList.x); + } + } + + this.setEquipment(i, itemstack); + this.dropChances[i] = 2.0F; + this.persistent = true; + this.receive(entityitem, 1); + entityitem.die(); + } + } + } + } + } + + this.world.methodProfiler.b(); + } + + protected boolean bk() { + return false; + } + + protected boolean isTypeNotPersistent() { + return true; + } + + // Kohi start - check for despawn on inactive ticks + public void inactiveTick() { + super.inactiveTick(); + this.w(); + } + // Kohi end + + protected void w() { + this.aU++; // MineHQ + if (this.persistent) { + this.aU = 0; + } else if (this.ticksLived % 50 == 0) { // Kohi - only check every 50 ticks + // MineHQ start + if (this.world.hardDespawnDistance == -1D) this.world.hardDespawnDistance = Math.sqrt(this.world.paperSpigotConfig.hardDespawnDistance); + EntityHuman entityhuman = this.world.findNearbyPlayerWhoAffectsSpawning(this, this.world.hardDespawnDistance); // PaperSpigot + // MineHQ end + + if (entityhuman != null) { + double d0 = entityhuman.locX - this.locX; + double d1 = entityhuman.locY - this.locY; + double d2 = entityhuman.locZ - this.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d3 > this.world.paperSpigotConfig.hardDespawnDistance) { // CraftBukkit - remove isTypeNotPersistent() check // PaperSpigot - custom despawn distances + this.die(); + } + + // Kohi - decrease random check to account for decreased interval + // MineHQ - decrease random check even more for performance + // MineHQ - remove random + if (this.aU > 600 && d3 > this.world.paperSpigotConfig.softDespawnDistance) { // CraftBukkit - remove isTypeNotPersistent() check // PaperSpigot - custom despawn distances + this.die(); + } else if (d3 < this.world.paperSpigotConfig.softDespawnDistance) { // PaperSpigot - custom despawn distances + this.aU = 0; + } + } + // MineHQ start + else { + if (this.aU > 600) { + this.die(); + } + } + // MineHQ end + } + } + + protected void bn() { + //++this.aU; // MineHQ + this.navigation.cleanUpExpiredSearches(); // Poweruser + this.world.methodProfiler.a("checkDespawn"); + this.w(); + this.world.methodProfiler.b(); + // Spigot Start + if ( this.fromMobSpawner ) + { + // PaperSpigot start - Allow nerfed mobs to jump + this.world.methodProfiler.a("goalSelector"); + this.goalSelector.a(); + this.world.methodProfiler.c("jump"); + this.bm.b(); + // PaperSpigot end + return; + } + // Spigot End + this.world.methodProfiler.a("sensing"); + this.bq.a(); + this.world.methodProfiler.b(); + this.world.methodProfiler.a("targetSelector"); + this.targetSelector.a(); + this.world.methodProfiler.b(); + this.world.methodProfiler.a("goalSelector"); + this.goalSelector.a(); + this.world.methodProfiler.b(); + this.world.methodProfiler.a("navigation"); + this.navigation.f(); + this.world.methodProfiler.b(); + this.world.methodProfiler.a("mob tick"); + this.bp(); + this.world.methodProfiler.b(); + this.world.methodProfiler.a("controls"); + this.world.methodProfiler.a("move"); + this.moveController.c(); + this.world.methodProfiler.c("look"); + this.lookController.a(); + this.world.methodProfiler.c("jump"); + this.bm.b(); + this.world.methodProfiler.b(); + this.world.methodProfiler.b(); + } + + protected void bq() { + super.bq(); + this.bd = 0.0F; + this.be = 0.0F; + this.w(); + float f = 8.0F; + + if (this.random.nextFloat() < 0.02F) { + EntityHuman entityhuman = this.world.findNearbyPlayer(this, (double) f); + + if (entityhuman != null) { + this.bu = entityhuman; + this.g = 10 + this.random.nextInt(20); + } else { + this.bf = (this.random.nextFloat() - 0.5F) * 20.0F; + } + } + + if (this.bu != null) { + this.a(this.bu, 10.0F, (float) this.x()); + if (this.g-- <= 0 || this.bu.dead || this.bu.f((Entity) this) > (double) (f * f)) { + this.bu = null; + } + } else { + if (this.random.nextFloat() < 0.05F) { + this.bf = (this.random.nextFloat() - 0.5F) * 20.0F; + } + + this.yaw += this.bf; + this.pitch = this.f; + } + + boolean flag = this.M(); + boolean flag1 = this.P(); + + if (flag || flag1) { + this.bc = this.random.nextFloat() < 0.8F; + } + } + + public int x() { + return 40; + } + + public void a(Entity entity, float f, float f1) { + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2; + + if (entity instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) entity; + + d2 = entityliving.locY + (double) entityliving.getHeadHeight() - (this.locY + (double) this.getHeadHeight()); + } else { + d2 = (entity.boundingBox.b + entity.boundingBox.e) / 2.0D - (this.locY + (double) this.getHeadHeight()); + } + + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1); + float f2 = (float) (Math.atan2(d1, d0) * 180.0D / 3.1415927410125732D) - 90.0F; + float f3 = (float) (-(Math.atan2(d2, d3) * 180.0D / 3.1415927410125732D)); + + this.pitch = this.b(this.pitch, f3, f1); + this.yaw = this.b(this.yaw, f2, f); + } + + private float b(float f, float f1, float f2) { + float f3 = MathHelper.g(f1 - f); + + if (f3 > f2) { + f3 = f2; + } + + if (f3 < -f2) { + f3 = -f2; + } + + return f + f3; + } + + public boolean canSpawn() { + return this.world.b(this.boundingBox) && this.world.getCubes(this, this.boundingBox).isEmpty() && !this.world.containsLiquid(this.boundingBox); + } + + public int bB() { + return 4; + } + + public int ax() { + if (this.getGoalTarget() == null) { + return 3; + } else { + int i = (int) (this.getHealth() - this.getMaxHealth() * 0.33F); + + i -= (3 - this.world.difficulty.a()) * 4; + if (i < 0) { + i = 0; + } + + return i + 3; + } + } + + public ItemStack be() { + return this.equipment[0]; + } + + public ItemStack getEquipment(int i) { + return this.equipment[i]; + } + + public ItemStack r(int i) { + return this.equipment[i + 1]; + } + + public void setEquipment(int i, ItemStack itemstack) { + this.equipment[i] = itemstack; + } + + public ItemStack[] getEquipment() { + return this.equipment; + } + + protected void dropEquipment(boolean flag, int i) { + for (int j = 0; j < this.getEquipment().length; ++j) { + ItemStack itemstack = this.getEquipment(j); + boolean flag1 = this.dropChances[j] > 1.0F; + + if (itemstack != null && (flag || flag1) && this.random.nextFloat() - (float) i * 0.01F < this.dropChances[j]) { + if (!flag1 && itemstack.g()) { + int k = Math.max(itemstack.l() - 25, 1); + int l = itemstack.l() - this.random.nextInt(this.random.nextInt(k) + 1); + + if (l > k) { + l = k; + } + + if (l < 1) { + l = 1; + } + + itemstack.setData(l); + } + + this.a(itemstack, 0.0F); + } + } + } + + protected void bC() { + if (this.random.nextFloat() < 0.15F * this.world.b(this.locX, this.locY, this.locZ)) { + int i = this.random.nextInt(2); + float f = this.world.difficulty == EnumDifficulty.HARD ? 0.1F : 0.25F; + + if (this.random.nextFloat() < 0.095F) { + ++i; + } + + if (this.random.nextFloat() < 0.095F) { + ++i; + } + + if (this.random.nextFloat() < 0.095F) { + ++i; + } + + for (int j = 3; j >= 0; --j) { + ItemStack itemstack = this.r(j); + + if (j < 3 && this.random.nextFloat() < f) { + break; + } + + if (itemstack == null) { + Item item = a(j + 1, i); + + if (item != null) { + this.setEquipment(j + 1, new ItemStack(item)); + } + } + } + } + } + + public static int b(ItemStack itemstack) { + if (itemstack.getItem() != Item.getItemOf(Blocks.PUMPKIN) && itemstack.getItem() != Items.SKULL) { + if (itemstack.getItem() instanceof ItemArmor) { + switch (((ItemArmor) itemstack.getItem()).b) { + case 0: + return 4; + + case 1: + return 3; + + case 2: + return 2; + + case 3: + return 1; + } + } + + return 0; + } else { + return 4; + } + } + + public static Item a(int i, int j) { + switch (i) { + case 4: + if (j == 0) { + return Items.LEATHER_HELMET; + } else if (j == 1) { + return Items.GOLD_HELMET; + } else if (j == 2) { + return Items.CHAINMAIL_HELMET; + } else if (j == 3) { + return Items.IRON_HELMET; + } else if (j == 4) { + return Items.DIAMOND_HELMET; + } + + case 3: + if (j == 0) { + return Items.LEATHER_CHESTPLATE; + } else if (j == 1) { + return Items.GOLD_CHESTPLATE; + } else if (j == 2) { + return Items.CHAINMAIL_CHESTPLATE; + } else if (j == 3) { + return Items.IRON_CHESTPLATE; + } else if (j == 4) { + return Items.DIAMOND_CHESTPLATE; + } + + case 2: + if (j == 0) { + return Items.LEATHER_LEGGINGS; + } else if (j == 1) { + return Items.GOLD_LEGGINGS; + } else if (j == 2) { + return Items.CHAINMAIL_LEGGINGS; + } else if (j == 3) { + return Items.IRON_LEGGINGS; + } else if (j == 4) { + return Items.DIAMOND_LEGGINGS; + } + + case 1: + if (j == 0) { + return Items.LEATHER_BOOTS; + } else if (j == 1) { + return Items.GOLD_BOOTS; + } else if (j == 2) { + return Items.CHAINMAIL_BOOTS; + } else if (j == 3) { + return Items.IRON_BOOTS; + } else if (j == 4) { + return Items.DIAMOND_BOOTS; + } + + default: + return null; + } + } + + protected void bD() { + float f = this.world.b(this.locX, this.locY, this.locZ); + + if (this.be() != null && this.random.nextFloat() < 0.25F * f) { + EnchantmentManager.a(this.random, this.be(), (int) (5.0F + f * (float) this.random.nextInt(18))); + } + + for (int i = 0; i < 4; ++i) { + ItemStack itemstack = this.r(i); + + if (itemstack != null && this.random.nextFloat() < 0.5F * f) { + EnchantmentManager.a(this.random, itemstack, (int) (5.0F + f * (float) this.random.nextInt(18))); + } + } + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + this.getAttributeInstance(GenericAttributes.b).a(new AttributeModifier("Random spawn bonus", this.random.nextGaussian() * 0.05D, 1)); + return groupdataentity; + } + + public boolean bE() { + return false; + } + + public String getName() { + return this.hasCustomName() ? this.getCustomName() : super.getName(); + } + + public void bF() { + this.persistent = true; + } + + public void setCustomName(String s) { + this.datawatcher.watch(10, s); + this.datawatcher.watch(2, s); // Spigot - protocol patch + } + + public String getCustomName() { + return this.datawatcher.getString(10); + } + + public boolean hasCustomName() { + return this.datawatcher.getString(10).length() > 0; + } + + public void setCustomNameVisible(boolean flag) { + this.datawatcher.watch(11, Byte.valueOf((byte) (flag ? 1 : 0))); + this.datawatcher.watch(3, Byte.valueOf((byte) (flag ? 1 : 0))); // Spigot - protocol patch + } + + public boolean getCustomNameVisible() { + return this.datawatcher.getByte(11) == 1; + } + + public void a(int i, float f) { + this.dropChances[i] = f; + } + + public boolean bJ() { + return this.canPickUpLoot; + } + + public void h(boolean flag) { + this.canPickUpLoot = flag; + } + + public boolean isPersistent() { + return this.persistent; + } + + public final boolean c(EntityHuman entityhuman) { + if (this.bN() && this.getLeashHolder() == entityhuman) { + // CraftBukkit start - fire PlayerUnleashEntityEvent + if (CraftEventFactory.callPlayerUnleashEntityEvent(this, entityhuman).isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, this, this.getLeashHolder())); + return false; + } + // CraftBukkit end + this.unleash(true, !entityhuman.abilities.canInstantlyBuild); + return true; + } else { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() == Items.LEASH && this.bM()) { + if (!(this instanceof EntityTameableAnimal) || !((EntityTameableAnimal) this).isTamed()) { + // CraftBukkit start - fire PlayerLeashEntityEvent + if (CraftEventFactory.callPlayerLeashEntityEvent(this, entityhuman, entityhuman).isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, this, this.getLeashHolder())); + return false; + } + // CraftBukkit end + this.setLeashHolder(entityhuman, true); + --itemstack.count; + return true; + } + + if (((EntityTameableAnimal) this).e(entityhuman)) { + // CraftBukkit start - fire PlayerLeashEntityEvent + if (CraftEventFactory.callPlayerLeashEntityEvent(this, entityhuman, entityhuman).isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, this, this.getLeashHolder())); + return false; + } + // CraftBukkit end + this.setLeashHolder(entityhuman, true); + --itemstack.count; + return true; + } + } + + return this.a(entityhuman) ? true : super.c(entityhuman); + } + } + + protected boolean a(EntityHuman entityhuman) { + return false; + } + + protected void bL() { + if (this.bx != null) { + this.bP(); + } + + if (this.bv) { + if (this.bw == null || this.bw.dead) { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.HOLDER_GONE)); // CraftBukkit + this.unleash(true, true); + } + } + } + + public void unleash(boolean flag, boolean flag1) { + if (this.bv) { + this.bv = false; + this.bw = null; + if (!this.world.isStatic && flag1) { + this.a(Items.LEASH, 1); + } + + if (!this.world.isStatic && flag && this.world instanceof WorldServer) { + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutAttachEntity(1, this, (Entity) null))); + } + } + } + + public boolean bM() { + return !this.bN() && !(this instanceof IMonster); + } + + public boolean bN() { + return this.bv; + } + + public Entity getLeashHolder() { + return this.bw; + } + + public void setLeashHolder(Entity entity, boolean flag) { + this.bv = true; + this.bw = entity; + if (!this.world.isStatic && flag && this.world instanceof WorldServer) { + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutAttachEntity(1, this, this.bw))); + } + } + + private void bP() { + if (this.bv && this.bx != null) { + if (this.bx.hasKeyOfType("UUIDMost", 4) && this.bx.hasKeyOfType("UUIDLeast", 4)) { + UUID uuid = new UUID(this.bx.getLong("UUIDMost"), this.bx.getLong("UUIDLeast")); + List list = this.world.a(EntityLiving.class, this.boundingBox.grow(10.0D, 10.0D, 10.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + + if (entityliving.getUniqueID().equals(uuid)) { + this.setLeashHolder(entityliving, true); // Poweruser + break; + } + } + // Poweruser start + } + if (this.bw == null && this.bx.hasKeyOfType("X", 99) && this.bx.hasKeyOfType("Y", 99) && this.bx.hasKeyOfType("Z", 99)) { + // Poweruser end + int i = this.bx.getInt("X"); + int j = this.bx.getInt("Y"); + int k = this.bx.getInt("Z"); + EntityLeash entityleash = EntityLeash.b(this.world, i, j, k); + + if (entityleash == null) { + entityleash = EntityLeash.a(this.world, i, j, k); + } + // Poweruser start + this.setLeashHolder(entityleash, true); + } + if (this.bw == null) { + // Poweruser end + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit + this.unleash(false, true); + } + } + + this.bx = null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityIronGolem.java b/vspigot-server/src/main/java/net/minecraft/server/EntityIronGolem.java new file mode 100644 index 0000000..a274d03 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityIronGolem.java @@ -0,0 +1,190 @@ +package net.minecraft.server; + +public class EntityIronGolem extends EntityGolem { + + private int bq; + Village bp; + private int br; + private int bs; + + public EntityIronGolem(World world) { + super(world); + this.a(1.4F, 2.9F); + this.getNavigation().a(true); + this.goalSelector.a(1, new PathfinderGoalMeleeAttack(this, 1.0D, true)); + this.goalSelector.a(2, new PathfinderGoalMoveTowardsTarget(this, 0.9D, 32.0F)); + this.goalSelector.a(3, new PathfinderGoalMoveThroughVillage(this, 0.6D, true)); + this.goalSelector.a(4, new PathfinderGoalMoveTowardsRestriction(this, 1.0D)); + this.goalSelector.a(5, new PathfinderGoalOfferFlower(this)); + this.goalSelector.a(6, new PathfinderGoalRandomStroll(this, 0.6D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalDefendVillage(this)); + this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget(this, EntityInsentient.class, 0, false, true, IMonster.a)); + } + + protected void c() { + super.c(); + this.datawatcher.a(16, Byte.valueOf((byte) 0)); + } + + public boolean bk() { + return true; + } + + protected void bp() { + if (--this.bq <= 0) { + this.bq = 70 + this.random.nextInt(50); + this.bp = this.world.villages.getClosestVillage(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ), 32); + if (this.bp == null) { + this.bX(); + } else { + ChunkCoordinates chunkcoordinates = this.bp.getCenter(); + + this.a(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z, (int) ((float) this.bp.getSize() * 0.6F)); + } + } + + super.bp(); + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(100.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.25D); + } + + protected int j(int i) { + return i; + } + + protected void o(Entity entity) { + if (entity instanceof IMonster && this.aI().nextInt(20) == 0) { + // CraftBukkit start + org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this, (EntityLiving) entity, org.bukkit.event.entity.EntityTargetEvent.TargetReason.COLLISION); + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.setGoalTarget(null); + } else { + this.setGoalTarget(((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle()); + } + } + // CraftBukkit end + } + + super.o(entity); + } + + public void e() { + super.e(); + if (this.br > 0) { + --this.br; + } + + if (this.bs > 0) { + --this.bs; + } + + if (this.motX * this.motX + this.motZ * this.motZ > 2.500000277905201E-7D && this.random.nextInt(5) == 0) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY - 0.20000000298023224D - (double) this.height); + int k = MathHelper.floor(this.locZ); + Block block = this.world.getType(i, j, k); + + if (block.getMaterial() != Material.AIR) { + this.world.addParticle("blockcrack_" + Block.getId(block) + "_" + this.world.getData(i, j, k), this.locX + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, this.boundingBox.b + 0.1D, this.locZ + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, 4.0D * ((double) this.random.nextFloat() - 0.5D), 0.5D, ((double) this.random.nextFloat() - 0.5D) * 4.0D); + } + } + } + + public boolean a(Class oclass) { + return this.isPlayerCreated() && EntityHuman.class.isAssignableFrom(oclass) ? false : super.a(oclass); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("PlayerCreated", this.isPlayerCreated()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setPlayerCreated(nbttagcompound.getBoolean("PlayerCreated")); + } + + public boolean n(Entity entity) { + this.br = 10; + this.world.broadcastEntityEffect(this, (byte) 4); + boolean flag = entity.damageEntity(DamageSource.mobAttack(this), (float) (7 + this.random.nextInt(15))); + + if (flag) { + entity.motY += 0.4000000059604645D; + } + + this.makeSound("mob.irongolem.throw", 1.0F, 1.0F); + return flag; + } + + public Village bZ() { + return this.bp; + } + + public void a(boolean flag) { + this.bs = flag ? 400 : 0; + this.world.broadcastEntityEffect(this, (byte) 11); + } + + protected String aT() { + return "mob.irongolem.hit"; + } + + protected String aU() { + return "mob.irongolem.death"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.irongolem.walk", 1.0F, 1.0F); + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(3); + + int k; + + for (k = 0; k < j; ++k) { + this.a(Item.getItemOf(Blocks.RED_ROSE), 1, 0.0F); + } + + k = 3 + this.random.nextInt(3); + + for (int l = 0; l < k; ++l) { + this.a(Items.IRON_INGOT, 1); + } + } + + public int cb() { + return this.bs; + } + + public boolean isPlayerCreated() { + return (this.datawatcher.getByte(16) & 1) != 0; + } + + public void setPlayerCreated(boolean flag) { + byte b0 = this.datawatcher.getByte(16); + + if (flag) { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 | 1))); + } else { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 & -2))); + } + } + + public void die(DamageSource damagesource) { + if (!this.isPlayerCreated() && this.killer != null && this.bp != null) { + this.bp.a(this.killer.getName(), -5); + } + + super.die(damagesource); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityItem.java b/vspigot-server/src/main/java/net/minecraft/server/EntityItem.java new file mode 100644 index 0000000..97830f3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityItem.java @@ -0,0 +1,389 @@ +package net.minecraft.server; + +import java.util.Iterator; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.bukkit.event.player.PlayerPickupItemEvent; // CraftBukkit + +public class EntityItem extends Entity { + + private static final Logger d = LogManager.getLogger(); + public int age; + public int pickupDelay; + private int e; + private String f; + private String g; + public float c; + private int lastTick = MinecraftServer.currentTick; // CraftBukkit + + public EntityItem(World world, double d0, double d1, double d2) { + super(world); + this.e = 5; + this.c = (float) (Math.random() * 3.141592653589793D * 2.0D); + this.a(0.25F, 0.25F); + this.height = this.length / 2.0F; + this.setPosition(d0, d1, d2); + this.yaw = (float) (Math.random() * 360.0D); + this.motX = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D)); + this.motY = 0.20000000298023224D; + this.motZ = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D)); + } + + public EntityItem(World world, double d0, double d1, double d2, ItemStack itemstack) { + this(world, d0, d1, d2); + // CraftBukkit start - Can't set null items in the datawatcher + if (itemstack == null || itemstack.getItem() == null) { + return; + } + // CraftBukkit end + this.setItemStack(itemstack); + } + + protected boolean g_() { + return false; + } + + public EntityItem(World world) { + super(world); + this.e = 5; + this.c = (float) (Math.random() * 3.141592653589793D * 2.0D); + this.a(0.25F, 0.25F); + this.height = this.length / 2.0F; + } + + protected void c() { + this.getDataWatcher().add(10, 5); + } + + public void h() { + if (this.getItemStack() == null) { + this.die(); + } else { + super.h(); + // CraftBukkit start - Use wall time for pickup and despawn timers + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.pickupDelay -= elapsedTicks; + this.age += elapsedTicks; + this.lastTick = MinecraftServer.currentTick; + // CraftBukkit end + + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.motY -= 0.03999999910593033D; + this.X = this.j(this.locX, (this.boundingBox.b + this.boundingBox.e) / 2.0D, this.locZ); + this.move(this.motX, this.motY, this.motZ); + boolean flag = (int) this.lastX != (int) this.locX || (int) this.lastY != (int) this.locY || (int) this.lastZ != (int) this.locZ; + + if (flag || this.ticksLived % 25 == 0) { + if (this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)).getMaterial() == Material.LAVA) { + this.motY = 0.20000000298023224D; + this.motX = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.motZ = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.makeSound("random.fizz", 0.4F, 2.0F + this.random.nextFloat() * 0.4F); + } + + if (!this.world.isStatic) { + this.k(); + } + } + + float f = 0.98F; + + if (this.onGround) { + f = this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.boundingBox.b) - 1, MathHelper.floor(this.locZ)).frictionFactor * 0.98F; + } + + this.motX *= (double) f; + this.motY *= 0.9800000190734863D; + this.motZ *= (double) f; + if (this.onGround) { + this.motY *= -0.5D; + } + // Spigot start - Make the hopper(s) below this item active. + // Called each tick on each item entity. + if (this.world.spigotConfig.altHopperTicking) { + int xi = MathHelper.floor(this.boundingBox.a); + int yi = MathHelper.floor(this.boundingBox.b) - 1; + int zi = MathHelper.floor(this.boundingBox.c); + int xf = MathHelper.floor(this.boundingBox.d); + int yf = MathHelper.floor(this.boundingBox.e) - 1; + int zf = MathHelper.floor(this.boundingBox.f); + for (int a = xi; a <= xf; a++) { + for (int c = zi; c <= zf; c++) { + for (int b = yi; b <= yf; b++) { + TileEntity tileEntity = this.world.getTileEntity(a, b, c); + if (tileEntity instanceof TileEntityHopper) { + ((TileEntityHopper) tileEntity).makeTick(); + } + } + } + } + } + // Spigot end + + // ++this.age; // CraftBukkit - Moved up + if (!this.world.isStatic && this.age >= world.spigotConfig.itemDespawnRate) { // Spigot + // CraftBukkit start - fire ItemDespawnEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { + this.age = 0; + return; + } + // CraftBukkit end + this.die(); + } + } + } + + // PaperSpigot start - copied from above + @Override + public void inactiveTick() { + // CraftBukkit start - Use wall time for pickup and despawn timers + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.pickupDelay -= elapsedTicks; + this.age += elapsedTicks; + this.lastTick = MinecraftServer.currentTick; + // CraftBukkit end + + if (!this.world.isStatic && this.age >= world.spigotConfig.itemDespawnRate) { // Spigot + // CraftBukkit start - fire ItemDespawnEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { + this.age = 0; + return; + } + // CraftBukkit end + this.die(); + } + } + // PaperSpigot end + + private void k() { + // Spigot start + double radius = world.spigotConfig.itemMerge; + Iterator iterator = this.world.a(EntityItem.class, this.boundingBox.grow(radius, radius, radius)).iterator(); + // Spigot end + + while (iterator.hasNext()) { + EntityItem entityitem = (EntityItem) iterator.next(); + + this.a(entityitem); + } + } + + public boolean a(EntityItem entityitem) { + if (entityitem == this) { + return false; + } else if (entityitem.isAlive() && this.isAlive()) { + ItemStack itemstack = this.getItemStack(); + ItemStack itemstack1 = entityitem.getItemStack(); + + if (itemstack1.getItem() != itemstack.getItem()) { + return false; + } else if (itemstack1.hasTag() ^ itemstack.hasTag()) { + return false; + } else if (itemstack1.hasTag() && !itemstack1.getTag().equals(itemstack.getTag())) { + return false; + } else if (itemstack1.getItem() == null) { + return false; + } else if (itemstack1.getItem().n() && itemstack1.getData() != itemstack.getData()) { + return false; + } else if (itemstack1.count < itemstack.count) { + return entityitem.a(this); + } else if (itemstack1.count + itemstack.count > itemstack1.getMaxStackSize()) { + return false; + } else { + // Spigot start + itemstack.count += itemstack1.count; + this.pickupDelay = Math.max(entityitem.pickupDelay, this.pickupDelay); + this.age = Math.min(entityitem.age, this.age); + this.setItemStack(itemstack); + entityitem.die(); + // Spigot end + return true; + } + } else { + return false; + } + } + + public void e() { + this.age = 4800; + } + + public boolean N() { + return this.world.a(this.boundingBox, Material.WATER, (Entity) this); + } + + protected void burn(int i) { + this.damageEntity(DamageSource.FIRE, (float) i); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else if (this.getItemStack() != null && this.getItemStack().getItem() == Items.NETHER_STAR && damagesource.isExplosion()) { + return false; + } else { + this.Q(); + this.e = (int) ((float) this.e - f); + if (this.e <= 0) { + this.die(); + } + + return false; + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("Health", (short) ((byte) this.e)); + nbttagcompound.setShort("Age", (short) this.age); + if (this.j() != null) { + nbttagcompound.setString("Thrower", this.f); + } + + if (this.i() != null) { + nbttagcompound.setString("Owner", this.g); + } + + if (this.getItemStack() != null) { + nbttagcompound.set("Item", this.getItemStack().save(new NBTTagCompound())); + } + } + + public void a(NBTTagCompound nbttagcompound) { + this.e = nbttagcompound.getShort("Health") & 255; + this.age = nbttagcompound.getShort("Age"); + if (nbttagcompound.hasKey("Owner")) { + this.g = nbttagcompound.getString("Owner"); + } + + if (nbttagcompound.hasKey("Thrower")) { + this.f = nbttagcompound.getString("Thrower"); + } + + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Item"); + + // CraftBukkit start - Handle missing "Item" compounds + if (nbttagcompound1 != null) { + ItemStack itemstack = ItemStack.createStack(nbttagcompound1); + if (itemstack != null) { + this.setItemStack(itemstack); + } else { + this.die(); + } + } else { + this.die(); + } + // CraftBukkit end + if (this.getItemStack() == null) { + this.die(); + } + } + + public void b_(EntityHuman entityhuman) { + if (!this.world.isStatic) { + ItemStack itemstack = this.getItemStack(); + int i = itemstack.count; + + // CraftBukkit start - fire PlayerPickupItemEvent + int canHold = entityhuman.inventory.canHold(itemstack); + int remaining = itemstack.count - canHold; + + if (this.pickupDelay <= 0 && canHold > 0) { + itemstack.count = canHold; + PlayerPickupItemEvent event = new PlayerPickupItemEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + // event.setCancelled(!entityhuman.canPickUpLoot); TODO + this.world.getServer().getPluginManager().callEvent(event); + itemstack.count = canHold + remaining; + + if (event.isCancelled()) { + return; + } + + // Possibly < 0; fix here so we do not have to modify code below + this.pickupDelay = 0; + } + // CraftBukkit end + + if (this.pickupDelay == 0 && (this.g == null || 6000 - this.age <= 200 || this.g.equals(entityhuman.getName())) && entityhuman.inventory.pickup(itemstack)) { + if (itemstack.getItem() == Item.getItemOf(Blocks.LOG)) { + entityhuman.a((Statistic) AchievementList.g); + } + + if (itemstack.getItem() == Item.getItemOf(Blocks.LOG2)) { + entityhuman.a((Statistic) AchievementList.g); + } + + if (itemstack.getItem() == Items.LEATHER) { + entityhuman.a((Statistic) AchievementList.t); + } + + if (itemstack.getItem() == Items.DIAMOND) { + entityhuman.a((Statistic) AchievementList.w); + } + + if (itemstack.getItem() == Items.BLAZE_ROD) { + entityhuman.a((Statistic) AchievementList.A); + } + + if (itemstack.getItem() == Items.DIAMOND && this.j() != null) { + EntityHuman entityhuman1 = this.world.a(this.j()); + + if (entityhuman1 != null && entityhuman1 != entityhuman) { + entityhuman1.a((Statistic) AchievementList.x); + } + } + + this.world.makeSound(entityhuman, "random.pop", 0.2F, ((this.random.nextFloat() - this.random.nextFloat()) * 0.7F + 1.0F) * 2.0F); + entityhuman.receive(this, i); + if (itemstack.count <= 0) { + this.die(); + } + } + } + } + + public String getName() { + return LocaleI18n.get("item." + this.getItemStack().a()); + } + + public boolean av() { + return false; + } + + public void b(int i) { + super.b(i); + if (!this.world.isStatic) { + this.k(); + } + } + + public ItemStack getItemStack() { + ItemStack itemstack = this.getDataWatcher().getItemStack(10); + + return itemstack == null ? new ItemStack(Blocks.STONE) : itemstack; + } + + public void setItemStack(ItemStack itemstack) { + this.getDataWatcher().watch(10, itemstack); + this.getDataWatcher().update(10); + } + + public String i() { + return this.g; + } + + public void a(String s) { + this.g = s; + } + + public String j() { + return this.f; + } + + public void b(String s) { + this.f = s; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityItemFrame.java b/vspigot-server/src/main/java/net/minecraft/server/EntityItemFrame.java new file mode 100644 index 0000000..5109b5d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityItemFrame.java @@ -0,0 +1,161 @@ +package net.minecraft.server; + +public class EntityItemFrame extends EntityHanging { + + private float e = 1.0F; + + public EntityItemFrame(World world) { + super(world); + } + + public EntityItemFrame(World world, int i, int j, int k, int l) { + super(world, i, j, k, l); + this.setDirection(l); + } + + protected void c() { + this.getDataWatcher().add(2, 5); + this.getDataWatcher().a(3, Byte.valueOf((byte) 0)); + // Spigot start - protocol patch + this.getDataWatcher().add(8, 5); + this.getDataWatcher().a(9, Byte.valueOf((byte) 0)); + // Spigot end + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else if (this.getItem() != null) { + if (!this.world.isStatic) { + // CraftBukkit start - fire EntityDamageEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f, false) || this.dead) { + return true; + } + // CraftBukkit end + + this.b(damagesource.getEntity(), false); + this.setItem((ItemStack) null); + } + + return true; + } else { + return super.damageEntity(damagesource, f); + } + } + + public int f() { + return 9; + } + + public int i() { + return 9; + } + + public void b(Entity entity) { + this.b(entity, true); + } + + public void b(Entity entity, boolean flag) { + ItemStack itemstack = this.getItem(); + + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + if (entityhuman.abilities.canInstantlyBuild) { + this.b(itemstack); + return; + } + } + + if (flag) { + this.a(new ItemStack(Items.ITEM_FRAME), 0.0F); + } + + if (itemstack != null && this.random.nextFloat() < this.e) { + itemstack = itemstack.cloneItemStack(); + this.b(itemstack); + this.a(itemstack, 0.0F); + } + } + + private void b(ItemStack itemstack) { + if (itemstack != null) { + if (itemstack.getItem() == Items.MAP) { + WorldMap worldmap = ((ItemWorldMap) itemstack.getItem()).getSavedMap(itemstack, this.world); + + worldmap.decorations.remove("frame-" + this.getId()); + } + + itemstack.a((EntityItemFrame) null); + } + } + + public ItemStack getItem() { + return this.getDataWatcher().getItemStack(2); + } + + public void setItem(ItemStack itemstack) { + if (itemstack != null) { + itemstack = itemstack.cloneItemStack(); + itemstack.count = 1; + itemstack.a(this); + } + + this.getDataWatcher().watch(2, itemstack); + this.getDataWatcher().update(2); + // Spigot start - protocol patch + this.getDataWatcher().watch(8, itemstack); + this.getDataWatcher().update(8); + // Spigot end + } + + public int getRotation() { + return this.getDataWatcher().getByte(3); + } + + public void setRotation(int i) { + this.getDataWatcher().watch(3, Byte.valueOf((byte) (i % 4))); + this.getDataWatcher().watch(9, Byte.valueOf((byte) ((i % 4) * 2))); // Spigot - protocol patch + } + + public void b(NBTTagCompound nbttagcompound) { + if (this.getItem() != null) { + nbttagcompound.set("Item", this.getItem().save(new NBTTagCompound())); + nbttagcompound.setByte("ItemRotation", (byte) this.getRotation()); + nbttagcompound.setFloat("ItemDropChance", this.e); + } + + super.b(nbttagcompound); + } + + public void a(NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Item"); + + if (nbttagcompound1 != null && !nbttagcompound1.isEmpty()) { + this.setItem(ItemStack.createStack(nbttagcompound1)); + this.setRotation(nbttagcompound.getByte("ItemRotation")); + if (nbttagcompound.hasKeyOfType("ItemDropChance", 99)) { + this.e = nbttagcompound.getFloat("ItemDropChance"); + } + } + + super.a(nbttagcompound); + } + + public boolean c(EntityHuman entityhuman) { + if (this.getItem() == null) { + ItemStack itemstack = entityhuman.be(); + + if (itemstack != null && !this.world.isStatic) { + this.setItem(itemstack); + if (!entityhuman.abilities.canInstantlyBuild && --itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + } + } else if (!this.world.isStatic) { + this.setRotation(this.getRotation() + 1); + } + + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityLargeFireball.java b/vspigot-server/src/main/java/net/minecraft/server/EntityLargeFireball.java new file mode 100644 index 0000000..326f7ea --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityLargeFireball.java @@ -0,0 +1,49 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit + +public class EntityLargeFireball extends EntityFireball { + + public int yield = 1; + + public EntityLargeFireball(World world) { + super(world); + } + + public EntityLargeFireball(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(world, entityliving, d0, d1, d2); + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isStatic) { + if (movingobjectposition.entity != null) { + movingobjectposition.entity.damageEntity(DamageSource.fireball(this, this.shooter), 6.0F); + } + + // CraftBukkit start - fire ExplosionPrimeEvent + ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(this.world.getServer(), this)); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + // give 'this' instead of (Entity) null so we know what causes the damage + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), this.world.getGameRules().getBoolean("mobGriefing")); + } + // CraftBukkit end + + this.die(); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("ExplosionPower", this.yield); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("ExplosionPower", 99)) { + // CraftBukkit - set bukkitYield when setting explosionpower + this.bukkitYield = this.yield = nbttagcompound.getInt("ExplosionPower"); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityLeash.java b/vspigot-server/src/main/java/net/minecraft/server/EntityLeash.java new file mode 100644 index 0000000..9b2ac82 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityLeash.java @@ -0,0 +1,137 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityLeash extends EntityHanging { + + public EntityLeash(World world) { + super(world); + } + + public EntityLeash(World world, int i, int j, int k) { + super(world, i, j, k, 0); + this.setPosition((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D); + } + + protected void c() { + super.c(); + } + + public void setDirection(int i) {} + + public int f() { + return 9; + } + + public int i() { + return 9; + } + + public void b(Entity entity) {} + + public boolean d(NBTTagCompound nbttagcompound) { + return false; + } + + public void b(NBTTagCompound nbttagcompound) {} + + public void a(NBTTagCompound nbttagcompound) {} + + public boolean c(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.be(); + boolean flag = false; + double d0; + List list; + Iterator iterator; + EntityInsentient entityinsentient; + + if (itemstack != null && itemstack.getItem() == Items.LEASH && !this.world.isStatic) { + d0 = 7.0D; + list = this.world.a(EntityInsentient.class, AxisAlignedBB.a(this.locX - d0, this.locY - d0, this.locZ - d0, this.locX + d0, this.locY + d0, this.locZ + d0)); + if (list != null) { + iterator = list.iterator(); + + while (iterator.hasNext()) { + entityinsentient = (EntityInsentient) iterator.next(); + if (entityinsentient.bN() && entityinsentient.getLeashHolder() == entityhuman) { + // CraftBukkit start + if (CraftEventFactory.callPlayerLeashEntityEvent(entityinsentient, this, entityhuman).isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, entityinsentient, entityinsentient.getLeashHolder())); + continue; + } + // CraftBukkit end + entityinsentient.setLeashHolder(this, true); + flag = true; + } + } + } + } + + if (!this.world.isStatic && !flag) { + // CraftBukkit start - Move below + // this.die(); + boolean die = true; + // CraftBukkit end + if (true || entityhuman.abilities.canInstantlyBuild) { // CraftBukkit - Process for non-creative as well + d0 = 7.0D; + list = this.world.a(EntityInsentient.class, AxisAlignedBB.a(this.locX - d0, this.locY - d0, this.locZ - d0, this.locX + d0, this.locY + d0, this.locZ + d0)); + if (list != null) { + iterator = list.iterator(); + + while (iterator.hasNext()) { + entityinsentient = (EntityInsentient) iterator.next(); + if (entityinsentient.bN() && entityinsentient.getLeashHolder() == this) { + // CraftBukkit start + if (CraftEventFactory.callPlayerUnleashEntityEvent(entityinsentient, entityhuman).isCancelled()) { + die = false; + continue; + } + entityinsentient.unleash(true, !entityhuman.abilities.canInstantlyBuild); // false -> survival mode boolean + // CraftBukkit end + } + } + } + } + // CraftBukkit start + if (die) { + this.die(); + } + // CraftBukkit end + } + + return true; + } + + public boolean survives() { + return this.world.getType(this.x, this.y, this.z).b() == 11; + } + + public static EntityLeash a(World world, int i, int j, int k) { + EntityLeash entityleash = new EntityLeash(world, i, j, k); + + entityleash.attachedToPlayer = true; + world.addEntity(entityleash); + return entityleash; + } + + public static EntityLeash b(World world, int i, int j, int k) { + List list = world.a(EntityLeash.class, AxisAlignedBB.a((double) i - 1.0D, (double) j - 1.0D, (double) k - 1.0D, (double) i + 1.0D, (double) j + 1.0D, (double) k + 1.0D)); + + if (list != null) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLeash entityleash = (EntityLeash) iterator.next(); + + if (entityleash.x == i && entityleash.y == j && entityleash.z == k) { + return entityleash; + } + } + } + + return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityLightning.java b/vspigot-server/src/main/java/net/minecraft/server/EntityLightning.java new file mode 100644 index 0000000..0671659 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityLightning.java @@ -0,0 +1,143 @@ +package net.minecraft.server; + +import java.util.List; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityLightning extends EntityWeather { + + private int lifeTicks; + public long a; + private int c; + + // CraftBukkit start + public boolean isEffect = false; + + public boolean isSilent = false; // Spigot + + public EntityLightning(World world, double d0, double d1, double d2) { + this(world, d0, d1, d2, false); + } + + public EntityLightning(World world, double d0, double d1, double d2, boolean isEffect) { + // CraftBukkit end + + super(world); + + // CraftBukkit - Set isEffect + this.isEffect = isEffect; + + this.setPositionRotation(d0, d1, d2, 0.0F, 0.0F); + this.lifeTicks = 2; + this.a = this.random.nextLong(); + this.c = this.random.nextInt(3) + 1; + + // CraftBukkit - add "!isEffect" + if (!isEffect && !world.isStatic && world.getGameRules().getBoolean("doFireTick") && (world.difficulty == EnumDifficulty.NORMAL || world.difficulty == EnumDifficulty.HARD) && world.areChunksLoaded(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2), 10)) { + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + + if (world.getType(i, j, k).getMaterial() == Material.AIR && Blocks.FIRE.canPlace(world, i, j, k)) { + // CraftBukkit start + if (!CraftEventFactory.callBlockIgniteEvent(world, i, j, k, this).isCancelled()) { + world.setTypeUpdate(i, j, k, Blocks.FIRE); + } + // CraftBukkit end + } + + for (i = 0; i < 4; ++i) { + j = MathHelper.floor(d0) + this.random.nextInt(3) - 1; + k = MathHelper.floor(d1) + this.random.nextInt(3) - 1; + int l = MathHelper.floor(d2) + this.random.nextInt(3) - 1; + + if (world.getType(j, k, l).getMaterial() == Material.AIR && Blocks.FIRE.canPlace(world, j, k, l)) { + // CraftBukkit start + if (!CraftEventFactory.callBlockIgniteEvent(world, j, k, l, this).isCancelled()) { + world.setTypeUpdate(j, k, l, Blocks.FIRE); + } + // CraftBukkit end + } + } + } + } + + // Spigot start + public EntityLightning(World world, double d0, double d1, double d2, boolean isEffect, boolean isSilent) + { + this( world, d0, d1, d2, isEffect ); + this.isSilent = isSilent; + } + // Spigot end + + public void h() { + super.h(); + if (!isSilent && this.lifeTicks == 2) { // Spigot + // CraftBukkit start - Use relative location for far away sounds + //this.world.makeSound(this.locX, this.locY, this.locZ, "ambient.weather.thunder", 10000.0F, 0.8F + this.random.nextFloat() * 0.2F); + float pitch = 0.8F + this.random.nextFloat() * 0.2F; + int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; + for (EntityPlayer player : (List) this.world.players) { + double deltaX = this.locX - player.locX; + double deltaZ = this.locZ - player.locZ; + double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; + if (distanceSquared > viewDistance * viewDistance) { + double deltaLength = Math.sqrt(distanceSquared); + double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; + double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; + player.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect("ambient.weather.thunder", relativeX, this.locY, relativeZ, 10000.0F, pitch)); + } else { + player.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect("ambient.weather.thunder", this.locX, this.locY, this.locZ, 10000.0F, pitch)); + } + } + // CraftBukkit end + this.world.makeSound(this.locX, this.locY, this.locZ, "random.explode", 2.0F, 0.5F + this.random.nextFloat() * 0.2F); + } + + --this.lifeTicks; + if (this.lifeTicks < 0) { + if (this.c == 0) { + this.die(); + } else if (this.lifeTicks < -this.random.nextInt(10)) { + --this.c; + this.lifeTicks = 1; + this.a = this.random.nextLong(); + // CraftBukkit - add "!isEffect" + if (!isEffect && !this.world.isStatic && this.world.getGameRules().getBoolean("doFireTick") && this.world.areChunksLoaded(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ), 10)) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (this.world.getType(i, j, k).getMaterial() == Material.AIR && Blocks.FIRE.canPlace(this.world, i, j, k)) { + // CraftBukkit start + if (!CraftEventFactory.callBlockIgniteEvent(world, i, j, k, this).isCancelled()) { + this.world.setTypeUpdate(i, j, k, Blocks.FIRE); + } + // CraftBukkit end + } + } + } + } + + if (this.lifeTicks >= 0 && !this.isEffect) { // CraftBukkit - add !this.isEffect + if (this.world.isStatic) { + this.world.q = 2; + } else { + double d0 = 3.0D; + List list = this.world.getEntities(this, AxisAlignedBB.a(this.locX - d0, this.locY - d0, this.locZ - d0, this.locX + d0, this.locY + 6.0D + d0, this.locZ + d0)); + + for (int l = 0; l < list.size(); ++l) { + Entity entity = (Entity) list.get(l); + + entity.a(this); + } + } + } + } + + protected void c() {} + + protected void a(NBTTagCompound nbttagcompound) {} + + protected void b(NBTTagCompound nbttagcompound) {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityLiving.java b/vspigot-server/src/main/java/net/minecraft/server/EntityLiving.java new file mode 100644 index 0000000..32f2d25 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityLiving.java @@ -0,0 +1,1875 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import net.valorhcf.knockback.Knockback; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.SpigotTimings; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.util.CraftPotionUtils; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.PotionEffectAddEvent; +import org.bukkit.event.entity.PotionEffectExpireEvent; +import org.bukkit.event.entity.PotionEffectExtendEvent; +import org.bukkit.event.entity.PotionEffectRemoveEvent; +import org.bukkit.event.player.PlayerAttackEvent; +import org.spigotmc.ActivationRange; +import org.spigotmc.SpigotConfig; + +import com.google.common.base.Function; + +import java.util.*; + +// CraftBukkit start +// CraftBukkit end + +public abstract class EntityLiving extends Entity { + + // joeleoli start + private Knockback knockback; + + public Knockback getKnockback() { + return knockback == null ? SpigotConfig.defaultKnockback : knockback; + } + + public void setKbProfile(Knockback profile) { + this.knockback = profile; + } + // joeleoli end + + private static final UUID b = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); + public static final AttributeModifier c = (new AttributeModifier(b, "Sprinting speed boost", 0.30000001192092896D, 2)).a(false); // Guardian: private -> public + private AttributeMapBase d; + public CombatTracker combatTracker = new CombatTracker(this); // CraftBukkit - private -> public, remove final + private final Map effects = new HashMap<>(); + private final ItemStack[] g = new ItemStack[5]; + public boolean at; + public int au; + public int av; + public float aw; + public int hurtTicks; + public int ay; + public float az; + public int deathTicks; + public int attackTicks; + public float aC; + public float aD; + public float aE; + public float aF; + public float aG; + public int maxNoDamageTicks = 20; + public float aI; + public float aJ; + public float aK; + public float aL; + public float aM; + public float aN; + public float aO; + public float aP; + public float aQ = 0.02F; + public EntityHuman killer; // CraftBukkit - protected -> public + protected int lastDamageByPlayerTime; + protected boolean aT; + protected int aU; + protected float aV; + protected float aW; + protected float aX; + protected float aY; + protected float aZ; + protected int ba; + public float lastDamage; // CraftBukkit - protected -> public + protected boolean bc; + public float bd; + public float be; + protected float bf; + protected int bg; + protected double bh; + protected double bi; + protected double bj; + protected double bk; + protected double bl; + public boolean updateEffects = true; // CraftBukkit - private -> public + public EntityLiving lastDamager; // CraftBukkit - private -> public + private int bm; + private EntityLiving bn; + private int bo; + private float bp; + private int bq; + private float br; + // CraftBukkit start + public int expToDrop; + public int maxAirTicks = 300; + private boolean applyingSprintKnockback; + ArrayList drops = null; + private DamageSource lastDamageSource; + + private int lastSwingTicks = 0; + + // CraftBukkit end + // Spigot start + public void inactiveTick() { + super.inactiveTick(); + ++this.aU; // Above all the floats + } + + // Spigot end + public double knockbackReduction; // Kohi + + public EntityLiving(World world) { + super(world); + this.aD(); + // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor + this.datawatcher.watch(6, (float) this.getAttributeInstance(GenericAttributes.maxHealth).getValue()); + this.k = true; + this.aL = (float) (Math.random() + 1.0D) * 0.01F; + this.setPosition(this.locX, this.locY, this.locZ); + this.aK = (float) Math.random() * 12398.0F; + this.yaw = (float) (Math.random() * 3.1415927410125732D * 2.0D); + this.aO = this.yaw; + this.W = 0.5F; + } + + protected void c() { + this.datawatcher.a(7, Integer.valueOf(0)); + this.datawatcher.a(8, Byte.valueOf((byte) 0)); + this.datawatcher.a(9, Byte.valueOf((byte) 0)); + this.datawatcher.a(6, Float.valueOf(1.0F)); + } + + protected void aD() { + this.getAttributeMap().b(GenericAttributes.maxHealth); + this.getAttributeMap().b(GenericAttributes.c); + this.getAttributeMap().b(GenericAttributes.d); + if (!this.bk()) { + this.getAttributeInstance(GenericAttributes.d).setValue(0.10000000149011612D); + } + } + + protected void a(double d0, boolean flag) { + if (!this.M()) { + this.N(); + } + + if (flag && this.fallDistance > 0.0F) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY - 0.20000000298023224D - (double) this.height); + int k = MathHelper.floor(this.locZ); + Block block = this.world.getType(i, j, k); + + if (block.getMaterial() == Material.AIR) { + int l = this.world.getType(i, j - 1, k).b(); + + if (l == 11 || l == 32 || l == 21) { + block = this.world.getType(i, j - 1, k); + } + } else if (!this.world.isStatic && this.fallDistance > 3.0F) { + // CraftBukkit start - supply player as argument in particles for visibility API to work + if (this instanceof EntityPlayer) { + this.world.a((EntityHuman) this, 2006, i, j, k, MathHelper.f(this.fallDistance - 3.0F)); + ((EntityPlayer) this).playerConnection.sendPacket(new PacketPlayOutWorldEvent(2006, i, j, k, MathHelper.f(this.fallDistance - 3.0F), false)); + } else { + this.world.triggerEffect(2006, i, j, k, MathHelper.f(this.fallDistance - 3.0F)); + } + // CraftBukkit end + } + + block.a(this.world, i, j, k, this, this.fallDistance); + } + + super.a(d0, flag); + } + + public boolean aE() { + return false; + } + + public void C() { + SpigotTimings.timerEntityLiving_C.startTiming(); // Poweruser + this.aC = this.aD; + super.C(); + + this.lastSwingTicks++; + + this.world.methodProfiler.a("livingEntityBaseTick"); + if (this.isAlive() && this.inBlock()) { + this.damageEntity(DamageSource.STUCK, 1.0F); + } + + if (this.isFireproof() || this.world.isStatic) { + this.extinguish(); + } + + boolean flag = this instanceof EntityHuman && ((EntityHuman) this).abilities.isInvulnerable; + + if (this.isAlive() && this.inWater && this.a(Material.WATER)) { + if (!this.aE() && !flag && !this.hasEffect(MobEffectList.WATER_BREATHING.id)) { + this.setAirTicks(this.j(this.getAirTicks())); + if (this.getAirTicks() == -20) { + this.setAirTicks(0); + + for (int i = 0; i < 8; ++i) { + float f = this.random.nextFloat() - this.random.nextFloat(); + float f1 = this.random.nextFloat() - this.random.nextFloat(); + float f2 = this.random.nextFloat() - this.random.nextFloat(); + + this.world.addParticle("bubble", this.locX + (double) f, this.locY + (double) f1, this.locZ + (double) f2, this.motX, this.motY, this.motZ); + } + + this.damageEntity(DamageSource.DROWN, 2.0F); + } + } + + if (!this.world.isStatic && this.am() && this.vehicle instanceof EntityLiving) { + this.mount((Entity) null); + } + } else { + // CraftBukkit start - Only set if needed to work around a DataWatcher inefficiency + if (this.getAirTicks() != 300) { + this.setAirTicks(maxAirTicks); + } + // CraftBukkit end + } + + if (this.isAlive() && 0 < this.fireTicks && this.L()) { + this.extinguish(); + } + + this.aI = this.aJ; + if (this.attackTicks > 0) { + --this.attackTicks; + } + + if (this.hurtTicks > 0) { + --this.hurtTicks; + } + + if (this.noDamageTicks > 0 && !(this instanceof EntityPlayer)) { + --this.noDamageTicks; + } + + if (this.getHealth() <= 0.0F) { + this.aF(); + } + + if (this.lastDamageByPlayerTime > 0) { + --this.lastDamageByPlayerTime; + } else { + this.killer = null; + } + + if (this.bn != null && !this.bn.isAlive()) { + this.bn = null; + } + + if (this.lastDamager != null) { + if (!this.lastDamager.isAlive()) { + this.b((EntityLiving) null); + } else if (this.ticksLived - this.bm > 100) { + this.b((EntityLiving) null); + } + } + + this.aO(); + this.aY = this.aX; + this.aN = this.aM; + this.aP = this.aO; + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + this.world.methodProfiler.b(); + SpigotTimings.timerEntityLiving_C.stopTiming(); // Poweruser + } + + // CraftBukkit start + public int getExpReward() { + int exp = this.getExpValue(this.killer); + + if (!this.world.isStatic && (this.lastDamageByPlayerTime > 0 || this.alwaysGivesExp()) && this.aG() && this.world.getGameRules().getBoolean("doMobLoot")) { + return exp; + } else { + return 0; + } + } + // CraftBukkit end + + public boolean isBaby() { + return false; + } + + protected void aF() { + ++this.deathTicks; + if (this.deathTicks >= 20 && !this.dead) { // CraftBukkit - (this.deathTicks == 20) -> (this.deathTicks >= 20 && !this.dead) + int i; + + // CraftBukkit start - Update getExpReward() above if the removed if() changes! + i = this.expToDrop; + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); + + i -= j; + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j)); + } + this.expToDrop = 0; + // CraftBukkit end + + this.die(); + + for (i = 0; i < 20; ++i) { + double d0 = this.random.nextGaussian() * 0.02D; + double d1 = this.random.nextGaussian() * 0.02D; + double d2 = this.random.nextGaussian() * 0.02D; + + this.world.addParticle("explode", this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, this.locY + (double) (this.random.nextFloat() * this.length), this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, d0, d1, d2); + } + } + } + + protected boolean aG() { + return !this.isBaby(); + } + + protected int j(int i) { + int j = EnchantmentManager.getOxygenEnchantmentLevel(this); + + return j > 0 && this.random.nextInt(j + 1) > 0 ? i : i - 1; + } + + protected int getExpValue(EntityHuman entityhuman) { + return 0; + } + + protected boolean alwaysGivesExp() { + return false; + } + + public Random aI() { + return this.random; + } + + public EntityLiving getLastDamager() { + return this.lastDamager; + } + + public int aK() { + return this.bm; + } + + public void b(EntityLiving entityliving) { + this.lastDamager = entityliving; + this.bm = this.ticksLived; + } + + public EntityLiving aL() { + return this.bn; + } + + public int aM() { + return this.bo; + } + + public void l(Entity entity) { + if (entity instanceof EntityLiving) { + this.bn = (EntityLiving) entity; + } else { + this.bn = null; + } + + this.bo = this.ticksLived; + } + + public int aN() { + return this.aU; + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setFloat("HealF", this.getHealth()); + nbttagcompound.setShort("Health", (short) ((int) Math.ceil((double) this.getHealth()))); + nbttagcompound.setShort("HurtTime", (short) this.hurtTicks); + nbttagcompound.setShort("DeathTime", (short) this.deathTicks); + nbttagcompound.setShort("AttackTime", (short) this.attackTicks); + nbttagcompound.setFloat("AbsorptionAmount", this.getAbsorptionHearts()); + ItemStack[] aitemstack = this.getEquipment(); + int i = aitemstack.length; + + int j; + ItemStack itemstack; + + for (j = 0; j < i; ++j) { + itemstack = aitemstack[j]; + if (itemstack != null) { + this.d.a(itemstack.D()); + } + } + + nbttagcompound.set("Attributes", GenericAttributes.a(this.getAttributeMap())); + aitemstack = this.getEquipment(); + i = aitemstack.length; + + for (j = 0; j < i; ++j) { + itemstack = aitemstack[j]; + if (itemstack != null) { + this.d.b(itemstack.D()); + } + } + + if (!this.effects.isEmpty()) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.effects.values().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + nbttaglist.add(mobeffect.a(new NBTTagCompound())); + } + + nbttagcompound.set("ActiveEffects", nbttaglist); + } + } + + public void a(NBTTagCompound nbttagcompound) { + this.setAbsorptionHearts(nbttagcompound.getFloat("AbsorptionAmount")); + if (nbttagcompound.hasKeyOfType("Attributes", 9) && this.world != null && !this.world.isStatic) { + GenericAttributes.a(this.getAttributeMap(), nbttagcompound.getList("Attributes", 10)); + } + + if (nbttagcompound.hasKeyOfType("ActiveEffects", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("ActiveEffects", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + MobEffect mobeffect = MobEffect.b(nbttagcompound1); + + if (mobeffect != null) { + this.effects.put(Integer.valueOf(mobeffect.getEffectId()), mobeffect); + } + } + } + + // CraftBukkit start + if (nbttagcompound.hasKey("Bukkit.MaxHealth")) { + NBTBase nbtbase = nbttagcompound.get("Bukkit.MaxHealth"); + if (nbtbase.getTypeId() == 5) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) ((NBTTagFloat) nbtbase).c()); + } else if (nbtbase.getTypeId() == 3) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) ((NBTTagInt) nbtbase).d()); + } + } + // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("HealF", 99)) { + this.setHealth(nbttagcompound.getFloat("HealF")); + } else { + NBTBase nbtbase = nbttagcompound.get("Health"); + + if (nbtbase == null) { + this.setHealth(this.getMaxHealth()); + } else if (nbtbase.getTypeId() == 5) { + this.setHealth(((NBTTagFloat) nbtbase).h()); + } else if (nbtbase.getTypeId() == 2) { + this.setHealth((float) ((NBTTagShort) nbtbase).e()); + } + } + + this.hurtTicks = nbttagcompound.getShort("HurtTime"); + this.deathTicks = nbttagcompound.getShort("DeathTime"); + this.attackTicks = nbttagcompound.getShort("AttackTime"); + } + + protected void aO() { + Iterator> iterator = Sets.newHashSet(this.effects.entrySet()).iterator(); + while (iterator.hasNext()) { + Map.Entry next = iterator.next(); + MobEffect mobeffect = next.getValue(); + + if (!mobeffect.tick(this)) { + if (!this.world.isStatic) { + PotionEffectExpireEvent event = new PotionEffectExpireEvent((LivingEntity) this.getBukkitEntity(), + CraftPotionUtils.toBukkit(mobeffect)); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + CraftPotionUtils.extendDuration(mobeffect, event.getDuration()); + continue; + } + + this.effects.remove(next.getKey()); + this.b(mobeffect); + } + } else if (mobeffect.getDuration() % 600 == 0) { + this.a(mobeffect, false); + } + } + int i; + + if (this.updateEffects) { + if (!this.world.isStatic) { + if (this.effects.isEmpty()) { + this.datawatcher.watch(8, Byte.valueOf((byte) 0)); + this.datawatcher.watch(7, Integer.valueOf(0)); + this.setInvisible(false); + } else { + i = PotionBrewer.a(this.effects.values()); + this.datawatcher.watch(8, Byte.valueOf((byte) (PotionBrewer.b(this.effects.values()) ? 1 : 0))); + this.datawatcher.watch(7, Integer.valueOf(i)); + this.setInvisible(this.hasEffect(MobEffectList.INVISIBILITY.id)); + } + } + + this.updateEffects = false; + } + + i = this.datawatcher.getInt(7); + boolean flag = this.datawatcher.getByte(8) > 0; + + if (i > 0) { + boolean flag1 = false; + + if (!this.isInvisible()) { + flag1 = this.random.nextBoolean(); + } else { + flag1 = this.random.nextInt(15) == 0; + } + + if (flag && !flag1) { + flag1 &= this.random.nextInt(5) == 0; + } + + if (flag1) { + double d0 = (double) (i >> 16 & 255) / 255.0D; + double d1 = (double) (i >> 8 & 255) / 255.0D; + double d2 = (double) (i >> 0 & 255) / 255.0D; + + this.world.addParticle(flag ? "mobSpellAmbient" : "mobSpell", this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length - (double) this.height, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, d0, d1, d2); + } + } + } + + public void removeAllEffects() { + Set> set = this.effects.entrySet(); + Iterator> iterator = new HashSet<>(set).iterator(); + while (iterator.hasNext()) { + Map.Entry next = iterator.next(); + int integer = next.getKey(); + + if (!this.world.isStatic) { + this.removeEffect(integer); + } + } + } + + public Collection getEffects() { + return this.effects.values(); + } + + public boolean hasEffect(int i) { + // CraftBukkit - Add size check for efficiency + return this.effects.size() != 0 && this.effects.containsKey(Integer.valueOf(i)); + } + + public boolean hasEffect(MobEffectList mobeffectlist) { + // CraftBukkit - Add size check for efficiency + return this.effects.size() != 0 && this.effects.containsKey(Integer.valueOf(mobeffectlist.id)); + } + + public MobEffect getEffect(MobEffectList mobeffectlist) { + return (MobEffect) this.effects.get(Integer.valueOf(mobeffectlist.id)); + } + + public void addEffect(MobEffect mobeffect) { + this.addEffect(mobeffect, PotionEffectAddEvent.EffectCause.UNKNOWN); + } + + public void addEffect(MobEffect mobeffect, PotionEffectAddEvent.EffectCause effectCause) { + if (this.d(mobeffect)) { + if (this.effects.containsKey(mobeffect.getEffectId())) { + MobEffect current = this.effects.get(mobeffect.getEffectId()); + + PotionEffectExtendEvent event = new PotionEffectExtendEvent((LivingEntity) this.getBukkitEntity(), + CraftPotionUtils.toBukkit(mobeffect), + CraftPotionUtils.toBukkit(current), effectCause); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) return; + + current.a(mobeffect); + this.a(current, true); + } else { + PotionEffectAddEvent event = new PotionEffectAddEvent((LivingEntity) this.getBukkitEntity(), + CraftPotionUtils.toBukkit(mobeffect), effectCause); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) return; + this.effects.put(mobeffect.getEffectId(), mobeffect); + this.a(mobeffect); + } + } + + } + + public boolean d(MobEffect mobeffect) { + if (this.getMonsterType() == EnumMonsterType.UNDEAD) { + int i = mobeffect.getEffectId(); + + if (i == MobEffectList.REGENERATION.id || i == MobEffectList.POISON.id) { + return false; + } + } + + return true; + } + + public boolean aR() { + return this.getMonsterType() == EnumMonsterType.UNDEAD; + } + + public void removeEffect(int i) { + MobEffect mobeffect = this.effects.remove(i); + + if (mobeffect != null) { + PotionEffectRemoveEvent event = new PotionEffectRemoveEvent((LivingEntity) this.getBukkitEntity(), + CraftPotionUtils.toBukkit(mobeffect)); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.effects.put(i, mobeffect); + return; + } + + this.b(mobeffect); + } + + return; + } + + protected void a(MobEffect mobeffect) { + this.updateEffects = true; + if (!this.world.isStatic) { + MobEffectList.byId[mobeffect.getEffectId()].b(this, this.getAttributeMap(), mobeffect.getAmplifier()); + } + } + + protected void a(MobEffect mobeffect, boolean flag) { + this.updateEffects = true; + if (flag && !this.world.isStatic) { + MobEffectList.byId[mobeffect.getEffectId()].a(this, this.getAttributeMap(), mobeffect.getAmplifier()); + MobEffectList.byId[mobeffect.getEffectId()].b(this, this.getAttributeMap(), mobeffect.getAmplifier()); + } + } + + protected void b(MobEffect mobeffect) { + this.updateEffects = true; + if (!this.world.isStatic) { + MobEffectList.byId[mobeffect.getEffectId()].a(this, this.getAttributeMap(), mobeffect.getAmplifier()); + } + } + + // CraftBukkit start - Delegate so we can handle providing a reason for health being regained + public void heal(float f) { + heal(f, EntityRegainHealthEvent.RegainReason.CUSTOM); + } + + public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason) { + float f1 = this.getHealth(); + + if (f1 > 0.0F) { + EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), f, regainReason); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setHealth((float) (this.getHealth() + event.getAmount())); + } + } + } + + public final float getHealth() { + // CraftBukkit start - Use unscaled health + if (this instanceof EntityPlayer) { + return (float) ((EntityPlayer) this).getBukkitEntity().getHealth(); + } + // CraftBukkit end + return this.datawatcher.getFloat(6); + } + + public void setHealth(float f) { + // CraftBukkit start - Handle scaled health + if (this instanceof EntityPlayer) { + org.bukkit.craftbukkit.entity.CraftPlayer player = ((EntityPlayer) this).getBukkitEntity(); + // Squeeze + if (f < 0.0F) { + player.setRealHealth(0.0D); + } else if (f > player.getMaxHealth()) { + player.setRealHealth(player.getMaxHealth()); + } else { + player.setRealHealth(f); + } + + // Griffin start - Instant respawn + // only send the update to anyone if the player has not died. + // if they do die, we handle all our stuff in EntityPlayer#die(DamageSource) + if (player.getHealth() != 0 || !SpigotConfig.instantRespawn) { + this.datawatcher.watch(6, Float.valueOf(player.getScaledHealth())); + } + // Griffin end - Instant respawn + return; + } + // CraftBukkit end + this.datawatcher.watch(6, Float.valueOf(MathHelper.a(f, 0.0F, this.getMaxHealth()))); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + // Guardian start + if (damagesource.getEntity() instanceof EntityPlayer) { + Bukkit.getPluginManager().callEvent(new PlayerAttackEvent(((EntityPlayer) damagesource.getEntity()).getBukkitEntity(), getBukkitEntity())); + } + // Guardian end + + if (this.isInvulnerable()) { + return false; + } else if (this.world.isStatic) { + return false; + } else { + this.aU = 0; + if (this.getHealth() <= 0.0F) { + return false; + } else if (damagesource.o() && this.hasEffect(MobEffectList.FIRE_RESISTANCE)) { + return false; + } else { + // CraftBukkit - Moved into d(DamageSource, float) + if (false && (damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && this.getEquipment(4) != null) { + this.getEquipment(4).damage((int) (f * 4.0F + this.random.nextFloat() * f * 2.0F), this); + f *= 0.75F; + } + + this.aF = 1.5F; + boolean flag = true; + int maxNoDamageTicks = this.maxNoDamageTicks; + + boolean shouldIgnoreArrow = damagesource instanceof EntityDamageSourceIndirect + && ((EntityDamageSourceIndirect) damagesource).getProximateDamageSource() instanceof EntityArrow; +// boolean isRodTick = this.lastDamageSource == null && damagesource instanceof EntityDamageSourceIndirect && ((EntityDamageSourceIndirect)damagesource).getProximateDamageSource() instanceof EntityFishingHook +// || this.lastDamageSource != null && this.lastDamageSource instanceof EntityDamageSourceIndirect && !(((EntityDamageSourceIndirect)this.lastDamageSource).getProximateDamageSource() instanceof EntityFishingHook) +// && damagesource instanceof EntityDamageSourceIndirect && ((EntityDamageSourceIndirect) damagesource).getProximateDamageSource() instanceof EntityFishingHook; + + if (((!(shouldIgnoreArrow))) && this.noDamageTicks > maxNoDamageTicks / 2) { + if (f <= this.lastDamage) { + return false; + } + + if (!this.d(damagesource, f - this.lastDamage)) { + return false; + } + + this.lastDamage = f; + flag = false; + } else { + float previousHealth = this.getHealth(); + if (!this.d(damagesource, f)) { + return false; + } + + this.lastDamage = f; + this.aw = previousHealth; + + this.noDamageTicks = this.maxNoDamageTicks; + + this.hurtTicks = this.ay = 10; + this.activatedTick = MinecraftServer.currentTick + this.maxNoDamageTicks * 2L; // Kohi + } + + // Guardian start + if ((damagesource.getEntity() instanceof EntityPlayer)) { + EntityPlayer player = (EntityPlayer) damagesource.getEntity(); + + long now = System.currentTimeMillis(); + if ((this instanceof EntityPlayer)) { + player.playerConnection.lastAttackPlayerTime = now; + } + } + // Guardian end + + this.az = 0.0F; + Entity entity = damagesource.getEntity(); + + if (entity != null) { + if (entity instanceof EntityLiving) { + this.b((EntityLiving) entity); + } + + if (entity instanceof EntityHuman) { + this.lastDamageByPlayerTime = 100; + this.killer = (EntityHuman) entity; + } else if (entity instanceof EntityWolf) { + EntityWolf entitywolf = (EntityWolf) entity; + + if (entitywolf.isTamed()) { + this.lastDamageByPlayerTime = 100; + this.killer = null; + } + } + } + + if (flag) { + this.world.broadcastEntityEffect(this, (byte) 2); + if (damagesource != DamageSource.DROWN) { + this.Q(); + } + + if (entity != null) { + double d0 = entity.locX - this.locX; + + double d1; + + for (d1 = entity.locZ - this.locZ; d0 * d0 + d1 * d1 < 1.0E-4D; d1 = (Math.random() - Math.random()) * 0.01D) { + d0 = (Math.random() - Math.random()) * 0.01D; + } + + this.az = (float) (Math.atan2(d1, d0) * 180.0D / 3.1415927410125732D) - this.yaw; + this.a(damagesource, f, d0, d1); + } else { + this.az = (float) ((int) (Math.random() * 2.0D) * 180); + } + } + + String s; + + if (this.getHealth() <= 0.0F) { + s = this.aU(); + if (flag && s != null) { + this.makeSound(s, this.bf(), this.bg()); + } + + this.die(damagesource); + } else { + s = this.aT(); + if (flag && s != null) { + this.makeSound(s, this.bf(), this.bg()); + } + } + this.lastDamageSource = damagesource; + return true; + } + + } + } + + public void a(ItemStack itemstack) { + this.makeSound("random.break", 0.8F, 0.8F + this.world.random.nextFloat() * 0.4F); + + for (int i = 0; i < 5; ++i) { + Vec3D vec3d = Vec3D.a(((double) this.random.nextFloat() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, 0.0D); + + vec3d.a(-this.pitch * 3.1415927F / 180.0F); + vec3d.b(-this.yaw * 3.1415927F / 180.0F); + Vec3D vec3d1 = Vec3D.a(((double) this.random.nextFloat() - 0.5D) * 0.3D, (double) (-this.random.nextFloat()) * 0.6D - 0.3D, 0.6D); + + vec3d1.a(-this.pitch * 3.1415927F / 180.0F); + vec3d1.b(-this.yaw * 3.1415927F / 180.0F); + vec3d1 = vec3d1.add(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ); + this.world.addParticle("iconcrack_" + Item.getId(itemstack.getItem()), vec3d1.a, vec3d1.b, vec3d1.c, vec3d.a, vec3d.b + 0.05D, vec3d.c); + } + } + + public void die(DamageSource damagesource) { + Entity entity = damagesource.getEntity(); + EntityLiving entityliving = this.aX(); + + if (this.ba >= 0 && entityliving != null) { + entityliving.b(this, this.ba); + } + + if (entity != null) { + entity.a(this); + } + + this.aT = true; + this.aW().g(); + if (!this.world.isStatic) { + int i = 0; + + if (entity instanceof EntityHuman) { + i = EnchantmentManager.getBonusMonsterLootEnchantmentLevel((EntityLiving) entity); + } + + if (this.aG() && this.world.getGameRules().getBoolean("doMobLoot")) { + this.drops = new ArrayList(); // CraftBukkit - Setup drop capture + + this.dropDeathLoot(this.lastDamageByPlayerTime > 0, i); + this.dropEquipment(this.lastDamageByPlayerTime > 0, i); + if (this.lastDamageByPlayerTime > 0) { + int j = this.random.nextInt(200) - i; + + if (j < 5) { + this.getRareDrop(j <= 0 ? 1 : 0); + } + } + + int exp = this.getExpReward() * (1 + this.random.nextInt(1 + i)); // Kohi - Caculate xp here and not in the event factory + + // CraftBukkit start - Call death event + CraftEventFactory.callEntityDeathEvent(this, this.drops, exp); // Kohi - Specify the exp to drop + this.drops = null; + } else { + CraftEventFactory.callEntityDeathEvent(this); + // CraftBukkit end + } + } + + this.world.broadcastEntityEffect(this, (byte) 3); + } + + protected void dropEquipment(boolean flag, int i) { + } + + public void a(DamageSource damageSource, float f, double d0, double d1) { + if (this.random.nextDouble() >= this.getAttributeInstance(GenericAttributes.c).getValue()) { + this.al = true; + float magnitude = MathHelper.sqrt(d0 * d0 + d1 * d1); + Knockback profile = getKnockback(); + + this.motX /= profile.getFriction(); + this.motY /= profile.getFriction(); + this.motZ /= profile.getFriction(); + + this.motX -= d0 / magnitude * profile.getHorizontal(); + this.motY += profile.getVertical(); + this.motZ -= d1 / magnitude * profile.getHorizontal(); + + if (this.motY > profile.getVerticalLimit()) { + this.motY = profile.getVerticalLimit(); + } + } + } + + protected String aT() { + return "game.neutral.hurt"; + } + + protected String aU() { + return "game.neutral.die"; + } + + protected void getRareDrop(int i) { + } + + protected void dropDeathLoot(boolean flag, int i) { + } + + public boolean h_() { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.boundingBox.b); + int k = MathHelper.floor(this.locZ); + Block block = this.world.getType(i, j, k); + + return block == Blocks.LADDER || block == Blocks.VINE; + } + + public boolean isAlive() { + return !this.dead && this.getHealth() > 0.0F; + } + + protected void b(float f) { + super.b(f); + MobEffect mobeffect = this.getEffect(MobEffectList.JUMP); + float f1 = mobeffect != null ? (float) (mobeffect.getAmplifier() + 1) : 0.0F; + int i = MathHelper.f(f - 3.0F - f1); + + if (i > 0) { + i *= 1.5; + // CraftBukkit start + if (!this.damageEntity(DamageSource.FALL, (float) i)) { + return; + } + // CraftBukkit end + this.makeSound(this.o(i), 1.0F, 1.0F); + // this.damageEntity(DamageSource.FALL, (float) i); // CraftBukkit - moved up + int j = MathHelper.floor(this.locX); + int k = MathHelper.floor(this.locY - 0.20000000298023224D - (double) this.height); + int l = MathHelper.floor(this.locZ); + Block block = this.world.getType(j, k, l); + + if (block.getMaterial() != Material.AIR) { + StepSound stepsound = block.stepSound; + + this.makeSound(stepsound.getStepSound(), stepsound.getVolume1() * 0.5F, stepsound.getVolume2() * 0.75F); + } + } + } + + protected String o(int i) { + return i > 4 ? "game.neutral.hurt.fall.big" : "game.neutral.hurt.fall.small"; + } + + public int aV() { + int i = 0; + ItemStack[] aitemstack = this.getEquipment(); + int j = aitemstack.length; + + for (int k = 0; k < j; ++k) { + ItemStack itemstack = aitemstack[k]; + + if (itemstack != null && itemstack.getItem() instanceof ItemArmor) { + int l = ((ItemArmor) itemstack.getItem()).c; + + i += l; + } + } + + return i; + } + + protected void damageArmor(float f) { + } + + protected float applyArmorModifier(DamageSource damagesource, float f) { + if (!damagesource.ignoresArmor()) { + int i = 25 - this.aV(); + float f1 = f * (float) i; + + // this.damageArmor(f); // CraftBukkit - Moved into d(DamageSource, float) + f = f1 / 25.0F; + } + + return f; + } + + protected float applyMagicModifier(DamageSource damagesource, float f) { + if (damagesource.isStarvation()) { + return f; + } else { + if (this instanceof EntityZombie) { + f = f; + } + + int i; + int j; + float f1; + + // CraftBukkit - Moved to d(DamageSource, float) + if (false && this.hasEffect(MobEffectList.RESISTANCE) && damagesource != DamageSource.OUT_OF_WORLD) { + i = (this.getEffect(MobEffectList.RESISTANCE).getAmplifier() + 1) * 5; + j = 25 - i; + f1 = f * (float) j; + f = f1 / 25.0F; + } + + if (f <= 0.0F) { + return 0.0F; + } else { + i = EnchantmentManager.a(this.getEquipment(), damagesource); + if (i > 20) { + i = 20; + } + + if (i > 0 && i <= 20) { + j = 25 - i; + f1 = f * (float) j; + f = f1 / 25.0F; + } + + return f; + } + } + } + + // CraftBukkit start + protected boolean d(final DamageSource damagesource, float f) { // void -> boolean, add final + if (!this.isInvulnerable()) { + final boolean human = this instanceof EntityHuman; + float originalDamage = f; + Function hardHat = new Function() { + @Override + public Double apply(Double f) { + if ((damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && EntityLiving.this.getEquipment(4) != null) { + return -(f - (f * 0.75F)); + } + return -0.0; + } + }; + float hardHatModifier = hardHat.apply((double) f).floatValue(); + f += hardHatModifier; + + Function blocking = new Function() { + @Override + public Double apply(Double f) { + if (human) { + if (!damagesource.ignoresArmor() && ((EntityHuman) EntityLiving.this).isBlocking() && f > 0.0F) { + return -(f - ((1.0F + f) * 0.5F)); + } + } + return -0.0; + } + }; + float blockingModifier = blocking.apply((double) f).floatValue(); + f += blockingModifier; + + Function armor = new Function() { + @Override + public Double apply(Double f) { + return -(f - EntityLiving.this.applyArmorModifier(damagesource, f.floatValue())); + } + }; + float armorModifier = armor.apply((double) f).floatValue(); + f += armorModifier; + + Function resistance = new Function() { + @Override + public Double apply(Double f) { + if (!damagesource.isStarvation() && EntityLiving.this.hasEffect(MobEffectList.RESISTANCE) && damagesource != DamageSource.OUT_OF_WORLD) { + int i = (EntityLiving.this.getEffect(MobEffectList.RESISTANCE).getAmplifier() + 1) * 5; + int j = 25 - i; + float f1 = f.floatValue() * (float) j; + return -(f - (f1 / 25.0F)); + } + return -0.0; + } + }; + float resistanceModifier = resistance.apply((double) f).floatValue(); + f += resistanceModifier; + + Function magic = new Function() { + @Override + public Double apply(Double f) { + return -(f - EntityLiving.this.applyMagicModifier(damagesource, f.floatValue())); + } + }; + float magicModifier = magic.apply((double) f).floatValue(); + f += magicModifier; + + Function absorption = new Function() { + @Override + public Double apply(Double f) { + return -(Math.max(f - Math.max(f - EntityLiving.this.getAbsorptionHearts(), 0.0F), 0.0F)); + } + }; + float absorptionModifier = absorption.apply((double) f).floatValue(); + + EntityDamageEvent event = CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, hardHat, blocking, armor, resistance, magic, absorption); + if (event.isCancelled()) { + return false; + } + + f = (float) event.getFinalDamage(); + + // Apply damage to helmet + if ((damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && this.getEquipment(4) != null) { + this.getEquipment(4).damage((int) (event.getDamage() * 4.0F + this.random.nextFloat() * event.getDamage() * 2.0F), this); + } + + // Apply damage to armor + if (!damagesource.ignoresArmor()) { + float armorDamage = (float) (event.getDamage() + event.getDamage(DamageModifier.BLOCKING) + event.getDamage(DamageModifier.HARD_HAT)); + this.damageArmor(armorDamage); + } + + absorptionModifier = (float) -event.getDamage(DamageModifier.ABSORPTION); + this.setAbsorptionHearts(Math.max(this.getAbsorptionHearts() - absorptionModifier, 0.0F)); + if (f != 0.0F) { + if (human) { + ((EntityHuman) this).applyExhaustion(damagesource.getExhaustionCost()); + } + // CraftBukkit end + float f2 = this.getHealth(); + + this.setHealth(f2 - f); + this.aW().a(damagesource, f2, f); + // CraftBukkit start + if (human) { + return true; + } + // CraftBukkit end + this.setAbsorptionHearts(this.getAbsorptionHearts() - f); + } + return true; // CraftBukkit + } + return false; // CraftBukkit + } + + public CombatTracker aW() { + return this.combatTracker; + } + + public EntityLiving aX() { + return (EntityLiving) (this.combatTracker.c() != null ? this.combatTracker.c() : (this.killer != null ? this.killer : (this.lastDamager != null ? this.lastDamager : null))); + } + + public final float getMaxHealth() { + return (float) this.getAttributeInstance(GenericAttributes.maxHealth).getValue(); + } + + public final int aZ() { + return this.datawatcher.getByte(9); + } + + public final void p(int i) { + this.datawatcher.watch(9, Byte.valueOf((byte) i)); + } + + private int j() { + return this.hasEffect(MobEffectList.FASTER_DIG) ? 6 - (1 + this.getEffect(MobEffectList.FASTER_DIG).getAmplifier()) * 1 : (this.hasEffect(MobEffectList.SLOWER_DIG) ? 6 + (1 + this.getEffect(MobEffectList.SLOWER_DIG).getAmplifier()) * 2 : 6); + } + + public void ba() { + if (!this.at || this.au >= this.j() / 2 || this.au < 0) { + this.au = -1; + this.at = true; + + if (this.lastSwingTicks < 5) { + return; + } + + if (this.world instanceof WorldServer) { + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutAnimation(this, 0))); + this.lastSwingTicks = 0; + } + } + } + + protected void G() { + this.damageEntity(DamageSource.OUT_OF_WORLD, 4.0F); + } + + protected void bb() { + int i = this.j(); + + if (this.at) { + ++this.au; + if (this.au >= i) { + this.au = 0; + this.at = false; + } + } else { + this.au = 0; + } + + this.aD = (float) this.au / (float) i; + } + + public AttributeInstance getAttributeInstance(IAttribute iattribute) { + return this.getAttributeMap().a(iattribute); + } + + public AttributeMapBase getAttributeMap() { + if (this.d == null) { + this.d = new AttributeMapServer(); + } + + return this.d; + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEFINED; + } + + public abstract ItemStack be(); + + public abstract ItemStack getEquipment(int i); + + public abstract void setEquipment(int i, ItemStack itemstack); + + public void setSprinting(boolean flag) { + super.setSprinting(flag); + this.setApplyingSprintKnockback(flag); + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.d); + + if (attributeinstance.a(b) != null) { + attributeinstance.b(c); + } + + if (flag) { + attributeinstance.a(c); + } + } + + public void setApplyingSprintKnockback(final boolean flag) { + this.applyingSprintKnockback = flag; + } + + public boolean isApplyingSprintKnockback() { + return this.applyingSprintKnockback; + } + + + public abstract ItemStack[] getEquipment(); + + protected float bf() { + return 1.0F; + } + + protected float bg() { + return this.isBaby() ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.5F : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F; + } + + protected boolean bh() { + return this.getHealth() <= 0.0F; + } + + public void enderTeleportTo(double d0, double d1, double d2) { + this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch); + } + + public void m(Entity entity) { + double d0 = entity.locX; + double d1 = entity.boundingBox.b + (double) entity.length; + double d2 = entity.locZ; + byte b0 = 1; + + for (int i = -b0; i <= b0; ++i) { + for (int j = -b0; j < b0; ++j) { + if (i != 0 || j != 0) { + int k = (int) (this.locX + (double) i); + int l = (int) (this.locZ + (double) j); + AxisAlignedBB axisalignedbb = this.boundingBox.c((double) i, 1.0D, (double) j); + + if (this.world.a(axisalignedbb).isEmpty()) { + if (World.a((IBlockAccess) this.world, k, (int) this.locY, l)) { + this.enderTeleportTo(this.locX + (double) i, this.locY + 1.0D, this.locZ + (double) j); + return; + } + + if (World.a((IBlockAccess) this.world, k, (int) this.locY - 1, l) || this.world.getType(k, (int) this.locY - 1, l).getMaterial() == Material.WATER) { + d0 = this.locX + (double) i; + d1 = this.locY + 1.0D; + d2 = this.locZ + (double) j; + } + } + } + } + } + + this.enderTeleportTo(d0, d1, d2); + } + + protected void bj() { + this.motY = 0.41999998688697815D; + if (this.hasEffect(MobEffectList.JUMP)) { + this.motY += (double) ((float) (this.getEffect(MobEffectList.JUMP).getAmplifier() + 1) * 0.1F); + } + + if (this.isSprinting()) { + float f = this.yaw * 0.017453292F; + + this.motX -= (double) (MathHelper.sin(f) * 0.2F); + this.motZ += (double) (MathHelper.cos(f) * 0.2F); + } + + this.al = true; + } + + public void e(float f, float f1) { + double d0; + + if (this.M() && (!(this instanceof EntityHuman) || !((EntityHuman) this).abilities.isFlying)) { + d0 = this.locY; + this.a(f, f1, this.bk() ? 0.04F : 0.02F); + this.move(this.motX, this.motY, this.motZ); + this.motX *= 0.800000011920929D; + this.motY *= 0.800000011920929D; + this.motZ *= 0.800000011920929D; + this.motY -= 0.02D; + if (this.positionChanged && this.c(this.motX, this.motY + 0.6000000238418579D - this.locY + d0, this.motZ)) { + this.motY = 0.30000001192092896D; + } + } else if (this.P() && (!(this instanceof EntityHuman) || !((EntityHuman) this).abilities.isFlying)) { + d0 = this.locY; + this.a(f, f1, 0.02F); + this.move(this.motX, this.motY, this.motZ); + this.motX *= 0.5D; + this.motY *= 0.5D; + this.motZ *= 0.5D; + this.motY -= 0.02D; + if (this.positionChanged && this.c(this.motX, this.motY + 0.6000000238418579D - this.locY + d0, this.motZ)) { + this.motY = 0.30000001192092896D; + } + } else { + float f2 = 0.91F; + + if (this.onGround) { + f2 = this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.boundingBox.b) - 1, MathHelper.floor(this.locZ)).frictionFactor * 0.91F; + } + + float f3 = 0.16277136F / (f2 * f2 * f2); + float f4; + + if (this.onGround) { + f4 = this.bl() * f3; + } else { + f4 = this.aQ; + } + + this.a(f, f1, f4); + f2 = 0.91F; + if (this.onGround) { + f2 = this.world.getType(MathHelper.floor(this.locX), MathHelper.floor(this.boundingBox.b) - 1, MathHelper.floor(this.locZ)).frictionFactor * 0.91F; + } + + if (this.h_()) { + float f5 = 0.15F; + + if (this.motX < (double) (-f5)) { + this.motX = (double) (-f5); + } + + if (this.motX > (double) f5) { + this.motX = (double) f5; + } + + if (this.motZ < (double) (-f5)) { + this.motZ = (double) (-f5); + } + + if (this.motZ > (double) f5) { + this.motZ = (double) f5; + } + + this.fallDistance = 0.0F; + if (this.motY < -0.15D) { + this.motY = -0.15D; + } + + boolean flag = this.isSneaking() && this instanceof EntityHuman; + + if (flag && this.motY < 0.0D) { + this.motY = 0.0D; + } + } + + this.move(this.motX, this.motY, this.motZ); + if (this.positionChanged && this.h_()) { + this.motY = 0.2D; + } + + if (this.world.isStatic && (!this.world.isLoaded((int) this.locX, 0, (int) this.locZ) || !this.world.getChunkAtWorldCoords((int) this.locX, (int) this.locZ).d)) { + if (this.locY > 0.0D) { + this.motY = -0.1D; + } else { + this.motY = 0.0D; + } + } else { + this.motY -= 0.08D; + } + + this.motY *= 0.9800000190734863D; + this.motX *= (double) f2; + this.motZ *= (double) f2; + } + + this.aE = this.aF; + d0 = this.locX - this.lastX; + double d1 = this.locZ - this.lastZ; + float f6 = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F; + + if (f6 > 1.0F) { + f6 = 1.0F; + } + + this.aF += (f6 - this.aF) * 0.4F; + this.aG += this.aF; + } + + protected boolean bk() { + return false; + } + + public float bl() { + return this.bk() ? this.bp : 0.1F; + } + + public void i(float f) { + this.bp = f; + } + + public boolean n(Entity entity) { + this.l(entity); + return false; + } + + public boolean isSleeping() { + return false; + } + + public void h() { + SpigotTimings.timerEntityBaseTick.startTiming(); // Spigot + super.h(); + if (!this.world.isStatic) { + int i = this.aZ(); + + if (i > 0) { + if (this.av <= 0) { + this.av = 20 * (30 - i); + } + + --this.av; + if (this.av <= 0) { + this.p(i - 1); + } + } + + for (int j = 0; j < 5; ++j) { + ItemStack itemstack = this.g[j]; + ItemStack itemstack1 = this.getEquipment(j); + + if (!ItemStack.matches(itemstack1, itemstack)) { + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutEntityEquipment(this.getId(), j, itemstack1))); + if (itemstack != null) { + this.d.a(itemstack.D()); + } + + if (itemstack1 != null) { + this.d.b(itemstack1.D()); + } + + this.g[j] = itemstack1 == null ? null : itemstack1.cloneItemStack(); + } + } + + if (this.ticksLived % 20 == 0) { + this.aW().g(); + } + } + + SpigotTimings.timerEntityBaseTick.stopTiming(); // Spigot + this.e(); + SpigotTimings.timerEntityTickRest.startTiming(); // Spigot + double d0 = this.locX - this.lastX; + double d1 = this.locZ - this.lastZ; + float f = (float) (d0 * d0 + d1 * d1); + float f1 = this.aM; + float f2 = 0.0F; + + this.aV = this.aW; + float f3 = 0.0F; + + if (f > 0.0025000002F) { + f3 = 1.0F; + f2 = (float) Math.sqrt((double) f) * 3.0F; + // CraftBukkit - Math -> TrigMath + f1 = (float) org.bukkit.craftbukkit.TrigMath.atan2(d1, d0) * 180.0F / 3.1415927F - 90.0F; + } + + if (this.aD > 0.0F) { + f1 = this.yaw; + } + + if (!this.onGround) { + f3 = 0.0F; + } + + this.aW += (f3 - this.aW) * 0.3F; + this.world.methodProfiler.a("headTurn"); + f2 = this.f(f1, f2); + this.world.methodProfiler.b(); + this.world.methodProfiler.a("rangeChecks"); + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + while (this.aM - this.aN < -180.0F) { + this.aN -= 360.0F; + } + + while (this.aM - this.aN >= 180.0F) { + this.aN += 360.0F; + } + + while (this.pitch - this.lastPitch < -180.0F) { + this.lastPitch -= 360.0F; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.aO - this.aP < -180.0F) { + this.aP -= 360.0F; + } + + while (this.aO - this.aP >= 180.0F) { + this.aP += 360.0F; + } + + this.world.methodProfiler.b(); + this.aX += f2; + SpigotTimings.timerEntityTickRest.stopTiming(); // Spigot + } + + protected float f(float f, float f1) { + float f2 = MathHelper.g(f - this.aM); + + this.aM += f2 * 0.3F; + float f3 = MathHelper.g(this.yaw - this.aM); + boolean flag = f3 < -90.0F || f3 >= 90.0F; + + if (f3 < -75.0F) { + f3 = -75.0F; + } + + if (f3 >= 75.0F) { + f3 = 75.0F; + } + + this.aM = this.yaw - f3; + if (f3 * f3 > 2500.0F) { + this.aM += f3 * 0.2F; + } + + if (flag) { + f1 *= -1.0F; + } + + return f1; + } + + public void e() { + if (this.bq > 0) { + --this.bq; + } + + if (this.bg > 0) { + double d0 = this.locX + (this.bh - this.locX) / (double) this.bg; + double d1 = this.locY + (this.bi - this.locY) / (double) this.bg; + double d2 = this.locZ + (this.bj - this.locZ) / (double) this.bg; + double d3 = MathHelper.g(this.bk - (double) this.yaw); + + this.yaw = (float) ((double) this.yaw + d3 / (double) this.bg); + this.pitch = (float) ((double) this.pitch + (this.bl - (double) this.pitch) / (double) this.bg); + --this.bg; + this.setPosition(d0, d1, d2); + this.b(this.yaw, this.pitch); + } else if (!this.br()) { + this.motX *= 0.98D; + this.motY *= 0.98D; + this.motZ *= 0.98D; + } + + if (Math.abs(this.motX) < 0.005D) { + this.motX = 0.0D; + } + + if (Math.abs(this.motY) < 0.005D) { + this.motY = 0.0D; + } + + if (Math.abs(this.motZ) < 0.005D) { + this.motZ = 0.0D; + } + + this.world.methodProfiler.a("ai"); + SpigotTimings.timerEntityAI.startTiming(); // Spigot + if (this.bh()) { + this.bc = false; + this.bd = 0.0F; + this.be = 0.0F; + this.bf = 0.0F; + } else if (this.br()) { + if (this.bk()) { + this.world.methodProfiler.a("newAi"); + this.bn(); + this.world.methodProfiler.b(); + } else { + this.world.methodProfiler.a("oldAi"); + this.bq(); + this.world.methodProfiler.b(); + this.aO = this.yaw; + } + } + SpigotTimings.timerEntityAI.stopTiming(); // Spigot + + this.world.methodProfiler.b(); + this.world.methodProfiler.a("jump"); + if (this.bc) { + if (!this.M() && !this.P()) { + if (this.onGround && this.bq == 0) { + this.bj(); + this.bq = 10; + } + } else { + this.motY += 0.03999999910593033D; + } + } else { + this.bq = 0; + } + + this.world.methodProfiler.b(); + this.world.methodProfiler.a("travel"); + this.bd *= 0.98F; + this.be *= 0.98F; + this.bf *= 0.9F; + SpigotTimings.timerEntityAIMove.startTiming(); // Spigot + this.e(this.bd, this.be); + SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot + this.world.methodProfiler.b(); + this.world.methodProfiler.a("push"); + if (!this.world.isStatic) { + SpigotTimings.timerEntityAICollision.startTiming(); // Spigot + this.bo(); + SpigotTimings.timerEntityAICollision.stopTiming(); // Spigot + } + + this.world.methodProfiler.b(); + } + + protected void bn() { + } + + protected void bo() { + // Kohi - skip checks if not activated + if (SpigotConfig.disableEntityCollisions || !ActivationRange.checkIfActive(this)) { // MineHQ + return; + } + + List list = this.world.getEntities(this, this.boundingBox.grow(0.20000000298023224D, 0.0D, 0.20000000298023224D)); + + if (this.R() && list != null && !list.isEmpty()) { // Spigot: Add this.R() condition + numCollisions -= world.spigotConfig.maxCollisionsPerEntity; // Spigot + for (int i = 0; i < list.size(); ++i) { + if (numCollisions > world.spigotConfig.maxCollisionsPerEntity) { + break; + } // Spigot + Entity entity = (Entity) list.get(i); + if (entity instanceof EntityPlayer) continue; // MineHQ - players don't get pushed + + // TODO better check now? + // CraftBukkit start - Only handle mob (non-player) collisions every other tick + if (entity instanceof EntityLiving && !(this instanceof EntityPlayer) && this.ticksLived % 2 == 0) { + continue; + } + // CraftBukkit end + + if (entity.S() && ActivationRange.checkIfActive(entity)) { + entity.numCollisions++; // Spigot + numCollisions++; // Spigot + this.o(entity); + } + } + numCollisions = 0; // Spigot + } + } + + protected void o(Entity entity) { + entity.collide(this); + } + + public void ab() { + super.ab(); + this.aV = this.aW; + this.aW = 0.0F; + this.fallDistance = 0.0F; + } + + protected void bp() { + } + + protected void bq() { + ++this.aU; + } + + public void f(boolean flag) { + this.bc = flag; + } + + public void receive(Entity entity, int i) { + if (!entity.dead && !this.world.isStatic) { + EntityTracker entitytracker = ((WorldServer) this.world).getTracker(); + + if (entity instanceof EntityItem) { + entitytracker.a(entity, (Packet) (new PacketPlayOutCollect(entity.getId(), this.getId()))); + } + + if (entity instanceof EntityArrow) { + entitytracker.a(entity, (Packet) (new PacketPlayOutCollect(entity.getId(), this.getId()))); + } + + if (entity instanceof EntityExperienceOrb) { + entitytracker.a(entity, (Packet) (new PacketPlayOutCollect(entity.getId(), this.getId()))); + } + } + } + + public boolean hasLineOfSight(Entity entity) { + return this.world.a(Vec3D.a(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ), Vec3D.a(entity.locX, entity.locY + (double) entity.getHeadHeight(), entity.locZ)) == null; + } + + public Vec3D ag() { + return this.j(1.0F); + } + + public Vec3D j(float f) { + float f1; + float f2; + float f3; + float f4; + + if (f == 1.0F) { + f1 = MathHelper.cos(-this.yaw * 0.017453292F - 3.1415927F); + f2 = MathHelper.sin(-this.yaw * 0.017453292F - 3.1415927F); + f3 = -MathHelper.cos(-this.pitch * 0.017453292F); + f4 = MathHelper.sin(-this.pitch * 0.017453292F); + return Vec3D.a((double) (f2 * f3), (double) f4, (double) (f1 * f3)); + } else { + f1 = this.lastPitch + (this.pitch - this.lastPitch) * f; + f2 = this.lastYaw + (this.yaw - this.lastYaw) * f; + f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); + f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); + float f5 = -MathHelper.cos(-f1 * 0.017453292F); + float f6 = MathHelper.sin(-f1 * 0.017453292F); + + return Vec3D.a((double) (f4 * f5), (double) f6, (double) (f3 * f5)); + } + } + + public boolean br() { + return !this.world.isStatic; + } + + public boolean R() { + return !this.dead; + } + + public boolean S() { + return !this.dead; + } + + public float getHeadHeight() { + return this.length * 0.85F; + } + + protected void Q() { + this.velocityChanged = this.random.nextDouble() >= this.getAttributeInstance(GenericAttributes.c).getValue(); + } + + public float getHeadRotation() { + return this.aO; + } + + public float getAbsorptionHearts() { + return this.br; + } + + public void setAbsorptionHearts(float f) { + if (f < 0.0F) { + f = 0.0F; + } + + this.br = f; + } + + public ScoreboardTeamBase getScoreboardTeam() { + return null; + } + + public boolean c(EntityLiving entityliving) { + return this.a(entityliving.getScoreboardTeam()); + } + + public boolean a(ScoreboardTeamBase scoreboardteambase) { + return this.getScoreboardTeam() != null ? this.getScoreboardTeam().isAlly(scoreboardteambase) : false; + } + + public void bu() { + } + + public void bv() { + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartAbstract.java new file mode 100644 index 0000000..0710fcc --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartAbstract.java @@ -0,0 +1,913 @@ +package net.minecraft.server; + +import java.util.List; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.vehicle.VehicleDamageEvent; +import org.bukkit.event.vehicle.VehicleDestroyEvent; +import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; +import org.bukkit.util.Vector; +// CraftBukkit end + +public abstract class EntityMinecartAbstract extends Entity { + + private boolean a; + private String b; + private static final int[][][] matrix = new int[][][] { { { 0, 0, -1}, { 0, 0, 1}}, { { -1, 0, 0}, { 1, 0, 0}}, { { -1, -1, 0}, { 1, 0, 0}}, { { -1, 0, 0}, { 1, -1, 0}}, { { 0, 0, -1}, { 0, -1, 1}}, { { 0, -1, -1}, { 0, 0, 1}}, { { 0, 0, 1}, { 1, 0, 0}}, { { 0, 0, 1}, { -1, 0, 0}}, { { 0, 0, -1}, { -1, 0, 0}}, { { 0, 0, -1}, { 1, 0, 0}}}; + private int d; + private double e; + private double f; + private double g; + private double h; + private double i; + + // CraftBukkit start + public boolean slowWhenEmpty = true; + private double derailedX = 0.5; + private double derailedY = 0.5; + private double derailedZ = 0.5; + private double flyingX = 0.95; + private double flyingY = 0.95; + private double flyingZ = 0.95; + public double maxSpeed = 0.4D; + // CraftBukkit end + + public EntityMinecartAbstract(World world) { + super(world); + this.k = true; + this.a(0.98F, 0.7F); + this.height = this.length / 2.0F; + } + + public static EntityMinecartAbstract a(World world, double d0, double d1, double d2, int i) { + switch (i) { + case 1: + return new EntityMinecartChest(world, d0, d1, d2); + + case 2: + return new EntityMinecartFurnace(world, d0, d1, d2); + + case 3: + return new EntityMinecartTNT(world, d0, d1, d2); + + case 4: + return new EntityMinecartMobSpawner(world, d0, d1, d2); + + case 5: + return new EntityMinecartHopper(world, d0, d1, d2); + + case 6: + return new EntityMinecartCommandBlock(world, d0, d1, d2); + + default: + return new EntityMinecartRideable(world, d0, d1, d2); + } + } + + protected boolean g_() { + return false; + } + + protected void c() { + this.datawatcher.a(17, new Integer(0)); + this.datawatcher.a(18, new Integer(1)); + this.datawatcher.a(19, new Float(0.0F)); + this.datawatcher.a(20, new org.spigotmc.ProtocolData.DualInt(0, 0)); // Spigot - protocol patch + this.datawatcher.a(21, new Integer(6)); + this.datawatcher.a(22, Byte.valueOf((byte) 0)); + } + + public AxisAlignedBB h(Entity entity) { + return entity.S() ? entity.boundingBox : null; + } + + public AxisAlignedBB J() { + return null; + } + + public boolean S() { + return true; + } + + public EntityMinecartAbstract(World world, double d0, double d1, double d2) { + this(world); + this.setPosition(d0, d1, d2); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + + this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleCreateEvent((Vehicle) this.getBukkitEntity())); // CraftBukkit + } + + public double ae() { + return (double) this.length * 0.0D - 0.30000001192092896D; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (!this.world.isStatic && !this.dead) { + if (this.isInvulnerable()) { + return false; + } else { + // CraftBukkit start - fire VehicleDamageEvent + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + org.bukkit.entity.Entity passenger = (damagesource.getEntity() == null) ? null : damagesource.getEntity().getBukkitEntity(); + + VehicleDamageEvent event = new VehicleDamageEvent(vehicle, passenger, f); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return true; + } + + f = (float) event.getDamage(); + // CraftBukkit end + + this.j(-this.l()); + this.c(10); + this.Q(); + this.setDamage(this.getDamage() + f * 10.0F); + boolean flag = damagesource.getEntity() instanceof EntityHuman && ((EntityHuman) damagesource.getEntity()).abilities.canInstantlyBuild; + + if (flag || this.getDamage() > 40.0F) { + if (this.passenger != null) { + this.passenger.mount(this); + } + + // CraftBukkit start + VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, passenger); + this.world.getServer().getPluginManager().callEvent(destroyEvent); + + if (destroyEvent.isCancelled()) { + this.setDamage(40); // Maximize damage so this doesn't get triggered again right away + return true; + } + // CraftBukkit end + + if (flag && !this.k_()) { + this.die(); + } else { + this.a(damagesource); + } + } + + return true; + } + } else { + return true; + } + } + + public void a(DamageSource damagesource) { + this.die(); + ItemStack itemstack = new ItemStack(Items.MINECART, 1); + + if (this.b != null) { + itemstack.c(this.b); + } + + this.a(itemstack, 0.0F); + } + + public boolean R() { + return !this.dead; + } + + public void die() { + super.die(); + } + + public void h() { + // CraftBukkit start + double prevX = this.locX; + double prevY = this.locY; + double prevZ = this.locZ; + float prevYaw = this.yaw; + float prevPitch = this.pitch; + // CraftBukkit end + + if (this.getType() > 0) { + this.c(this.getType() - 1); + } + + if (this.getDamage() > 0.0F) { + this.setDamage(this.getDamage() - 1.0F); + } + + if (this.locY < -64.0D) { + this.G(); + } + + int i; + + if (!this.world.isStatic && this.world instanceof WorldServer) { + this.world.methodProfiler.a("portal"); + MinecraftServer minecraftserver = ((WorldServer) this.world).getMinecraftServer(); + + i = this.D(); + if (this.an) { + if (true || minecraftserver.getAllowNether()) { // CraftBukkit - multi-world should still allow teleport even if default vanilla nether disabled + if (this.vehicle == null && this.ao++ >= i) { + this.ao = i; + this.portalCooldown = this.ai(); + byte b0; + + if (this.world.worldProvider.dimension == -1) { + b0 = 0; + } else { + b0 = -1; + } + + this.b(b0); + } + + this.an = false; + } + } else { + if (this.ao > 0) { + this.ao -= 4; + } + + if (this.ao < 0) { + this.ao = 0; + } + } + + if (this.portalCooldown > 0) { + --this.portalCooldown; + } + + this.world.methodProfiler.b(); + } + + if (this.world.isStatic) { + if (this.d > 0) { + double d0 = this.locX + (this.e - this.locX) / (double) this.d; + double d1 = this.locY + (this.f - this.locY) / (double) this.d; + double d2 = this.locZ + (this.g - this.locZ) / (double) this.d; + double d3 = MathHelper.g(this.h - (double) this.yaw); + + this.yaw = (float) ((double) this.yaw + d3 / (double) this.d); + this.pitch = (float) ((double) this.pitch + (this.i - (double) this.pitch) / (double) this.d); + --this.d; + this.setPosition(d0, d1, d2); + this.b(this.yaw, this.pitch); + } else { + this.setPosition(this.locX, this.locY, this.locZ); + this.b(this.yaw, this.pitch); + } + } else { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.motY -= 0.03999999910593033D; + int j = MathHelper.floor(this.locX); + + i = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (BlockMinecartTrackAbstract.b_(this.world, j, i - 1, k)) { + --i; + } + + double d4 = this.maxSpeed; // CraftBukkit + double d5 = 0.0078125D; + Block block = this.world.getType(j, i, k); + + if (BlockMinecartTrackAbstract.a(block)) { + int l = this.world.getData(j, i, k); + + this.a(j, i, k, d4, d5, block, l); + if (block == Blocks.ACTIVATOR_RAIL) { + this.a(j, i, k, (l & 8) != 0); + } + } else { + this.b(d4); + } + + this.I(); + this.pitch = 0.0F; + double d6 = this.lastX - this.locX; + double d7 = this.lastZ - this.locZ; + + if (d6 * d6 + d7 * d7 > 0.001D) { + this.yaw = (float) (Math.atan2(d7, d6) * 180.0D / 3.141592653589793D); + if (this.a) { + this.yaw += 180.0F; + } + } + + double d8 = (double) MathHelper.g(this.yaw - this.lastYaw); + + if (d8 < -170.0D || d8 >= 170.0D) { + this.yaw += 180.0F; + this.a = !this.a; + } + + this.b(this.yaw, this.pitch); + + // CraftBukkit start + org.bukkit.World bworld = this.world.getWorld(); + Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch); + Location to = new Location(bworld, this.locX, this.locY, this.locZ, this.yaw, this.pitch); + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + + this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); + + if (!from.equals(to)) { + this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to)); + } + // CraftBukkit end + + List list = this.world.getEntities(this, this.boundingBox.grow(0.20000000298023224D, 0.0D, 0.20000000298023224D)); + + if (list != null && !list.isEmpty()) { + for (int i1 = 0; i1 < list.size(); ++i1) { + Entity entity = (Entity) list.get(i1); + + if (entity != this.passenger && entity.S() && entity instanceof EntityMinecartAbstract) { + entity.collide(this); + } + } + } + + if (this.passenger != null && this.passenger.dead) { + if (this.passenger.vehicle == this) { + this.passenger.vehicle = null; + } + + this.passenger = null; + } + // Spigot start - Make hoppers around this container minecart active. + // Called each tick on each minecart. + if (this.world.spigotConfig.altHopperTicking && this instanceof EntityMinecartContainer) { + int xi = MathHelper.floor(this.boundingBox.a) - 1; + int yi = MathHelper.floor(this.boundingBox.b) - 1; + int zi = MathHelper.floor(this.boundingBox.c) - 1; + int xf = MathHelper.floor(this.boundingBox.d) + 1; + int yf = MathHelper.floor(this.boundingBox.e) + 1; + int zf = MathHelper.floor(this.boundingBox.f) + 1; + for (int a = xi; a <= xf; a++) { + for (int b = yi; b <= yf; b++) { + for (int c = zi; c <= zf; c++) { + TileEntity tileEntity = this.world.getTileEntity(a, b, c); + if (tileEntity instanceof TileEntityHopper) { + ((TileEntityHopper) tileEntity).makeTick(); + } + } + } + } + } + // Spigot end + } + } + + public void a(int i, int j, int k, boolean flag) {} + + protected void b(double d0) { + if (this.motX < -d0) { + this.motX = -d0; + } + + if (this.motX > d0) { + this.motX = d0; + } + + if (this.motZ < -d0) { + this.motZ = -d0; + } + + if (this.motZ > d0) { + this.motZ = d0; + } + + if (this.onGround) { + // CraftBukkit start - replace magic numbers with our variables + this.motX *= this.derailedX; + this.motY *= this.derailedY; + this.motZ *= this.derailedZ; + // CraftBukkit end + } + + this.move(this.motX, this.motY, this.motZ); + if (!this.onGround) { + // CraftBukkit start - replace magic numbers with our variables + this.motX *= this.flyingX; + this.motY *= this.flyingY; + this.motZ *= this.flyingZ; + // CraftBukkit end + } + } + + protected void a(int i, int j, int k, double d0, double d1, Block block, int l) { + this.fallDistance = 0.0F; + Vec3D vec3d = this.a(this.locX, this.locY, this.locZ); + + this.locY = (double) j; + boolean flag = false; + boolean flag1 = false; + + if (block == Blocks.GOLDEN_RAIL) { + flag = (l & 8) != 0; + flag1 = !flag; + } + + if (((BlockMinecartTrackAbstract) block).e()) { + l &= 7; + } + + if (l >= 2 && l <= 5) { + this.locY = (double) (j + 1); + } + + if (l == 2) { + this.motX -= d1; + } + + if (l == 3) { + this.motX += d1; + } + + if (l == 4) { + this.motZ += d1; + } + + if (l == 5) { + this.motZ -= d1; + } + + int[][] aint = matrix[l]; + double d2 = (double) (aint[1][0] - aint[0][0]); + double d3 = (double) (aint[1][2] - aint[0][2]); + double d4 = Math.sqrt(d2 * d2 + d3 * d3); + double d5 = this.motX * d2 + this.motZ * d3; + + if (d5 < 0.0D) { + d2 = -d2; + d3 = -d3; + } + + double d6 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + if (d6 > 2.0D) { + d6 = 2.0D; + } + + this.motX = d6 * d2 / d4; + this.motZ = d6 * d3 / d4; + double d7; + double d8; + double d9; + double d10; + + if (this.passenger != null && this.passenger instanceof EntityLiving) { + d7 = (double) ((EntityLiving) this.passenger).be; + if (d7 > 0.0D) { + d8 = -Math.sin((double) (this.passenger.yaw * 3.1415927F / 180.0F)); + d9 = Math.cos((double) (this.passenger.yaw * 3.1415927F / 180.0F)); + d10 = this.motX * this.motX + this.motZ * this.motZ; + if (d10 < 0.01D) { + this.motX += d8 * 0.1D; + this.motZ += d9 * 0.1D; + flag1 = false; + } + } + } + + if (flag1) { + d7 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + if (d7 < 0.03D) { + this.motX *= 0.0D; + this.motY *= 0.0D; + this.motZ *= 0.0D; + } else { + this.motX *= 0.5D; + this.motY *= 0.0D; + this.motZ *= 0.5D; + } + } + + d7 = 0.0D; + d8 = (double) i + 0.5D + (double) aint[0][0] * 0.5D; + d9 = (double) k + 0.5D + (double) aint[0][2] * 0.5D; + d10 = (double) i + 0.5D + (double) aint[1][0] * 0.5D; + double d11 = (double) k + 0.5D + (double) aint[1][2] * 0.5D; + + d2 = d10 - d8; + d3 = d11 - d9; + double d12; + double d13; + + if (d2 == 0.0D) { + this.locX = (double) i + 0.5D; + d7 = this.locZ - (double) k; + } else if (d3 == 0.0D) { + this.locZ = (double) k + 0.5D; + d7 = this.locX - (double) i; + } else { + d12 = this.locX - d8; + d13 = this.locZ - d9; + d7 = (d12 * d2 + d13 * d3) * 2.0D; + } + + this.locX = d8 + d2 * d7; + this.locZ = d9 + d3 * d7; + this.setPosition(this.locX, this.locY + (double) this.height, this.locZ); + d12 = this.motX; + d13 = this.motZ; + if (this.passenger != null) { + d12 *= 0.75D; + d13 *= 0.75D; + } + + if (d12 < -d0) { + d12 = -d0; + } + + if (d12 > d0) { + d12 = d0; + } + + if (d13 < -d0) { + d13 = -d0; + } + + if (d13 > d0) { + d13 = d0; + } + + this.move(d12, 0.0D, d13); + if (aint[0][1] != 0 && MathHelper.floor(this.locX) - i == aint[0][0] && MathHelper.floor(this.locZ) - k == aint[0][2]) { + this.setPosition(this.locX, this.locY + (double) aint[0][1], this.locZ); + } else if (aint[1][1] != 0 && MathHelper.floor(this.locX) - i == aint[1][0] && MathHelper.floor(this.locZ) - k == aint[1][2]) { + this.setPosition(this.locX, this.locY + (double) aint[1][1], this.locZ); + } + + this.i(); + Vec3D vec3d1 = this.a(this.locX, this.locY, this.locZ); + + if (vec3d1 != null && vec3d != null) { + double d14 = (vec3d.b - vec3d1.b) * 0.05D; + + d6 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + if (d6 > 0.0D) { + this.motX = this.motX / d6 * (d6 + d14); + this.motZ = this.motZ / d6 * (d6 + d14); + } + + this.setPosition(this.locX, vec3d1.b, this.locZ); + } + + int i1 = MathHelper.floor(this.locX); + int j1 = MathHelper.floor(this.locZ); + + if (i1 != i || j1 != k) { + d6 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + this.motX = d6 * (double) (i1 - i); + this.motZ = d6 * (double) (j1 - k); + } + + if (flag) { + double d15 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + if (d15 > 0.01D) { + double d16 = 0.06D; + + this.motX += this.motX / d15 * d16; + this.motZ += this.motZ / d15 * d16; + } else if (l == 1) { + if (this.world.getType(i - 1, j, k).r()) { + this.motX = 0.02D; + } else if (this.world.getType(i + 1, j, k).r()) { + this.motX = -0.02D; + } + } else if (l == 0) { + if (this.world.getType(i, j, k - 1).r()) { + this.motZ = 0.02D; + } else if (this.world.getType(i, j, k + 1).r()) { + this.motZ = -0.02D; + } + } + } + } + + protected void i() { + if (this.passenger != null || !this.slowWhenEmpty) { // CraftBukkit - add !this.slowWhenEmpty + this.motX *= 0.996999979019165D; + this.motY *= 0.0D; + this.motZ *= 0.996999979019165D; + } else { + this.motX *= 0.9599999785423279D; + this.motY *= 0.0D; + this.motZ *= 0.9599999785423279D; + } + } + + public Vec3D a(double d0, double d1, double d2) { + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + + if (BlockMinecartTrackAbstract.b_(this.world, i, j - 1, k)) { + --j; + } + + Block block = this.world.getType(i, j, k); + + if (BlockMinecartTrackAbstract.a(block)) { + int l = this.world.getData(i, j, k); + + d1 = (double) j; + if (((BlockMinecartTrackAbstract) block).e()) { + l &= 7; + } + + if (l >= 2 && l <= 5) { + d1 = (double) (j + 1); + } + + int[][] aint = matrix[l]; + double d3 = 0.0D; + double d4 = (double) i + 0.5D + (double) aint[0][0] * 0.5D; + double d5 = (double) j + 0.5D + (double) aint[0][1] * 0.5D; + double d6 = (double) k + 0.5D + (double) aint[0][2] * 0.5D; + double d7 = (double) i + 0.5D + (double) aint[1][0] * 0.5D; + double d8 = (double) j + 0.5D + (double) aint[1][1] * 0.5D; + double d9 = (double) k + 0.5D + (double) aint[1][2] * 0.5D; + double d10 = d7 - d4; + double d11 = (d8 - d5) * 2.0D; + double d12 = d9 - d6; + + if (d10 == 0.0D) { + d0 = (double) i + 0.5D; + d3 = d2 - (double) k; + } else if (d12 == 0.0D) { + d2 = (double) k + 0.5D; + d3 = d0 - (double) i; + } else { + double d13 = d0 - d4; + double d14 = d2 - d6; + + d3 = (d13 * d10 + d14 * d12) * 2.0D; + } + + d0 = d4 + d10 * d3; + d1 = d5 + d11 * d3; + d2 = d6 + d12 * d3; + if (d11 < 0.0D) { + ++d1; + } + + if (d11 > 0.0D) { + d1 += 0.5D; + } + + return Vec3D.a(d0, d1, d2); + } else { + return null; + } + } + + protected void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.getBoolean("CustomDisplayTile")) { + this.k(nbttagcompound.getInt("DisplayTile")); + this.l(nbttagcompound.getInt("DisplayData")); + this.m(nbttagcompound.getInt("DisplayOffset")); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8) && nbttagcompound.getString("CustomName").length() > 0) { + this.b = nbttagcompound.getString("CustomName"); + } + } + + protected void b(NBTTagCompound nbttagcompound) { + if (this.t()) { + nbttagcompound.setBoolean("CustomDisplayTile", true); + nbttagcompound.setInt("DisplayTile", this.n().getMaterial() == Material.AIR ? 0 : Block.getId(this.n())); + nbttagcompound.setInt("DisplayData", this.p()); + nbttagcompound.setInt("DisplayOffset", this.r()); + } + + if (this.b != null && this.b.length() > 0) { + nbttagcompound.setString("CustomName", this.b); + } + } + + public void collide(Entity entity) { + if (!this.world.isStatic) { + if (entity != this.passenger) { + // CraftBukkit start + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + org.bukkit.entity.Entity hitEntity = (entity == null) ? null : entity.getBukkitEntity(); + + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, hitEntity); + this.world.getServer().getPluginManager().callEvent(collisionEvent); + + if (collisionEvent.isCancelled()) { + return; + } + // CraftBukkit end + + if (entity instanceof EntityLiving && !(entity instanceof EntityHuman) && !(entity instanceof EntityIronGolem) && this.m() == 0 && this.motX * this.motX + this.motZ * this.motZ > 0.01D && this.passenger == null && entity.vehicle == null) { + entity.mount(this); + } + + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2 = d0 * d0 + d1 * d1; + + // CraftBukkit - collision + if (d2 >= 9.999999747378752E-5D && !collisionEvent.isCollisionCancelled()) { + d2 = (double) MathHelper.sqrt(d2); + d0 /= d2; + d1 /= d2; + double d3 = 1.0D / d2; + + if (d3 > 1.0D) { + d3 = 1.0D; + } + + d0 *= d3; + d1 *= d3; + d0 *= 0.10000000149011612D; + d1 *= 0.10000000149011612D; + d0 *= (double) (1.0F - this.Y); + d1 *= (double) (1.0F - this.Y); + d0 *= 0.5D; + d1 *= 0.5D; + if (entity instanceof EntityMinecartAbstract) { + double d4 = entity.locX - this.locX; + double d5 = entity.locZ - this.locZ; + Vec3D vec3d = Vec3D.a(d4, 0.0D, d5).a(); + Vec3D vec3d1 = Vec3D.a((double) MathHelper.cos(this.yaw * 3.1415927F / 180.0F), 0.0D, (double) MathHelper.sin(this.yaw * 3.1415927F / 180.0F)).a(); + double d6 = Math.abs(vec3d.b(vec3d1)); + + if (d6 < 0.800000011920929D) { + return; + } + + double d7 = entity.motX + this.motX; + double d8 = entity.motZ + this.motZ; + + if (((EntityMinecartAbstract) entity).m() == 2 && this.m() != 2) { + this.motX *= 0.20000000298023224D; + this.motZ *= 0.20000000298023224D; + this.g(entity.motX - d0, 0.0D, entity.motZ - d1); + entity.motX *= 0.949999988079071D; + entity.motZ *= 0.949999988079071D; + } else if (((EntityMinecartAbstract) entity).m() != 2 && this.m() == 2) { + entity.motX *= 0.20000000298023224D; + entity.motZ *= 0.20000000298023224D; + entity.g(this.motX + d0, 0.0D, this.motZ + d1); + this.motX *= 0.949999988079071D; + this.motZ *= 0.949999988079071D; + } else { + d7 /= 2.0D; + d8 /= 2.0D; + this.motX *= 0.20000000298023224D; + this.motZ *= 0.20000000298023224D; + this.g(d7 - d0, 0.0D, d8 - d1); + entity.motX *= 0.20000000298023224D; + entity.motZ *= 0.20000000298023224D; + entity.g(d7 + d0, 0.0D, d8 + d1); + } + } else { + this.g(-d0, 0.0D, -d1); + entity.g(d0 / 4.0D, 0.0D, d1 / 4.0D); + } + } + } + } + } + + public void setDamage(float f) { + this.datawatcher.watch(19, Float.valueOf(f)); + } + + public float getDamage() { + return this.datawatcher.getFloat(19); + } + + public void c(int i) { + this.datawatcher.watch(17, Integer.valueOf(i)); + } + + public int getType() { + return this.datawatcher.getInt(17); + } + + public void j(int i) { + this.datawatcher.watch(18, Integer.valueOf(i)); + } + + public int l() { + return this.datawatcher.getInt(18); + } + + public abstract int m(); + + public Block n() { + if (!this.t()) { + return this.o(); + } else { + int i = this.getDataWatcher().getInt(20) & '\uffff'; + + return Block.getById(i); + } + } + + public Block o() { + return Blocks.AIR; + } + + public int p() { + return !this.t() ? this.q() : this.getDataWatcher().getInt(20) >> 16; + } + + public int q() { + return 0; + } + + public int r() { + return !this.t() ? this.s() : this.getDataWatcher().getInt(21); + } + + public int s() { + return 6; + } + + public void k(int i) { + // Spigot start - protocol patch + org.spigotmc.ProtocolData.DualInt val = datawatcher.getDualInt(20); + val.value = Integer.valueOf(i & '\uffff' | this.p() << 16); + val.value2 = Integer.valueOf(i & '\uffff' | this.p() << 12); + this.getDataWatcher().watch(20, val); + // Spigot end + this.a(true); + } + + public void l(int i) { + // Spigot start - protocol patch + org.spigotmc.ProtocolData.DualInt val = datawatcher.getDualInt(20); + val.value = Integer.valueOf(Block.getId(this.n()) & '\uffff' | i << 16); + val.value2 = Integer.valueOf(Block.getId(this.n()) & '\uffff' | i << 12); + this.getDataWatcher().watch(20, val); + // Spigot end + this.a(true); + } + + public void m(int i) { + this.getDataWatcher().watch(21, Integer.valueOf(i)); + this.a(true); + } + + public boolean t() { + return this.getDataWatcher().getByte(22) == 1; + } + + public void a(boolean flag) { + this.getDataWatcher().watch(22, Byte.valueOf((byte) (flag ? 1 : 0))); + } + + public void a(String s) { + this.b = s; + } + + public String getName() { + return this.b != null ? this.b : super.getName(); + } + + public boolean k_() { + return this.b != null; + } + + public String u() { + return this.b; + } + + // CraftBukkit start - Methods for getting and setting flying and derailed velocity modifiers + public Vector getFlyingVelocityMod() { + return new Vector(flyingX, flyingY, flyingZ); + } + + public void setFlyingVelocityMod(Vector flying) { + flyingX = flying.getX(); + flyingY = flying.getY(); + flyingZ = flying.getZ(); + } + + public Vector getDerailedVelocityMod() { + return new Vector(derailedX, derailedY, derailedZ); + } + + public void setDerailedVelocityMod(Vector derailed) { + derailedX = derailed.getX(); + derailedY = derailed.getY(); + derailedZ = derailed.getZ(); + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartCommandBlockListener.java b/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartCommandBlockListener.java new file mode 100644 index 0000000..5b45285 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartCommandBlockListener.java @@ -0,0 +1,25 @@ +package net.minecraft.server; + +// CraftBukkit - package-private -> public +public class EntityMinecartCommandBlockListener extends CommandBlockListenerAbstract { + + final EntityMinecartCommandBlock a; + + EntityMinecartCommandBlockListener(EntityMinecartCommandBlock entityminecartcommandblock) { + this.a = entityminecartcommandblock; + this.sender = (org.bukkit.craftbukkit.entity.CraftMinecartCommand) entityminecartcommandblock.getBukkitEntity(); // CraftBukkit - Set the sender + } + + public void e() { + this.a.getDataWatcher().watch(23, this.getCommand()); + this.a.getDataWatcher().watch(24, ChatSerializer.a(this.h())); + } + + public ChunkCoordinates getChunkCoordinates() { + return new ChunkCoordinates(MathHelper.floor(this.a.locX), MathHelper.floor(this.a.locY + 0.5D), MathHelper.floor(this.a.locZ)); + } + + public World getWorld() { + return this.a.world; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartContainer.java b/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartContainer.java new file mode 100644 index 0000000..bf8e745 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityMinecartContainer.java @@ -0,0 +1,249 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +// CraftBukkit end + +public abstract class EntityMinecartContainer extends EntityMinecartAbstract implements IInventory { + + private ItemStack[] items = new ItemStack[27]; // CraftBukkit - 36 -> 27 + private boolean b = true; + + // CraftBukkit start + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public InventoryHolder getOwner() { + org.bukkit.entity.Entity cart = getBukkitEntity(); + if(cart instanceof InventoryHolder) return (InventoryHolder) cart; + return null; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public EntityMinecartContainer(World world) { + super(world); + } + + public EntityMinecartContainer(World world, double d0, double d1, double d2) { + super(world, d0, d1, d2); + } + + public void a(DamageSource damagesource) { + super.a(damagesource); + + for (int i = 0; i < this.getSize(); ++i) { + ItemStack itemstack = this.getItem(i); + + if (itemstack != null) { + float f = this.random.nextFloat() * 0.8F + 0.1F; + float f1 = this.random.nextFloat() * 0.8F + 0.1F; + float f2 = this.random.nextFloat() * 0.8F + 0.1F; + + while (itemstack.count > 0) { + int j = this.random.nextInt(21) + 10; + + if (j > itemstack.count) { + j = itemstack.count; + } + + itemstack.count -= j; + EntityItem entityitem = new EntityItem(this.world, this.locX + (double) f, this.locY + (double) f1, this.locZ + (double) f2, new ItemStack(itemstack.getItem(), j, itemstack.getData())); + float f3 = 0.05F; + + entityitem.motX = (double) ((float) this.random.nextGaussian() * f3); + entityitem.motY = (double) ((float) this.random.nextGaussian() * f3 + 0.2F); + entityitem.motZ = (double) ((float) this.random.nextGaussian() * f3); + this.world.addEntity(entityitem); + } + } + } + } + + public ItemStack getItem(int i) { + return this.items[i]; + } + + public ItemStack splitStack(int i, int j) { + if (this.items[i] != null) { + ItemStack itemstack; + + if (this.items[i].count <= j) { + itemstack = this.items[i]; + this.items[i] = null; + return itemstack; + } else { + itemstack = this.items[i].a(j); + if (this.items[i].count == 0) { + this.items[i] = null; + } + + return itemstack; + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.items[i] != null) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.items[i] = itemstack; + if (itemstack != null && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return this.dead ? false : entityhuman.f(this) <= 64.0D; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public String getInventoryName() { + return this.k_() ? this.u() : "container.minecart"; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void b(int i) { + // Spigot Start + for ( HumanEntity human : new java.util.ArrayList( transaction ) ) + { + human.closeInventory(); + } + // Spigot End + this.b = false; + super.b(i); + } + + public void die() { + if (this.b) { + for (int i = 0; i < this.getSize(); ++i) { + ItemStack itemstack = this.getItem(i); + + if (itemstack != null) { + float f = this.random.nextFloat() * 0.8F + 0.1F; + float f1 = this.random.nextFloat() * 0.8F + 0.1F; + float f2 = this.random.nextFloat() * 0.8F + 0.1F; + + while (itemstack.count > 0) { + int j = this.random.nextInt(21) + 10; + + if (j > itemstack.count) { + j = itemstack.count; + } + + itemstack.count -= j; + EntityItem entityitem = new EntityItem(this.world, this.locX + (double) f, this.locY + (double) f1, this.locZ + (double) f2, new ItemStack(itemstack.getItem(), j, itemstack.getData())); + + if (itemstack.hasTag()) { + entityitem.getItemStack().setTag((NBTTagCompound) itemstack.getTag().clone()); + } + + float f3 = 0.05F; + + entityitem.motX = (double) ((float) this.random.nextGaussian() * f3); + entityitem.motY = (double) ((float) this.random.nextGaussian() * f3 + 0.2F); + entityitem.motZ = (double) ((float) this.random.nextGaussian() * f3); + this.world.addEntity(entityitem); + } + } + } + } + + super.die(); + } + + protected void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + this.items[i].save(nbttagcompound1); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.items = new ItemStack[this.getSize()]; + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + int j = nbttagcompound1.getByte("Slot") & 255; + + if (j >= 0 && j < this.items.length) { + this.items[j] = ItemStack.createStack(nbttagcompound1); + } + } + } + + public boolean c(EntityHuman entityhuman) { + if (!this.world.isStatic) { + entityhuman.openContainer(this); + } + + return true; + } + + protected void i() { + int i = 15 - Container.b((IInventory) this); + float f = 0.98F + (float) i * 0.001F; + + this.motX *= (double) f; + this.motY *= 0.0D; + this.motZ *= (double) f; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityMonster.java b/vspigot-server/src/main/java/net/minecraft/server/EntityMonster.java new file mode 100644 index 0000000..123cc0d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityMonster.java @@ -0,0 +1,191 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + +public abstract class EntityMonster extends EntityCreature implements IMonster { + + public EntityMonster(World world) { + super(world); + this.b = 5; + } + + public void e() { + this.bb(); + float f = this.d(1.0F); + + if (f > 0.5F) { + this.aU += 2; + } + + super.e(); + } + + public void h() { + super.h(); + if (!this.world.isStatic && this.world.difficulty == EnumDifficulty.PEACEFUL) { + this.die(); + } + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + } + + protected String H() { + return "game.hostile.swim"; + } + + protected String O() { + return "game.hostile.swim.splash"; + } + + private long lastTargetSearchTick = -1L; // MineHQ + protected Entity findTarget() { + // MineHQ start + if (this.lastTargetSearchTick + 50 < this.ticksLived) { + this.lastTargetSearchTick = this.ticksLived; + } else { + return null; + } + // MineHQ end + EntityHuman entityhuman = this.world.findNearbyVulnerablePlayer(this, 16.0D); + + return entityhuman != null && this.hasLineOfSight(entityhuman) ? entityhuman : null; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else if (super.damageEntity(damagesource, f)) { + Entity entity = damagesource.getEntity(); + + if (this.passenger != entity && this.vehicle != entity) { + if (entity != this) { + // CraftBukkit start - We still need to call events for entities without goals + if (entity != this.target && (this instanceof EntityBlaze || this instanceof EntityEnderman || this instanceof EntitySpider || this instanceof EntityGiantZombie || this instanceof EntitySilverfish)) { + EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent(this, entity, EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY); + + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.target = null; + } else { + this.target = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); + } + } + } else { + this.target = entity; + } + // CraftBukkit end + } + + return true; + } else { + return true; + } + } else { + return false; + } + } + + protected String aT() { + return "game.hostile.hurt"; + } + + protected String aU() { + return "game.hostile.die"; + } + + protected String o(int i) { + return i > 4 ? "game.hostile.hurt.fall.big" : "game.hostile.hurt.fall.small"; + } + + public boolean n(Entity entity) { + float f = (float) this.getAttributeInstance(GenericAttributes.e).getValue(); + int i = 0; + + if (entity instanceof EntityLiving) { + f += EnchantmentManager.a((EntityLiving) this, (EntityLiving) entity); + i += EnchantmentManager.getKnockbackEnchantmentLevel(this, (EntityLiving) entity); + } + + boolean flag = entity.damageEntity(DamageSource.mobAttack(this), f); + + if (flag) { + if (i > 0) { + entity.g((double) (-MathHelper.sin(this.yaw * 3.1415927F / 180.0F) * (float) i * 0.5F), 0.1D, (double) (MathHelper.cos(this.yaw * 3.1415927F / 180.0F) * (float) i * 0.5F)); + this.motX *= 0.6D; + this.motZ *= 0.6D; + } + + int j = EnchantmentManager.getFireAspectEnchantmentLevel(this); + + if (j > 0) { + // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item + EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), j * 4); + org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + + if (!combustEvent.isCancelled()) { + entity.setOnFire(combustEvent.getDuration()); + } + // CraftBukkit end + } + + if (entity instanceof EntityLiving) { + EnchantmentManager.a((EntityLiving) entity, (Entity) this); + } + + EnchantmentManager.b(this, entity); + } + + return flag; + } + + protected void a(Entity entity, float f) { + if (this.attackTicks <= 0 && f < 2.0F && entity.boundingBox.e > this.boundingBox.b && entity.boundingBox.b < this.boundingBox.e) { + this.attackTicks = 20; + this.n(entity); + } + } + + public float a(int i, int j, int k) { + return 0.5F - this.world.n(i, j, k); + } + + protected boolean j_() { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.boundingBox.b); + int k = MathHelper.floor(this.locZ); + + if (this.world.b(EnumSkyBlock.SKY, i, j, k) > this.random.nextInt(32)) { + return false; + } else { + // int l = this.world.getLightLevel(i, j, k); // MineHQ + boolean passes; // MineHQ + if (this.world.P()) { + int i1 = this.world.j; + + this.world.j = 10; + // l = this.world.getLightLevel(i, j, k); // MineHQ + passes = !this.world.isLightLevel(i, j, k, this.random.nextInt(9)); // MineHQ + this.world.j = i1; + } else { passes = !this.world.isLightLevel(i, j, k, this.random.nextInt(9)); } // MineHQ + + return passes; // MineHQ + } + } + + public boolean canSpawn() { + return this.world.difficulty != EnumDifficulty.PEACEFUL && this.j_() && super.canSpawn(); + } + + protected void aD() { + super.aD(); + this.getAttributeMap().b(GenericAttributes.e); + } + + protected boolean aG() { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityMushroomCow.java b/vspigot-server/src/main/java/net/minecraft/server/EntityMushroomCow.java new file mode 100644 index 0000000..b4e4306 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityMushroomCow.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +import org.bukkit.event.player.PlayerShearEntityEvent; // CraftBukkit + +public class EntityMushroomCow extends EntityCow { + + public EntityMushroomCow(World world) { + super(world); + this.a(0.9F, 1.3F); + } + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() == Items.BOWL && this.getAge() >= 0) { + if (itemstack.count == 1) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, new ItemStack(Items.MUSHROOM_SOUP)); + return true; + } + + if (entityhuman.inventory.pickup(new ItemStack(Items.MUSHROOM_SOUP)) && !entityhuman.abilities.canInstantlyBuild) { + entityhuman.inventory.splitStack(entityhuman.inventory.itemInHandIndex, 1); + return true; + } + } + + if (itemstack != null && itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) { + // CraftBukkit start + PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + + this.die(); + this.world.addParticle("largeexplode", this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D); + if (!this.world.isStatic) { + EntityCow entitycow = new EntityCow(this.world); + + entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + entitycow.setHealth(this.getHealth()); + entitycow.aM = this.aM; + this.world.addEntity(entitycow); + + for (int i = 0; i < 5; ++i) { + this.world.addEntity(new EntityItem(this.world, this.locX, this.locY + (double) this.length, this.locZ, new ItemStack(Blocks.RED_MUSHROOM))); + } + + itemstack.damage(1, entityhuman); + this.makeSound("mob.sheep.shear", 1.0F, 1.0F); + } + + return true; + } else { + return super.a(entityhuman); + } + } + + public EntityMushroomCow c(EntityAgeable entityageable) { + return new EntityMushroomCow(this.world); + } + + public EntityCow b(EntityAgeable entityageable) { + return this.c(entityageable); + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.c(entityageable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityOcelot.java b/vspigot-server/src/main/java/net/minecraft/server/EntityOcelot.java new file mode 100644 index 0000000..8abbcdf --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityOcelot.java @@ -0,0 +1,258 @@ +package net.minecraft.server; + +public class EntityOcelot extends EntityTameableAnimal { + + public boolean spawnBonus = true; // Spigot + private PathfinderGoalTempt bq; + + public EntityOcelot(World world) { + super(world); + this.a(0.6F, 0.8F); + this.getNavigation().a(true); + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, this.bp); + this.goalSelector.a(3, this.bq = new PathfinderGoalTempt(this, 0.6D, Items.RAW_FISH, true)); + this.goalSelector.a(4, new PathfinderGoalAvoidPlayer(this, EntityHuman.class, 16.0F, 0.8D, 1.33D)); + this.goalSelector.a(5, new PathfinderGoalFollowOwner(this, 1.0D, 10.0F, 5.0F)); + this.goalSelector.a(6, new PathfinderGoalJumpOnBlock(this, 1.33D)); + this.goalSelector.a(7, new PathfinderGoalLeapAtTarget(this, 0.3F)); + this.goalSelector.a(8, new PathfinderGoalOcelotAttack(this)); + this.goalSelector.a(9, new PathfinderGoalBreed(this, 0.8D)); + this.goalSelector.a(10, new PathfinderGoalRandomStroll(this, 0.8D)); + this.goalSelector.a(11, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 10.0F)); + this.targetSelector.a(1, new PathfinderGoalRandomTargetNonTamed(this, EntityChicken.class, 750, false)); + } + + protected void c() { + super.c(); + this.datawatcher.a(18, Byte.valueOf((byte) 0)); + } + + // Spigot start - When this ocelot begins standing, chests below this ocelot must be + // updated as if its contents have changed. We update chests if this ocelot is sitting + // knowing that it may be dead, gone, or standing after this method returns. + // Called each tick on each ocelot. + @Override + public void h() { + if (this.world.spigotConfig.altHopperTicking && this.isSitting()) { + int xi = MathHelper.floor(this.boundingBox.a); + int yi = MathHelper.floor(this.boundingBox.b) - 1; + int zi = MathHelper.floor(this.boundingBox.c); + int xf = MathHelper.floor(this.boundingBox.d); + int yf = MathHelper.floor(this.boundingBox.e) - 1; + int zf = MathHelper.floor(this.boundingBox.f); + for (int a = xi; a <= xf; a++) { + for (int c = zi; c <= zf; c++) { + for (int b = yi; b <= yf; b++) { + this.world.updateChestAndHoppers(a, b, c); + } + } + } + } + super.h(); + } + // Spigot end + + public void bp() { + if (this.getControllerMove().a()) { + double d0 = this.getControllerMove().b(); + + if (d0 == 0.6D) { + this.setSneaking(true); + this.setSprinting(false); + } else if (d0 == 1.33D) { + this.setSneaking(false); + this.setSprinting(true); + } else { + this.setSneaking(false); + this.setSprinting(false); + } + } else { + this.setSneaking(false); + this.setSprinting(false); + } + } + + protected boolean isTypeNotPersistent() { + return !this.isTamed() /*&& this.ticksLived > 2400*/; // CraftBukkit + } + + public boolean bk() { + return true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.30000001192092896D); + } + + protected void b(float f) {} + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("CatType", this.getCatType()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setCatType(nbttagcompound.getInt("CatType")); + } + + protected String t() { + return this.isTamed() ? (this.ce() ? "mob.cat.purr" : (this.random.nextInt(4) == 0 ? "mob.cat.purreow" : "mob.cat.meow")) : ""; + } + + protected String aT() { + return "mob.cat.hitt"; + } + + protected String aU() { + return "mob.cat.hitt"; + } + + protected float bf() { + return 0.4F; + } + + protected Item getLoot() { + return Items.LEATHER; + } + + public boolean n(Entity entity) { + return entity.damageEntity(DamageSource.mobAttack(this), 3.0F); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + this.bp.setSitting(false); + return super.damageEntity(damagesource, f); + } + } + + protected void dropDeathLoot(boolean flag, int i) {} + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (this.isTamed()) { + if (this.e(entityhuman) && !this.world.isStatic && !this.c(itemstack)) { + this.bp.setSitting(!this.isSitting()); + } + } else if (this.bq.f() && itemstack != null && itemstack.getItem() == Items.RAW_FISH && entityhuman.f(this) < 9.0D) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + if (itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + + if (!this.world.isStatic) { + // CraftBukkit - added event call and isCancelled check + if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) { + this.setTamed(true); + this.setCatType(1 + this.world.random.nextInt(3)); + this.setOwnerUUID(entityhuman.getUniqueID().toString()); + this.i(true); + this.bp.setSitting(true); + this.world.broadcastEntityEffect(this, (byte) 7); + } else { + this.i(false); + this.world.broadcastEntityEffect(this, (byte) 6); + } + } + + return true; + } + + return super.a(entityhuman); + } + + public EntityOcelot b(EntityAgeable entityageable) { + EntityOcelot entityocelot = new EntityOcelot(this.world); + + if (this.isTamed()) { + entityocelot.setOwnerUUID(this.getOwnerUUID()); + entityocelot.setTamed(true); + entityocelot.setCatType(this.getCatType()); + } + + return entityocelot; + } + + public boolean c(ItemStack itemstack) { + return itemstack != null && itemstack.getItem() == Items.RAW_FISH; + } + + public boolean mate(EntityAnimal entityanimal) { + if (entityanimal == this) { + return false; + } else if (!this.isTamed()) { + return false; + } else if (!(entityanimal instanceof EntityOcelot)) { + return false; + } else { + EntityOcelot entityocelot = (EntityOcelot) entityanimal; + + return !entityocelot.isTamed() ? false : this.ce() && entityocelot.ce(); + } + } + + public int getCatType() { + return this.datawatcher.getByte(18); + } + + public void setCatType(int i) { + this.datawatcher.watch(18, Byte.valueOf((byte) i)); + } + + public boolean canSpawn() { + if (this.world.random.nextInt(3) == 0) { + return false; + } else { + if (this.world.b(this.boundingBox) && this.world.getCubes(this, this.boundingBox).isEmpty() && !this.world.containsLiquid(this.boundingBox)) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.boundingBox.b); + int k = MathHelper.floor(this.locZ); + + if (j < 63) { + return false; + } + + Block block = this.world.getType(i, j - 1, k); + + if (block == Blocks.GRASS || block.getMaterial() == Material.LEAVES) { + return true; + } + } + + return false; + } + } + + public String getName() { + return this.hasCustomName() ? this.getCustomName() : (this.isTamed() ? LocaleI18n.get("entity.Cat.name") : super.getName()); + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + groupdataentity = super.prepare(groupdataentity); + if (spawnBonus && this.world.random.nextInt(7) == 0) { // Spigot + for (int i = 0; i < 2; ++i) { + EntityOcelot entityocelot = new EntityOcelot(this.world); + + entityocelot.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entityocelot.setAge(-24000); + this.world.addEntity(entityocelot, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OCELOT_BABY); // CraftBukkit - add SpawnReason + } + } + + return groupdataentity; + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.b(entityageable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityPainting.java b/vspigot-server/src/main/java/net/minecraft/server/EntityPainting.java new file mode 100644 index 0000000..77ba1f7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityPainting.java @@ -0,0 +1,81 @@ +package net.minecraft.server; + +import java.util.ArrayList; + +public class EntityPainting extends EntityHanging { + + public EnumArt art; + + public EntityPainting(World world) { + super(world); + this.art = EnumArt.values()[this.random.nextInt(EnumArt.values().length)]; // CraftBukkit - generate a non-null painting + } + + public EntityPainting(World world, int i, int j, int k, int l) { + super(world, i, j, k, l); + EnumArt[] aenumart = EnumArt.values(); + ArrayList arraylist = new ArrayList(aenumart.length); + int i1 = aenumart.length; + + for (int j1 = 0; j1 < i1; ++j1) { + EnumArt enumart = aenumart[j1]; + + this.art = enumart; + this.setDirection(l); + if (this.survives()) { + arraylist.add(enumart); + } + } + + if (!arraylist.isEmpty()) { + this.art = (EnumArt) arraylist.get(this.random.nextInt(arraylist.size())); + } + + this.setDirection(l); + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setString("Motive", this.art.B); + super.b(nbttagcompound); + } + + public void a(NBTTagCompound nbttagcompound) { + String s = nbttagcompound.getString("Motive"); + EnumArt[] aenumart = EnumArt.values(); + int i = aenumart.length; + + for (int j = 0; j < i; ++j) { + EnumArt enumart = aenumart[j]; + + if (enumart.B.equals(s)) { + this.art = enumart; + } + } + + if (this.art == null) { + this.art = EnumArt.KEBAB; + } + + super.a(nbttagcompound); + } + + public int f() { + return this.art.C; + } + + public int i() { + return this.art.D; + } + + public void b(Entity entity) { + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + if (entityhuman.abilities.canInstantlyBuild) { + return; + } + } + + this.a(new ItemStack(Items.PAINTING), 0.0F); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityPig.java b/vspigot-server/src/main/java/net/minecraft/server/EntityPig.java new file mode 100644 index 0000000..f5b5109 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityPig.java @@ -0,0 +1,169 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityPig extends EntityAnimal { + + private final PathfinderGoalPassengerCarrotStick bp; + + public EntityPig(World world) { + super(world); + this.a(0.9F, 0.9F); + this.getNavigation().a(true); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.25D)); + this.goalSelector.a(2, this.bp = new PathfinderGoalPassengerCarrotStick(this, 0.3F)); + this.goalSelector.a(3, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(4, new PathfinderGoalTempt(this, 1.2D, Items.CARROT_STICK, false)); + this.goalSelector.a(4, new PathfinderGoalTempt(this, 1.2D, Items.CARROT, false)); + this.goalSelector.a(5, new PathfinderGoalFollowParent(this, 1.1D)); + this.goalSelector.a(6, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + } + + @Override + public void h() { + super.h(); + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + } + + public boolean bk() { + return true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.25D); + } + + protected void bn() { + super.bn(); + } + + public boolean bE() { + ItemStack itemstack = ((EntityHuman) this.passenger).be(); + + return itemstack != null && itemstack.getItem() == Items.CARROT_STICK; + } + + protected void c() { + super.c(); + this.datawatcher.a(16, Byte.valueOf((byte) 0)); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("Saddle", this.hasSaddle()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setSaddle(nbttagcompound.getBoolean("Saddle")); + } + + protected String t() { + return "mob.pig.say"; + } + + protected String aT() { + return "mob.pig.say"; + } + + protected String aU() { + return "mob.pig.death"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.pig.step", 0.15F, 1.0F); + } + + public boolean a(EntityHuman entityhuman) { + if (super.a(entityhuman)) { + return true; + } else if (this.hasSaddle() && !this.world.isStatic && (this.passenger == null || this.passenger == entityhuman)) { + entityhuman.mount(this); + return true; + } else { + return false; + } + } + + protected Item getLoot() { + return this.isBurning() ? Items.GRILLED_PORK : Items.PORK; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(3) + 1 + this.random.nextInt(1 + i); + + for (int k = 0; k < j; ++k) { + if (this.isBurning()) { + this.a(Items.GRILLED_PORK, 1); + } else { + this.a(Items.PORK, 1); + } + } + + if (this.hasSaddle()) { + this.a(Items.SADDLE, 1); + } + } + + public boolean hasSaddle() { + return (this.datawatcher.getByte(16) & 1) != 0; + } + + public void setSaddle(boolean flag) { + if (flag) { + this.datawatcher.watch(16, Byte.valueOf((byte) 1)); + } else { + this.datawatcher.watch(16, Byte.valueOf((byte) 0)); + } + } + + public void a(EntityLightning entitylightning) { + if (!this.world.isStatic) { + EntityPigZombie entitypigzombie = new EntityPigZombie(this.world); + + // CraftBukkit start + if (CraftEventFactory.callPigZapEvent(this, entitylightning, entitypigzombie).isCancelled()) { + return; + } + // CraftBukkit end + + entitypigzombie.setEquipment(0, new ItemStack(Items.GOLD_SWORD)); + entitypigzombie.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + // CraftBukkit - added a reason for spawning this creature + this.world.addEntity(entitypigzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); + this.die(); + } + } + + protected void b(float f) { + super.b(f); + if (f > 5.0F && this.passenger instanceof EntityHuman) { + ((EntityHuman) this.passenger).a((Statistic) AchievementList.u); + } + } + + public EntityPig b(EntityAgeable entityageable) { + return new EntityPig(this.world); + } + + public boolean c(ItemStack itemstack) { + return itemstack != null && itemstack.getItem() == Items.CARROT; + } + + public PathfinderGoalPassengerCarrotStick ca() { + return this.bp; + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.b(entityageable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityPigZombie.java b/vspigot-server/src/main/java/net/minecraft/server/EntityPigZombie.java new file mode 100644 index 0000000..0a765ad --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityPigZombie.java @@ -0,0 +1,161 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.UUID; + +import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + +public class EntityPigZombie extends EntityZombie { + + private static final UUID bq = UUID.fromString("49455A49-7EC5-45BA-B886-3B90B23A1718"); + private static final AttributeModifier br = (new AttributeModifier(bq, "Attacking speed boost", 0.45D, 0)).a(false); + public int angerLevel; // CraftBukkit - private -> public + private int soundDelay; + private Entity bu; + + public EntityPigZombie(World world) { + super(world); + this.fireProof = true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(bp).setValue(0.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.5D); + this.getAttributeInstance(GenericAttributes.e).setValue(5.0D); + } + + protected boolean bk() { + return false; + } + + public void h() { + if (this.bu != this.target && !this.world.isStatic) { + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.d); + + attributeinstance.b(br); + if (this.target != null) { + attributeinstance.a(br); + } + } + + this.bu = this.target; + if (this.soundDelay > 0 && --this.soundDelay == 0) { + this.makeSound("mob.zombiepig.zpigangry", this.bf() * 2.0F, ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) * 1.8F); + } + + super.h(); + } + + public boolean canSpawn() { + // MineHQ - Add mobsEnabled check. + return this.world.spigotConfig.mobsEnabled && this.world.difficulty != EnumDifficulty.PEACEFUL && this.world.b(this.boundingBox) && this.world.getCubes(this, this.boundingBox).isEmpty() && !this.world.containsLiquid(this.boundingBox); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setShort("Anger", (short) this.angerLevel); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.angerLevel = nbttagcompound.getShort("Anger"); + } + + protected Entity findTarget() { + return this.angerLevel == 0 ? null : super.findTarget(); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + Entity entity = damagesource.getEntity(); + + if (entity instanceof EntityHuman) { + List list = this.world.getEntities(this, this.boundingBox.grow(32.0D, 32.0D, 32.0D)); + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (entity1 instanceof EntityPigZombie) { + EntityPigZombie entitypigzombie = (EntityPigZombie) entity1; + + entitypigzombie.c(entity, EntityTargetEvent.TargetReason.PIG_ZOMBIE_TARGET); + } + } + + this.c(entity, EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY); + } + + return super.damageEntity(damagesource, f); + } + } + + // CraftBukkit start + private void c(Entity entity, EntityTargetEvent.TargetReason reason) { // add TargetReason + EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), entity.getBukkitEntity(), reason); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + if (event.getTarget() == null) { + this.target = null; + return; + } + entity = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); + // CraftBukkit end + + this.target = entity; + this.angerLevel = 400 + this.random.nextInt(400); + this.soundDelay = this.random.nextInt(40); + } + + protected String t() { + return "mob.zombiepig.zpig"; + } + + protected String aT() { + return "mob.zombiepig.zpighurt"; + } + + protected String aU() { + return "mob.zombiepig.zpigdeath"; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(2 + i); + + int k; + + for (k = 0; k < j; ++k) { + this.a(Items.ROTTEN_FLESH, 1); + } + + j = this.random.nextInt(2 + i); + + for (k = 0; k < j; ++k) { + this.a(Items.GOLD_NUGGET, 1); + } + } + + public boolean a(EntityHuman entityhuman) { + return false; + } + + protected void getRareDrop(int i) { + this.a(Items.GOLD_INGOT, 1); + } + + protected void bC() { + this.setEquipment(0, new ItemStack(Items.GOLD_SWORD)); + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + super.prepare(groupdataentity); + this.setVillager(false); + return groupdataentity; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityPlayer.java b/vspigot-server/src/main/java/net/minecraft/server/EntityPlayer.java new file mode 100644 index 0000000..59d9983 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +1,1378 @@ +package net.minecraft.server; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import net.minecraft.util.com.google.common.collect.HashMultimap; +import net.minecraft.util.com.google.common.collect.Sets; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.io.netty.buffer.Unpooled; +import net.minecraft.util.org.apache.commons.io.Charsets; +import net.valorhcf.block.BlockPosition; +import net.valorhcf.block.BlockPositionData; +import net.valorhcf.block.ChunkPosition; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.WeatherType; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +// CraftBukkit end +import org.bukkit.material.MaterialData; +import org.spigotmc.ProtocolData; // Spigot - protocol patch +import org.spigotmc.SpigotConfig; +import org.spigotmc.SpigotDebreakifier; + +public class EntityPlayer extends EntityHuman implements ICrafting { + + private static final Logger bL = LogManager.getLogger(); + public String locale = "en_US"; // Spigot + public PlayerConnection playerConnection; + public final MinecraftServer server; + public final PlayerInteractManager playerInteractManager; + public double d; + public double e; + public final List chunkCoordIntPairQueue = new LinkedList(); + public final Set paddingChunks = new HashSet(); // MineHQ + // public final List removeQueue = new LinkedList(); // CraftBukkit - private -> public // MineHQ + private final ServerStatisticManager bO; + private float bP = Float.MIN_VALUE; + private float bQ = -1.0E8F; + private int bR = -99999999; + private boolean bS = true; + public int lastSentExp = -99999999; // CraftBukkit - private -> public + public int invulnerableTicks = 60; // CraftBukkit - private -> public + private EnumChatVisibility bV; + private boolean bW = true; + private long bX = System.currentTimeMillis(); + private int containerCounter; + public boolean g; + public int ping; + public boolean viewingCredits; + // CraftBukkit start + public String displayName; + public String listName; + public org.bukkit.Location compassTarget; + public int newExp = 0; + public int newLevel = 0; + public int newTotalExp = 0; + public boolean keepLevel = false; + public double maxHealthCache; + public boolean joining = true; + public int lastPing = -1; // Spigot + + // CraftBukkit end + // Spigot start + public boolean collidesWithEntities = true; + public boolean allowServerSidePhase = false; + + private final ConcurrentMap fakeBlockMap = new ConcurrentHashMap<>(); + + @Override + public boolean R() + { + return this.collidesWithEntities && super.R(); // (first !this.isDead near bottom of EntityLiving) + } + + @Override + public boolean S() + { + return this.collidesWithEntities && super.S(); // (second !this.isDead near bottom of EntityLiving) + } + // Spigot end + + // MineHQ start + public int playerMapX; + public int playerMapZ; + // MineHQ end + + public EntityPlayer(MinecraftServer minecraftserver, WorldServer worldserver, GameProfile gameprofile, PlayerInteractManager playerinteractmanager) { + super(worldserver, gameprofile); + playerinteractmanager.player = this; + this.playerInteractManager = playerinteractmanager; + ChunkCoordinates chunkcoordinates = worldserver.getSpawn(); + int i = chunkcoordinates.x; + int j = chunkcoordinates.z; + int k = chunkcoordinates.y; + + if (!worldserver.worldProvider.g && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) { + int l = Math.max(5, minecraftserver.getSpawnProtection() - 6); + + i += this.random.nextInt(l * 2) - l; + j += this.random.nextInt(l * 2) - l; + k = worldserver.i(i, j); + } + + this.server = minecraftserver; + this.bO = minecraftserver.getPlayerList().a((EntityHuman) this); + this.W = 0.0F; + this.height = 0.0F; + this.setPositionRotation((double) i + 0.5D, (double) k, (double) j + 0.5D, 0.0F, 0.0F); + + while (!worldserver.getCubes(this, this.boundingBox).isEmpty()) { + this.setPosition(this.locX, this.locY + 1.0D, this.locZ); + } + + // CraftBukkit start + this.displayName = this.getName(); + this.listName = this.getName(); + // this.canPickUpLoot = true; TODO + this.maxHealthCache = this.getMaxHealth(); + // CraftBukkit end + + knockbackReduction = 0.2D; // Kohi + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("playerGameType", 99)) { + if (MinecraftServer.getServer().getForceGamemode()) { + this.playerInteractManager.setGameMode(MinecraftServer.getServer().getGamemode()); + } else { + this.playerInteractManager.setGameMode(EnumGamemode.getById(nbttagcompound.getInt("playerGameType"))); + } + } + this.getBukkitEntity().readExtraData(nbttagcompound); // CraftBukkit + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId()); + this.getBukkitEntity().setExtraData(nbttagcompound); // CraftBukkit + } + + // CraftBukkit start - World fallback code, either respawn location or global spawn + public void spawnIn(World world) { + super.spawnIn(world); + if (world == null) { + this.dead = false; + ChunkCoordinates position = null; + if (this.spawnWorld != null && !this.spawnWorld.equals("")) { + CraftWorld cworld = (CraftWorld) Bukkit.getServer().getWorld(this.spawnWorld); + if (cworld != null && this.getBed() != null) { + world = cworld.getHandle(); + position = EntityHuman.getBed(cworld.getHandle(), this.getBed(), false); + } + } + if (world == null || position == null) { + world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); + position = world.getSpawn(); + } + this.world = world; + this.setPosition(position.x + 0.5, position.y, position.z + 0.5); + } + this.dimension = ((WorldServer) this.world).dimension; + this.playerInteractManager.a((WorldServer) world); + } + // CraftBukkit end + + public void levelDown(int i) { + super.levelDown(i); + this.lastSentExp = -1; + } + + public void syncInventory() { + this.activeContainer.addSlotListener(this); + } + + protected void e_() { + this.height = 0.0F; + } + + public float getHeadHeight() { + return 1.62F; + } + + public void h() { + this.world.timings.entityPlayerTickNormal.startTiming(); // Poweruser + + // CraftBukkit start + if (this.joining) { + this.joining = false; + } + // CraftBukkit end + + this.playerInteractManager.a(); + --this.invulnerableTicks; + if (this.noDamageTicks > 0) { + --this.noDamageTicks; + } + + this.activeContainer.b(); + if (!this.world.isStatic && !this.activeContainer.a((EntityHuman) this)) { + this.closeInventory(); + this.activeContainer = this.defaultContainer; + } + + // MineHQ start - nope + /* + while (!this.removeQueue.isEmpty()) { + int i = Math.min(this.removeQueue.size(), 127); + int[] aint = new int[i]; + Iterator iterator = this.removeQueue.iterator(); + int j = 0; + + while (iterator.hasNext() && j < i) { + aint[j++] = ((Integer) iterator.next()).intValue(); + iterator.remove(); + } + + this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(aint)); + } + */ + // MineHQ end + + if (!this.chunkCoordIntPairQueue.isEmpty()) { + ArrayList arraylist = new ArrayList(); + Iterator iterator1 = this.chunkCoordIntPairQueue.iterator(); + ArrayList arraylist1 = new ArrayList(); + + Chunk chunk; + + while (iterator1.hasNext() && arraylist.size() < this.world.spigotConfig.maxBulkChunk) { // Spigot + ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator1.next(); + + if (chunkcoordintpair != null) { + if (this.world.isLoaded(chunkcoordintpair.x << 4, 0, chunkcoordintpair.z << 4)) { + chunk = this.world.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z); + if (chunk.isReady()) { + arraylist.add(chunk); + arraylist1.addAll(chunk.tileEntities.values()); // CraftBukkit - Get tile entities directly from the chunk instead of the world + iterator1.remove(); + } + } + } else { + iterator1.remove(); + } + } + + if (!arraylist.isEmpty()) { + // MineHQ start - if any real chunks overlap padding chunks, first send an unload then remove it from this player's padding list + for (Object o : arraylist) { + if (this.paddingChunks.isEmpty()) { + break; + } + Chunk c = (Chunk) o; + if (this.paddingChunks.contains(c.l())) { + this.paddingChunks.remove(c.l()); + this.playerConnection.sendPacket(PacketPlayOutMapChunk.unload(c.locX, c.locZ)); + } + } + // MineHQ end + this.playerConnection.sendPacket(new PacketPlayOutMapChunkBulk(arraylist, this.playerConnection.networkManager.getVersion())); // Spigot - protocol patch + Iterator iterator2 = arraylist1.iterator(); + + while (iterator2.hasNext()) { + TileEntity tileentity = (TileEntity) iterator2.next(); + + this.b(tileentity); + } + + // MineHQ start - nope + /* + iterator2 = arraylist.iterator(); + + while (iterator2.hasNext()) { + chunk = (Chunk) iterator2.next(); + this.r().getTracker().a(this, chunk); + } + */ + // MineHQ end + } + } + this.world.timings.entityPlayerTickNormal.stopTiming(); // Poweruser + } + + public void i() { + this.world.timings.entityPlayerTickOnMove.startTiming(); // Poweruser + try { + super.h(); + + if(this.world.spigotConfig.updateMapItemsInPlayerInventory) { // Poweruser + for (int i = 0; i < this.inventory.getSize(); ++i) { + ItemStack itemstack = this.inventory.getItem(i); + + if (itemstack != null && itemstack.getItem().h()) { + Packet packet = ((ItemWorldMapBase) itemstack.getItem()).c(itemstack, this.world, this); + + if (packet != null) { + this.playerConnection.sendPacket(packet); + } + } + } + } + + // CraftBukkit - Optionally scale health + if (this.getHealth() != this.bQ || this.bR != this.foodData.getFoodLevel() || this.foodData.getSaturationLevel() == 0.0F != this.bS) { + this.playerConnection.sendPacket(new PacketPlayOutUpdateHealth(this.getBukkitEntity().getScaledHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel())); + this.bQ = this.getHealth(); + this.bR = this.foodData.getFoodLevel(); + this.bS = this.foodData.getSaturationLevel() == 0.0F; + } + + if (this.getHealth() + this.getAbsorptionHearts() != this.bP) { + this.bP = this.getHealth() + this.getAbsorptionHearts(); + // CraftBukkit - Update ALL the scores! + this.world.getServer().getScoreboardManager().updateAllScoresForList(IScoreboardCriteria.f, this.getName(), com.google.common.collect.ImmutableList.of(this)); + } + + // CraftBukkit start - Force max health updates + if (this.maxHealthCache != this.getMaxHealth()) { + this.getBukkitEntity().updateScaledHealth(); + } + // CraftBukkit end + + if (this.expTotal != this.lastSentExp) { + this.lastSentExp = this.expTotal; + this.playerConnection.sendPacket(new PacketPlayOutExperience(this.exp, this.expTotal, this.expLevel)); + } + + if (false && this.ticksLived % 20 * 5 == 0 && !this.getStatisticManager().hasAchievement(AchievementList.L)) { // we don't care about this + this.j(); + } + + // CraftBukkit start - initialize oldLevel and fire PlayerLevelChangeEvent + if (this.oldLevel == -1) { + this.oldLevel = this.expLevel; + } + + if (this.oldLevel != this.expLevel) { + CraftEventFactory.callPlayerLevelChangeEvent(this.world.getServer().getPlayer((EntityPlayer) this), this.oldLevel, this.expLevel); + this.oldLevel = this.expLevel; + } + // CraftBukkit end + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Ticking player"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Player being ticked"); + + this.a(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + this.world.timings.entityPlayerTickOnMove.stopTiming(); // Poweruser + } + + protected void j() { + BiomeBase biomebase = this.world.getBiome(MathHelper.floor(this.locX), MathHelper.floor(this.locZ)); + + if (biomebase != null) { + String s = biomebase.af; + AchievementSet achievementset = (AchievementSet) this.getStatisticManager().b((Statistic) AchievementList.L); // CraftBukkit - fix decompile error + + if (achievementset == null) { + achievementset = (AchievementSet) this.getStatisticManager().a(AchievementList.L, new AchievementSet()); + } + + achievementset.add(s); + if (this.getStatisticManager().b(AchievementList.L) && achievementset.size() == BiomeBase.n.size()) { + HashSet hashset = Sets.newHashSet(BiomeBase.n); + Iterator iterator = achievementset.iterator(); + + while (iterator.hasNext()) { + String s1 = (String) iterator.next(); + Iterator iterator1 = hashset.iterator(); + + while (iterator1.hasNext()) { + BiomeBase biomebase1 = (BiomeBase) iterator1.next(); + + if (biomebase1.af.equals(s1)) { + iterator1.remove(); + } + } + + if (hashset.isEmpty()) { + break; + } + } + + if (hashset.isEmpty()) { + this.a((Statistic) AchievementList.L); + } + } + } + } + + public void die(DamageSource damagesource) { + // CraftBukkit start - fire PlayerDeathEvent + if (this.dead) { + return; + } + + java.util.List loot = new java.util.ArrayList(); + boolean keepInventory = this.world.getGameRules().getBoolean("keepInventory"); + + if (!keepInventory) { + for (int i = 0; i < this.inventory.items.length; ++i) { + if (this.inventory.items[i] != null) { + loot.add(CraftItemStack.asCraftMirror(this.inventory.items[i])); + } + } + + for (int i = 0; i < this.inventory.armor.length; ++i) { + if (this.inventory.armor[i] != null) { + loot.add(CraftItemStack.asCraftMirror(this.inventory.armor[i])); + } + } + } + + IChatBaseComponent chatmessage = this.aW().b(); + + String deathmessage = chatmessage.c(); + org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory); + + String deathMessage = event.getDeathMessage(); + + if (deathMessage != null && deathMessage.length() > 0) { + if (deathMessage.equals(deathmessage)) { + this.server.getPlayerList().sendMessage(chatmessage); + } else { + this.server.getPlayerList().sendMessage(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(deathMessage)); + } + } + + // we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory. + if (!event.getKeepInventory()) { + for (int i = 0; i < this.inventory.items.length; ++i) { + this.inventory.setItem(i, null); + } + + for (int i = 0; i < this.inventory.armor.length; ++i) { + this.inventory.player.setEquipment(i, null); + } + } + + this.closeInventory(); + // CraftBukkit end + + // CraftBukkit - Get our scores instead + Collection collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.c, this.getName(), new java.util.ArrayList()); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); // CraftBukkit - Use our scores instead + + scoreboardscore.incrementScore(); + } + + EntityLiving entityliving = this.aX(); + + if (entityliving != null) { + int i = EntityTypes.a(entityliving); + MonsterEggInfo monsteregginfo = (MonsterEggInfo) EntityTypes.eggInfo.get(Integer.valueOf(i)); + + if (monsteregginfo != null) { + this.a(monsteregginfo.e, 1); + } + + entityliving.b(this, this.ba); + } + + this.a(StatisticList.v, 1); + this.aW().g(); + + // Griffin start - Instant respawn + if (getBukkitEntity().isOnline() && SpigotConfig.instantRespawn) { + // exp start - we have to handle it here because that's handled somewhere else (while the entity is still dead, which doesn't happen with this) + // code borrowed from EntityLiving + int i; + i = this.expToDrop; + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); + + i -= j; + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j)); + } + this.expToDrop = 0; + // exp end + + ChunkCoordinates chunkcoordinates = getBed(); + ChunkCoordinates chunkcoordinates1; + + Location location = null; + + boolean isBedSpawn = false; + CraftWorld cworld = (CraftWorld) this.server.server.getWorld(spawnWorld); + if (cworld != null && chunkcoordinates != null) { + chunkcoordinates1 = EntityHuman.getBed(cworld.getHandle(), chunkcoordinates, isRespawnForced()); + if (chunkcoordinates1 != null) { + isBedSpawn = true; + location = new Location(cworld, chunkcoordinates1.x + 0.5, chunkcoordinates1.y, chunkcoordinates1.z + 0.5); + } else { + setRespawnPosition(null, true); + playerConnection.sendPacket(new PacketPlayOutGameStateChange(0, 0)); + } + } + + if (location == null) { + cworld = (CraftWorld) this.server.server.getWorlds().get(0); + chunkcoordinates = cworld.getHandle().getSpawn(); + + location = new Location(cworld, chunkcoordinates.x + 0.5, chunkcoordinates.y, chunkcoordinates.z + 0.5, cworld.getHandle().getWorldData().getSpawnYaw(), cworld.getHandle().getWorldData().getSpawnPitch()); // Poweruser + } + + Player respawnPlayer = getBukkitEntity(); + PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn); + Bukkit.getPluginManager().callEvent(respawnEvent); + + if (playerConnection.isDisconnected()) { + return; + } + + location = respawnEvent.getRespawnLocation(); + reset(); + + getBukkitEntity().teleport(location); + + dead = false; + } + // Griffin end - Instant respawn + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + // CraftBukkit - this.server.getPvP() -> this.world.pvpMode + boolean flag = this.server.X() && this.world.pvpMode && "fall".equals(damagesource.translationIndex); + + if (!flag && this.invulnerableTicks > 0 && damagesource != DamageSource.OUT_OF_WORLD) { + return false; + } else { + if (damagesource instanceof EntityDamageSource) { + Entity entity = damagesource.getEntity(); + + if (entity instanceof EntityHuman && !this.a((EntityHuman) entity)) { + return false; + } + + if (entity instanceof EntityArrow) { + EntityArrow entityarrow = (EntityArrow) entity; + + if (entityarrow.shooter instanceof EntityHuman && !this.a((EntityHuman) entityarrow.shooter)) { + return false; + } + } + } + + return super.damageEntity(damagesource, f); + } + } + } + + public boolean a(EntityHuman entityhuman) { + // CraftBukkit - this.server.getPvP() -> this.world.pvpMode + return !this.world.pvpMode ? false : super.a(entityhuman); + } + + public void b(int i) { + // PaperSpigot start - Allow configurable end portal credits + boolean endPortal = this.dimension == 1 && i == 1; + if (endPortal) { + this.a((Statistic) AchievementList.D); + if (!world.paperSpigotConfig.disableEndCredits) { + this.world.kill(this); + this.viewingCredits = true; + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(4, 0.0F)); + } + // PaperSpigot end + } else { + if (this.dimension == 0 && i == 1) { + this.a((Statistic) AchievementList.C); + // CraftBukkit start - Rely on custom portal management + /* + ChunkCoordinates chunkcoordinates = this.server.getWorldServer(i).getDimensionSpawn(); + + if (chunkcoordinates != null) { + this.playerConnection.a((double) chunkcoordinates.x, (double) chunkcoordinates.y, (double) chunkcoordinates.z, 0.0F, 0.0F); + } + + i = 1; + */ + // CraftBukkit end + } else { + this.a((Statistic) AchievementList.y); + } + } + + // PaperSpigot start - Allow configurable end portal credits + if (!endPortal || world.paperSpigotConfig.disableEndCredits) { + // CraftBukkit start + TeleportCause cause = (endPortal || (this.dimension == 1 || i == 1)) ? TeleportCause.END_PORTAL : TeleportCause.NETHER_PORTAL; + this.server.getPlayerList().changeDimension(this, i, cause); + // CraftBukkit end + this.lastSentExp = -1; + this.bQ = -1.0F; + this.bR = -1; + } + // PaperSpigot end + } + + private void b(TileEntity tileentity) { + if (tileentity != null) { + Packet packet = tileentity.getUpdatePacket(); + + if (packet != null) { + this.playerConnection.sendPacket(packet); + } + } + } + + public void receive(Entity entity, int i) { + super.receive(entity, i); + this.activeContainer.b(); + } + + public EnumBedResult a(int i, int j, int k) { + EnumBedResult enumbedresult = super.a(i, j, k); + + if (enumbedresult == EnumBedResult.OK) { + PacketPlayOutBed packetplayoutbed = new PacketPlayOutBed(this, i, j, k); + + this.r().getTracker().a((Entity) this, (Packet) packetplayoutbed); + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + this.playerConnection.sendPacket(packetplayoutbed); + } + + return enumbedresult; + + } + + + public ConcurrentMap getFakeBlocks() { + return this.fakeBlockMap; + } + + public void clearFakeBlocks(final boolean send) { + if (send) { + this.setFakeBlocks(Collections.emptyMap(), new ArrayList<>(this.fakeBlockMap.keySet()), true); + } + else { + this.fakeBlockMap.clear(); + } + } + + public void setFakeBlocks(final Map blockMap, final Collection replace, final boolean send) { + if (this.playerConnection != null) { + final HashMultimap map = HashMultimap.create(); + for (final Map.Entry entry : blockMap.entrySet()) { + final BlockPosition blockPosition = entry.getKey(); + MaterialData materialData = entry.getValue(); + if (materialData == null) { + materialData = new MaterialData(0); + } + final MaterialData previous = this.fakeBlockMap.put(blockPosition, materialData); + if (send && previous != materialData) { + int x = blockPosition.getX(); + int y = blockPosition.getY(); + int z = blockPosition.getZ(); + int chunkX = x >> 4; + int chunkZ = z >> 4; + int posX = x - (chunkX << 4); + int posZ = z - (chunkZ << 4); + map.put(new net.valorhcf.block.ChunkPosition(chunkX, chunkZ), new BlockPositionData(new BlockPosition(posX, y, posZ), materialData)); + } + } + for (final BlockPosition blockPosition2 : replace) { + if (this.fakeBlockMap.remove(blockPosition2) != null) { + int x2 = blockPosition2.getX(); + int y2 = blockPosition2.getY(); + int z2 = blockPosition2.getZ(); + int type = CraftMagicNumbers.getId(this.world.getType(blockPosition2.getX(), blockPosition2.getY(), blockPosition2.getZ())); + int data = this.world.getData(blockPosition2.getX(), blockPosition2.getY(), blockPosition2.getZ()); + int chunkX2 = x2 >> 4; + int chunkZ2 = z2 >> 4; + int posX2 = x2 - (chunkX2 << 4); + int posZ2 = z2 - (chunkZ2 << 4); + map.put(new net.valorhcf.block.ChunkPosition(chunkX2, chunkZ2), new BlockPositionData(new BlockPosition(posX2, y2, posZ2), new MaterialData(type, (byte) data))); + } + } + if (send) { + for ( Map.Entry> entry2 : map.asMap().entrySet()) { + net.valorhcf.block.ChunkPosition chunkPosition = entry2.getKey(); + Collection blocks = entry2.getValue(); + int dirtyCount = blocks.size(); + ByteArrayDataOutput byteArrayDataOutput = ByteStreams.newDataOutput(); + short[] dirtData = new short[dirtyCount]; + int[] blockData = new int[dirtyCount]; + int i = 0; + for (final BlockPositionData blockPositionData : blocks) { + final BlockPosition blockPosition3 = blockPositionData.getBlockPosition(); + final MaterialData materialData2 = blockPositionData.getMaterialData(); + final int blockId = materialData2.getItemTypeId(); + int data2 = materialData2.getData(); + data2 = SpigotDebreakifier.getCorrectedData(blockId, data2); + final short dirty = (short)(blockPosition3.getX() << 12 | blockPosition3.getZ() << 8 | blockPosition3.getY()); + final int blocky = (blockId & 0xFFF) << 4 | (data2 & 0xF); + dirtData[i] = dirty; + blockData[i] = blocky; + byteArrayDataOutput.writeShort(dirty); + byteArrayDataOutput.writeShort((short)blocky); + ++i; + } + final PacketPlayOutMultiBlockChange packetPlayOutMultiBlockChange = PacketPlayOutMultiBlockChange. + create(new ChunkCoordIntPair(chunkPosition.getX(), chunkPosition.getZ()), byteArrayDataOutput.toByteArray(), dirtyCount, dirtData, blockData); + this.playerConnection.sendPacket(packetPlayOutMultiBlockChange); + } + } + } + } + + public void a(boolean flag, boolean flag1, boolean flag2) { + if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one! + + if (this.isSleeping()) { + this.r().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(this, 2)); + } + + super.a(flag, flag1, flag2); + if (this.playerConnection != null) { + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + } + } + + public void mount(Entity entity) { + // CraftBukkit start + this.setPassengerOf(entity); + } + + public void setPassengerOf(Entity entity) { + // mount(null) doesn't really fly for overloaded methods, + // so this method is needed + Entity currentVehicle = this.vehicle; + + super.setPassengerOf(entity); + + // Check if the vehicle actually changed. + if (currentVehicle != this.vehicle) { + this.playerConnection.sendPacket(new PacketPlayOutAttachEntity(0, this, this.vehicle)); + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + // MineHQ start + if (this.vehicle instanceof EntityLiving) { + AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.vehicle).getAttributeMap(); + Collection collection = attributemapserver.c(); + if (!collection.isEmpty()) { + this.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.vehicle.getId(), collection)); + } + } + // MineHQ end + } + // CraftBukkit end + } + + protected void a(double d0, boolean flag) {} + + public void b(double d0, boolean flag) { + super.a(d0, flag); + } + + public void a(TileEntity tileentity) { + if (tileentity instanceof TileEntitySign) { + ((TileEntitySign) tileentity).a((EntityHuman) this); + this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentity.x, tileentity.y, tileentity.z)); + } + } + + public int nextContainerCounter() { // CraftBukkit - private void -> public int + this.containerCounter = this.containerCounter % 100 + 1; + return this.containerCounter; // CraftBukkit + } + + public void startCrafting(int i, int j, int k) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerWorkbench(this.inventory, this.world, i, j, k)); + if (container == null) { + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 1, "Crafting", 0, true)); // Spigot - protocol patch + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void startEnchanting(int i, int j, int k, String s) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerEnchantTable(this.inventory, this.world, i, j, k)); + if (container == null) { + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 4, s == null ? "" : s, 0, s != null)); // Spigot - protocol patch + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openAnvil(int i, int j, int k) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerAnvil(this.inventory, this.world, i, j, k, this)); + if (container == null) { + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 8, "Repairing", 0, true)); // Spigot - protocol patch + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openContainer(IInventory iinventory) { + if (this.activeContainer != this.defaultContainer) { + this.closeInventory(); + } + + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerChest(this.inventory, iinventory)); + if (container == null) { + iinventory.closeContainer(); + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 0, iinventory.getInventoryName(), iinventory.getSize(), iinventory.k_())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openHopper(TileEntityHopper tileentityhopper) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHopper(this.inventory, tileentityhopper)); + if (container == null) { + tileentityhopper.closeContainer(); + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 9, tileentityhopper.getInventoryName(), tileentityhopper.getSize(), tileentityhopper.k_())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openMinecartHopper(EntityMinecartHopper entityminecarthopper) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHopper(this.inventory, entityminecarthopper)); + if (container == null) { + entityminecarthopper.closeContainer(); + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 9, entityminecarthopper.getInventoryName(), entityminecarthopper.getSize(), entityminecarthopper.k_())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openFurnace(TileEntityFurnace tileentityfurnace) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerFurnace(this.inventory, tileentityfurnace)); + if (container == null) { + tileentityfurnace.closeContainer(); + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 2, tileentityfurnace.getInventoryName(), tileentityfurnace.getSize(), tileentityfurnace.k_())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openDispenser(TileEntityDispenser tileentitydispenser) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerDispenser(this.inventory, tileentitydispenser)); + if (container == null) { + tileentitydispenser.closeContainer(); + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, tileentitydispenser instanceof TileEntityDropper ? 10 : 3, tileentitydispenser.getInventoryName(), tileentitydispenser.getSize(), tileentitydispenser.k_())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openBrewingStand(TileEntityBrewingStand tileentitybrewingstand) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerBrewingStand(this.inventory, tileentitybrewingstand)); + if (container == null) { + tileentitybrewingstand.closeContainer(); + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 5, tileentitybrewingstand.getInventoryName(), tileentitybrewingstand.getSize(), tileentitybrewingstand.k_())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openBeacon(TileEntityBeacon tileentitybeacon) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerBeacon(this.inventory, tileentitybeacon)); + if (container == null) { + tileentitybeacon.closeContainer(); + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 7, tileentitybeacon.getInventoryName(), tileentitybeacon.getSize(), tileentitybeacon.k_())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openTrade(IMerchant imerchant, String s) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world)); + if (container == null) { + return; + } + // CraftBukkit end + + this.nextContainerCounter(); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + InventoryMerchant inventorymerchant = ((ContainerMerchant) this.activeContainer).getMerchantInventory(); + + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 6, s == null ? "" : s, inventorymerchant.getSize(), s != null)); + MerchantRecipeList merchantrecipelist = imerchant.getOffers(this); + + if (merchantrecipelist != null) { + PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer(), playerConnection.networkManager.getVersion()); // Spigot + + try { + packetdataserializer.writeInt(this.containerCounter); + merchantrecipelist.a(packetdataserializer); + this.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|TrList", packetdataserializer)); + } catch (Exception ioexception) { // CraftBukkit - IOException -> Exception + bL.error("Couldn\'t send trade list", ioexception); + } finally { + packetdataserializer.release(); + } + } + } + + public void openHorseInventory(EntityHorse entityhorse, IInventory iinventory) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorse)); + if (container == null) { + iinventory.closeContainer(); + return; + } + // CraftBukkit end + + if (this.activeContainer != this.defaultContainer) { + this.closeInventory(); + } + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, 11, iinventory.getInventoryName(), iinventory.getSize(), iinventory.k_(), entityhorse.getId())); + this.activeContainer = container; // CraftBukkit - Use container we passed to event + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void a(Container container, int i, ItemStack itemstack) { + if (!(container.getSlot(i) instanceof SlotResult)) { + if (!this.g) { + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(container.windowId, i, itemstack)); + } + } + } + + public void updateInventory(Container container) { + this.a(container, container.a()); + } + + public void a(Container container, List list) { + this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, list)); + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried())); + // CraftBukkit start - Send a Set Slot to update the crafting result slot + if (java.util.EnumSet.of(InventoryType.CRAFTING,InventoryType.WORKBENCH).contains(container.getBukkitView().getType())) { + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(container.windowId, 0, container.getSlot(0).getItem())); + } + // CraftBukkit end + } + + public void setContainerData(Container container, int i, int j) { + // Spigot start - protocol patch + if ( container instanceof ContainerFurnace && playerConnection.networkManager.getVersion() >= 47 ) + { + switch ( i ) { + case 0: + i = 2; + this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, 3, 200)); + break; + case 1: + i = 0; + break; + case 2: + i = 1; + } + } + // Spigot end + this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, j)); + } + + public void closeInventory() { + CraftEventFactory.handleInventoryCloseEvent(this); // CraftBukkit + this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId)); + this.m(); + } + + public void broadcastCarriedItem() { + if (!this.g) { + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried())); + } + } + + public void m() { + this.activeContainer.b((EntityHuman) this); + this.activeContainer = this.defaultContainer; + } + + public void a(float f, float f1, boolean flag, boolean flag1) { + if (this.vehicle != null) { + if (f >= -1.0F && f <= 1.0F) { + this.bd = f; + } + + if (f1 >= -1.0F && f1 <= 1.0F) { + this.be = f1; + } + + this.bc = flag; + this.setSneaking(flag1); + } + } + + public void a(Statistic statistic, int i) { + if (true) return; // we don't care about statistics + if (statistic != null) { + this.bO.b(this, statistic, i); + Iterator iterator = this.getScoreboard().getObjectivesForCriteria(statistic.k()).iterator(); + + while (iterator.hasNext()) { + ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next(); + + this.getScoreboard().getPlayerScoreForObjective(this.getName(), scoreboardobjective).incrementScore(); + } + + if (this.bO.e()) { + this.bO.a(this); + } + } + } + + public void n() { + if (this.passenger != null) { + this.passenger.mount(this); + } + + if (this.sleeping) { + this.a(true, false, false); + } + } + + public void triggerHealthUpdate() { + this.bQ = -1.0E8F; + this.lastSentExp = -1; // CraftBukkit - Added to reset + } + + public void b(IChatBaseComponent ichatbasecomponent) { + this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent)); + } + + protected void p() { + this.playerConnection.sendPacket(new PacketPlayOutEntityStatus(this, (byte) 9)); + super.p(); + } + + public void a(ItemStack itemstack, int i) { + super.a(itemstack, i); + if (itemstack != null && itemstack.getItem() != null && itemstack.getItem().d(itemstack) == EnumAnimation.EAT) { + this.r().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(this, 3)); + } + } + + public void copyTo(EntityHuman entityhuman, boolean flag) { + super.copyTo(entityhuman, flag); + this.lastSentExp = -1; + this.bQ = -1.0F; + this.bR = -1; + // this.removeQueue.addAll(((EntityPlayer) entityhuman).removeQueue); // MineHQ + } + + protected void a(MobEffect mobeffect) { + super.a(mobeffect); + if (this.playerConnection != null) { + this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect)); + } + } + + protected void a(MobEffect mobeffect, boolean flag) { + super.a(mobeffect, flag); + + if (this.playerConnection != null) { + this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect)); + } + } + + protected void b(MobEffect mobeffect) { + super.b(mobeffect); + + if (this.playerConnection != null) { + this.playerConnection.sendPacket(new PacketPlayOutRemoveEntityEffect(this.getId(), mobeffect)); + } + } + + public void enderTeleportTo(double d0, double d1, double d2) { + this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch); + } + + public void b(Entity entity) { + this.r().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 4)); + } + + public void c(Entity entity) { + this.r().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 5)); + } + + public void updateAbilities() { + if (this.playerConnection != null) { + this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities)); + } + } + + public WorldServer r() { + return (WorldServer) this.world; + } + + public void a(EnumGamemode enumgamemode) { + this.playerInteractManager.setGameMode(enumgamemode); + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, (float) enumgamemode.getId())); + } + + // CraftBukkit start - Support multi-line messages + public void sendMessage(IChatBaseComponent[] ichatbasecomponent) { + for (IChatBaseComponent component : ichatbasecomponent) { + this.sendMessage(component); + } + } + // CraftBukkit end + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent)); + } + + public boolean a(int i, String s) { + if ("seed".equals(s) && !this.server.X()) { + return true; + } else if (!"tell".equals(s) && !"help".equals(s) && !"me".equals(s)) { + if (this.server.getPlayerList().isOp(this.getProfile())) { + OpListEntry oplistentry = (OpListEntry) this.server.getPlayerList().getOPs().get(this.getProfile()); + + return oplistentry != null ? oplistentry.a() >= i : this.server.l() >= i; + } else { + return false; + } + } else { + return true; + } + } + + public String s() { + String s = this.playerConnection.networkManager.getSocketAddress().toString(); + + s = s.substring(s.indexOf("/") + 1); + s = s.substring(0, s.indexOf(":")); + return s; + } + + public void a(PacketPlayInSettings packetplayinsettings) { + this.locale = packetplayinsettings.c(); + int i = 256 >> packetplayinsettings.d(); + + if (i > 3 && i < 20) { + ; + } + + this.bV = packetplayinsettings.e(); + this.bW = packetplayinsettings.f(); + if (this.server.N() && this.server.M().equals(this.getName())) { + this.server.a(packetplayinsettings.g()); + } + + // Spigot start - protocol patch, handle metadata usage change (show cape -> collisions) + if (packetplayinsettings.version < 16) + { + this.b( 1, !packetplayinsettings.h(), packetplayinsettings.version ); + } else + { + this.b( 1, false, packetplayinsettings.version ); + datawatcher.watch( 10, new ProtocolData.HiddenByte( (byte) packetplayinsettings.flags ) ); + } + // Spigot end + } + + public EnumChatVisibility getChatFlags() { + return this.bV; + } + + public void setResourcePack(String s) { + this.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|RPack", s.getBytes(Charsets.UTF_8))); + // Spigot start - protocol patch + if ( playerConnection.networkManager.getVersion() >= 36 ) + { + playerConnection.sendPacket( new org.spigotmc.ProtocolInjector.PacketPlayResourcePackSend( s, "thinkislazy" ) ); + } + // Spigot end + } + + public ChunkCoordinates getChunkCoordinates() { + return new ChunkCoordinates(MathHelper.floor(this.locX), MathHelper.floor(this.locY + 0.5D), MathHelper.floor(this.locZ)); + } + + public void v() { + this.bX = MinecraftServer.ar(); + } + + public ServerStatisticManager getStatisticManager() { + return this.bO; + } + + public void d(Entity entity) { + this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(new int[] { entity.getId() })); // MineHQ + } + + public long x() { + return this.bX; + } + + // CraftBukkit start - Add per-player time and weather. + public long timeOffset = 0; + public boolean relativeTime = true; + + public long getPlayerTime() { + if (this.relativeTime) { + // Adds timeOffset to the current server time. + return this.world.getDayTime() + this.timeOffset; + } else { + // Adds timeOffset to the beginning of this day. + return this.world.getDayTime() - (this.world.getDayTime() % 24000) + this.timeOffset; + } + } + + public WeatherType weather = null; + + public WeatherType getPlayerWeather() { + return this.weather; + } + + public void setPlayerWeather(WeatherType type, boolean plugin) { + if (!plugin && this.weather != null) { + return; + } + + if (plugin) { + this.weather = type; + } + + if (type == WeatherType.DOWNFALL) { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(2, 0)); + // this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, this.world.j(1.0F))); + // this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, this.world.h(1.0F))); + } else { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1, 0)); + } + } + + public void resetPlayerWeather() { + this.weather = null; + this.setPlayerWeather(this.world.getWorldData().hasStorm() ? WeatherType.DOWNFALL : WeatherType.CLEAR, false); + } + + @Override + public String toString() { + return super.toString() + "(" + this.getName() + " at " + this.locX + "," + this.locY + "," + this.locZ + ")"; + } + + public void reset() { + float exp = 0; + boolean keepInventory = this.world.getGameRules().getBoolean("keepInventory"); + + if (this.keepLevel || keepInventory) { + exp = this.exp; + this.newTotalExp = this.expTotal; + this.newLevel = this.expLevel; + } + + this.setHealth(this.getMaxHealth()); + this.fireTicks = 0; + this.fallDistance = 0; + this.foodData = new FoodMetaData(this); + this.expLevel = this.newLevel; + this.expTotal = this.newTotalExp; + this.exp = 0; + this.deathTicks = 0; + this.removeAllEffects(); + this.updateEffects = true; + this.activeContainer = this.defaultContainer; + this.killer = null; + this.lastDamager = null; + this.combatTracker = new CombatTracker(this); + this.lastSentExp = -1; + if (this.keepLevel || keepInventory) { + this.exp = exp; + } else { + this.giveExp(this.newExp); + } + this.keepLevel = false; + } + + @Override + public CraftPlayer getBukkitEntity() { + return (CraftPlayer) super.getBukkitEntity(); + } + // CraftBukkit end + + // MineHQ start - Disguises + public GameProfile getDisguiseProfile() { + if (getBukkitEntity().isDisguised()) { + return getBukkitEntity().disguisedProfile; + } + + return getProfile(); + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityPotion.java b/vspigot-server/src/main/java/net/minecraft/server/EntityPotion.java new file mode 100644 index 0000000..69db29f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityPotion.java @@ -0,0 +1,157 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import java.util.HashMap; + +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; +// CraftBukkit end + +public class EntityPotion extends EntityProjectile { + + public ItemStack item; // CraftBukkit private -> public + + public EntityPotion(World world) { + super(world); + } + + public EntityPotion(World world, EntityLiving entityliving, int i) { + this(world, entityliving, new ItemStack(Items.POTION, 1, i)); + } + + public EntityPotion(World world, EntityLiving entityliving, ItemStack itemstack) { + super(world, entityliving); + this.item = itemstack; + } + + public EntityPotion(World world, double d0, double d1, double d2, ItemStack itemstack) { + super(world, d0, d1, d2); + this.item = itemstack; + } + + protected float i() { + return 0.05F; + } + + protected float e() { + return 0.5F; + } + + protected float f() { + return -20.0F; + } + + public void setPotionValue(int i) { + if (this.item == null) { + this.item = new ItemStack(Items.POTION, 1, 0); + } + + this.item.setData(i); + } + + public int getPotionValue() { + if (this.item == null) { + this.item = new ItemStack(Items.POTION, 1, 0); + } + + return this.item.getData(); + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isStatic) { + List list = Items.POTION.g(this.item); + + if (true || list != null && !list.isEmpty()) { // CraftBukkit - Call event even if no effects to apply + AxisAlignedBB axisalignedbb = this.boundingBox.grow(4.0D, 2.0D, 4.0D); + List list1 = this.world.a(EntityLiving.class, axisalignedbb); + + if (list1 != null) { // CraftBukkit - Run code even if there are no entities around + Iterator iterator = list1.iterator(); + + // CraftBukkit + HashMap affected = new HashMap(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + double d0 = this.f(entityliving); + + if (d0 < 16.0D) { + double d1 = 1.0D - Math.sqrt(d0) / 4.0D; + + if (entityliving == movingobjectposition.entity) { + d1 = 1.0D; + } + + // CraftBukkit start + affected.put((LivingEntity) entityliving.getBukkitEntity(), d1); + } + } + + org.bukkit.event.entity.PotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPotionSplashEvent(this, affected); + if (!event.isCancelled() && list != null && !list.isEmpty()) { // do not process effects if there are no effects to process + for (LivingEntity victim : event.getAffectedEntities()) { + if (!(victim instanceof CraftLivingEntity)) { + continue; + } + + EntityLiving entityliving = ((CraftLivingEntity) victim).getHandle(); + double d1 = event.getIntensity(victim); + // CraftBukkit end + + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator1.next(); + int i = mobeffect.getEffectId(); + + // CraftBukkit start - Abide by PVP settings - for players only! + if (!this.world.pvpMode && this.getShooter() instanceof EntityPlayer && entityliving instanceof EntityPlayer && entityliving != this.getShooter()) { + // Block SLOWER_MOVEMENT, SLOWER_DIG, HARM, BLINDNESS, HUNGER, WEAKNESS and POISON potions + if (i == 2 || i == 4 || i == 7 || i == 15 || i == 17 || i == 18 || i == 19) continue; + } + // CraftBukkit end + + if (MobEffectList.byId[i].isInstant()) { + // CraftBukkit - Added 'this' + MobEffectList.byId[i].applyInstantEffect(this.getShooter(), entityliving, mobeffect.getAmplifier(), d1, this); + } else { + int j = (int) (d1 * (double) mobeffect.getDuration() + 0.5D); + + if (j > 20) { + entityliving.addEffect(new MobEffect(i, j, mobeffect.getAmplifier())); + } + } + } + } + } + } + } + + this.world.triggerEffect(2002, (int) Math.round(this.locX), (int) Math.round(this.locY), (int) Math.round(this.locZ), this.getPotionValue()); + this.die(); + } + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("Potion", 10)) { + this.item = ItemStack.createStack(nbttagcompound.getCompound("Potion")); + } else { + this.setPotionValue(nbttagcompound.getInt("potionValue")); + } + + if (this.item == null) { + this.die(); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.item != null) { + nbttagcompound.set("Potion", this.item.save(new NBTTagCompound())); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityProjectile.java b/vspigot-server/src/main/java/net/minecraft/server/EntityProjectile.java new file mode 100644 index 0000000..dcc55ed --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityProjectile.java @@ -0,0 +1,260 @@ +package net.minecraft.server; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.spigotmc.SpigotConfig; + +public abstract class EntityProjectile extends Entity implements IProjectile { + + private int blockX = -1; + private int blockY = -1; + private int blockZ = -1; + private Block inBlockId; + protected boolean inGround; + public int shake; + public EntityLiving shooter; // CraftBukkit - private -> public + public String shooterName; // CraftBukkit - private -> public + private int i; + private int at; + + public EntityProjectile(World world) { + super(world); + this.a(0.25F, 0.25F); + } + + protected void c() {} + + public EntityProjectile(World world, EntityLiving entityliving) { + super(world); + this.shooter = entityliving; + this.projectileSource = (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + this.a(0.25F, 0.25F); + this.setPositionRotation(entityliving.locX, entityliving.locY + (double) entityliving.getHeadHeight(), entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.locX -= (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * 0.16F); + this.locY -= 0.10000000149011612D; + this.locZ -= (double) (MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * 0.16F); + this.setPosition(this.locX, this.locY, this.locZ); + this.height = 0.0F; + float f = 0.4F; + + this.motX = (double) (-MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F) * f); + this.motZ = (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F) * f); + this.motY = (double) (-MathHelper.sin((this.pitch + this.f()) / 180.0F * 3.1415927F) * f); + this.shoot(this.motX, this.motY, this.motZ, this.e(), 1.0F); + } + + public EntityProjectile(World world, double d0, double d1, double d2) { + super(world); + this.i = 0; + this.a(0.25F, 0.25F); + this.setPosition(d0, d1, d2); + this.height = 0.0F; + } + + protected float e() { + return 1.5F; + } + + protected float f() { + return 0.0F; + } + + public void shoot(double d0, double d1, double d2, float f, float f1) { + float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d0 /= (double) f2; + d1 /= (double) f2; + d2 /= (double) f2; + d0 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d1 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d2 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d0 *= (double) f; + d1 *= (double) f; + d2 *= (double) f; + this.motX = d0; + this.motY = d1; + this.motZ = d2; + float f3 = MathHelper.sqrt(d0 * d0 + d2 * d2); + + this.lastYaw = this.yaw = (float) (Math.atan2(d0, d2) * 180.0D / 3.1415927410125732D); + this.lastPitch = this.pitch = (float) (Math.atan2(d1, (double) f3) * 180.0D / 3.1415927410125732D); + this.i = 0; + } + + public void h() { + this.S = this.locX; + this.T = this.locY; + this.U = this.locZ; + super.h(); + if (this.shake > 0) { + --this.shake; + } + + if (this.inGround) { + if (this.world.getType(this.blockX, this.blockY, this.blockZ) == this.inBlockId) { + ++this.i; + if (this.i == 1200) { + this.die(); + } + + return; + } + + this.inGround = false; + this.motX *= (double) (this.random.nextFloat() * 0.2F); + this.motY *= (double) (this.random.nextFloat() * 0.2F); + this.motZ *= (double) (this.random.nextFloat() * 0.2F); + this.i = 0; + this.at = 0; + } else { + ++this.at; + } + + Vec3D vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.a(vec3d, vec3d1); + + vec3d = Vec3D.a(this.locX, this.locY, this.locZ); + vec3d1 = Vec3D.a(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + if (movingobjectposition != null) { + vec3d1 = Vec3D.a(movingobjectposition.pos.a, movingobjectposition.pos.b, movingobjectposition.pos.c); + } + + if (!this.world.isStatic) { + Entity entity = null; + List list = this.world.getEntities(this, this.boundingBox.a(this.motX, this.motY, this.motZ).grow(1.0D, 1.0D, 1.0D)); + double d0 = 0.0D; + EntityLiving entityliving = this.getShooter(); + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (entity1.R() && (entity1 != entityliving || this.at >= 5)) { + float f = 0.3F; + AxisAlignedBB axisalignedbb = entity1.boundingBox.grow((double) f, (double) f, (double) f); + MovingObjectPosition movingobjectposition1 = axisalignedbb.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { + double d1 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + } + + // PaperSpigot start - Allow projectiles to fly through players the shooter can't see + if (movingobjectposition != null && movingobjectposition.entity instanceof EntityPlayer && shooter != null && shooter instanceof EntityPlayer) { + if (!((EntityPlayer) shooter).getBukkitEntity().canSee(((EntityPlayer) movingobjectposition.entity).getBukkitEntity())) { + movingobjectposition = null; + } + } + // PaperSpigot end + + if (movingobjectposition != null) { + if (movingobjectposition.type == EnumMovingObjectType.BLOCK && this.world.getType(movingobjectposition.b, movingobjectposition.c, movingobjectposition.d) == Blocks.PORTAL) { + this.ah(); + } else { + this.a(movingobjectposition); + // CraftBukkit start + if (this.dead) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); + } + // CraftBukkit end + } + } + + this.locX += this.motX; + this.locY += this.motY; + this.locZ += this.motZ; + float f1 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (Math.atan2(this.motX, this.motZ) * 180.0D / 3.1415927410125732D); + + for (this.pitch = (float) (Math.atan2(this.motY, (double) f1) * 180.0D / 3.1415927410125732D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + float f2 = 0.99F; + float f3 = this.i(); + + if (this.M()) { + for (int j = 0; j < 4; ++j) { + float f4 = 0.25F; + + this.world.addParticle("bubble", this.locX - this.motX * (double) f4, this.locY - this.motY * (double) f4, this.locZ - this.motZ * (double) f4, this.motX, this.motY, this.motZ); + } + + f2 = 0.8F; + } + + this.motX *= (double) f2; + this.motY *= (double) f2; + this.motZ *= (double) f2; + this.motY -= (double) f3; + this.setPosition(this.locX, this.locY, this.locZ); + } + + protected float i() { + return 0.03F; + } + + protected abstract void a(MovingObjectPosition movingobjectposition); + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("xTile", (short) this.blockX); + nbttagcompound.setShort("yTile", (short) this.blockY); + nbttagcompound.setShort("zTile", (short) this.blockZ); + nbttagcompound.setByte("inTile", (byte) Block.getId(this.inBlockId)); + nbttagcompound.setByte("shake", (byte) this.shake); + nbttagcompound.setByte("inGround", (byte) (this.inGround ? 1 : 0)); + if ((this.shooterName == null || this.shooterName.length() == 0) && this.shooter != null && this.shooter instanceof EntityHuman) { + this.shooterName = this.shooter.getName(); + } + + nbttagcompound.setString("ownerName", this.shooterName == null ? "" : this.shooterName); + } + + public void a(NBTTagCompound nbttagcompound) { + this.blockX = nbttagcompound.getShort("xTile"); + this.blockY = nbttagcompound.getShort("yTile"); + this.blockZ = nbttagcompound.getShort("zTile"); + this.inBlockId = Block.getById(nbttagcompound.getByte("inTile") & 255); + this.shake = nbttagcompound.getByte("shake") & 255; + this.inGround = nbttagcompound.getByte("inGround") == 1; + this.shooterName = nbttagcompound.getString("ownerName"); + if (this.shooterName != null && this.shooterName.length() == 0) { + this.shooterName = null; + } + } + + public EntityLiving getShooter() { + if (this.shooter == null && this.shooterName != null && this.shooterName.length() > 0) { + this.shooter = this.world.a(this.shooterName); + } + + return this.shooter; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySheep.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySheep.java new file mode 100644 index 0000000..237cb36 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySheep.java @@ -0,0 +1,229 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.event.entity.SheepRegrowWoolEvent; +import org.bukkit.event.player.PlayerShearEntityEvent; +// CraftBukkit end + +public class EntitySheep extends EntityAnimal { + + private final InventoryCrafting bq = new InventoryCrafting(new ContainerSheepBreed(this), 2, 1); + public static final float[][] bp = new float[][] { { 1.0F, 1.0F, 1.0F}, { 0.85F, 0.5F, 0.2F}, { 0.7F, 0.3F, 0.85F}, { 0.4F, 0.6F, 0.85F}, { 0.9F, 0.9F, 0.2F}, { 0.5F, 0.8F, 0.1F}, { 0.95F, 0.5F, 0.65F}, { 0.3F, 0.3F, 0.3F}, { 0.6F, 0.6F, 0.6F}, { 0.3F, 0.5F, 0.6F}, { 0.5F, 0.25F, 0.7F}, { 0.2F, 0.3F, 0.7F}, { 0.4F, 0.3F, 0.2F}, { 0.4F, 0.5F, 0.2F}, { 0.6F, 0.2F, 0.2F}, { 0.1F, 0.1F, 0.1F}}; + private int br; + private PathfinderGoalEatTile bs = new PathfinderGoalEatTile(this); + + public EntitySheep(World world) { + super(world); + this.a(0.9F, 1.3F); + this.getNavigation().a(true); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.25D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.1D, Items.WHEAT, false)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.1D)); + this.goalSelector.a(5, this.bs); + this.goalSelector.a(6, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.bq.setItem(0, new ItemStack(Items.INK_SACK, 1, 0)); + this.bq.setItem(1, new ItemStack(Items.INK_SACK, 1, 0)); + this.bq.resultInventory = new InventoryCraftResult(); // CraftBukkit - add result slot for event + } + + @Override + public void h() { + super.h(); + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + } + + protected boolean bk() { + return true; + } + + protected void bn() { + this.br = this.bs.f(); + super.bn(); + } + + public void e() { + if (this.world.isStatic) { + this.br = Math.max(0, this.br - 1); + } + + super.e(); + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.23000000417232513D); + } + + protected void c() { + super.c(); + this.datawatcher.a(16, new Byte((byte) 0)); + } + + protected void dropDeathLoot(boolean flag, int i) { + if (!this.isSheared()) { + this.a(new ItemStack(Item.getItemOf(Blocks.WOOL), 1, this.getColor()), 0.0F); + } + } + + protected Item getLoot() { + return Item.getItemOf(Blocks.WOOL); + } + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (itemstack != null && itemstack.getItem() == Items.SHEARS && !this.isSheared() && !this.isBaby()) { + if (!this.world.isStatic) { + // CraftBukkit start + PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + + this.setSheared(true); + int i = 1 + this.random.nextInt(3); + + for (int j = 0; j < i; ++j) { + EntityItem entityitem = this.a(new ItemStack(Item.getItemOf(Blocks.WOOL), 1, this.getColor()), 1.0F); + + entityitem.motY += (double) (this.random.nextFloat() * 0.05F); + entityitem.motX += (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F); + entityitem.motZ += (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F); + } + } + + itemstack.damage(1, entityhuman); + this.makeSound("mob.sheep.shear", 1.0F, 1.0F); + } + + return super.a(entityhuman); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("Sheared", this.isSheared()); + nbttagcompound.setByte("Color", (byte) this.getColor()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setSheared(nbttagcompound.getBoolean("Sheared")); + this.setColor(nbttagcompound.getByte("Color")); + } + + protected String t() { + return "mob.sheep.say"; + } + + protected String aT() { + return "mob.sheep.say"; + } + + protected String aU() { + return "mob.sheep.say"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.sheep.step", 0.15F, 1.0F); + } + + public int getColor() { + return this.datawatcher.getByte(16) & 15; + } + + public void setColor(int i) { + byte b0 = this.datawatcher.getByte(16); + + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 & 240 | i & 15))); + } + + public boolean isSheared() { + return (this.datawatcher.getByte(16) & 16) != 0; + } + + public void setSheared(boolean flag) { + byte b0 = this.datawatcher.getByte(16); + + if (flag) { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 | 16))); + } else { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 & -17))); + } + } + + public static int a(Random random) { + int i = random.nextInt(100); + + return i < 5 ? 15 : (i < 10 ? 7 : (i < 15 ? 8 : (i < 18 ? 12 : (random.nextInt(500) == 0 ? 6 : 0)))); + } + + public EntitySheep b(EntityAgeable entityageable) { + EntitySheep entitysheep = (EntitySheep) entityageable; + EntitySheep entitysheep1 = new EntitySheep(this.world); + int i = this.a(this, entitysheep); + + entitysheep1.setColor(15 - i); + return entitysheep1; + } + + public void p() { + // CraftBukkit start + SheepRegrowWoolEvent event = new SheepRegrowWoolEvent((org.bukkit.entity.Sheep) this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setSheared(false); + } + // CraftBukkit end + + if (this.isBaby()) { + this.a(60); + } + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + groupdataentity = super.prepare(groupdataentity); + this.setColor(a(this.world.random)); + return groupdataentity; + } + + private int a(EntityAnimal entityanimal, EntityAnimal entityanimal1) { + int i = this.b(entityanimal); + int j = this.b(entityanimal1); + + this.bq.getItem(0).setData(i); + this.bq.getItem(1).setData(j); + ItemStack itemstack = CraftingManager.getInstance().craft(this.bq, ((EntitySheep) entityanimal).world); + int k; + + if (itemstack != null && itemstack.getItem() == Items.INK_SACK) { + k = itemstack.getData(); + } else { + k = this.world.random.nextBoolean() ? i : j; + } + + return k; + } + + private int b(EntityAnimal entityanimal) { + return 15 - ((EntitySheep) entityanimal).getColor(); + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.b(entityageable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySilverfish.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySilverfish.java new file mode 100644 index 0000000..0881e25 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySilverfish.java @@ -0,0 +1,171 @@ +package net.minecraft.server; + +import net.minecraft.util.org.apache.commons.lang3.tuple.ImmutablePair; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntitySilverfish extends EntityMonster { + + private int bp; + + public EntitySilverfish(World world) { + super(world); + this.a(0.3F, 0.7F); + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.6000000238418579D); + this.getAttributeInstance(GenericAttributes.e).setValue(1.0D); + } + + protected boolean g_() { + return false; + } + + protected Entity findTarget() { + double d0 = 8.0D; + + return this.world.findNearbyVulnerablePlayer(this, d0); + } + + protected String t() { + return "mob.silverfish.say"; + } + + protected String aT() { + return "mob.silverfish.hit"; + } + + protected String aU() { + return "mob.silverfish.kill"; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + if (this.bp <= 0 && (damagesource instanceof EntityDamageSource || damagesource == DamageSource.MAGIC)) { + this.bp = 20; + } + + return super.damageEntity(damagesource, f); + } + } + + protected void a(Entity entity, float f) { + if (this.attackTicks <= 0 && f < 1.2F && entity.boundingBox.e > this.boundingBox.b && entity.boundingBox.b < this.boundingBox.e) { + this.attackTicks = 20; + this.n(entity); + } + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.silverfish.step", 0.15F, 1.0F); + } + + protected Item getLoot() { + return Item.getById(0); + } + + public void h() { + this.aM = this.yaw; + super.h(); + } + + protected void bq() { + super.bq(); + if (!this.world.isStatic) { + int i; + int j; + int k; + int l; + + if (this.bp > 0) { + --this.bp; + if (this.bp == 0) { + i = MathHelper.floor(this.locX); + j = MathHelper.floor(this.locY); + k = MathHelper.floor(this.locZ); + boolean flag = false; + + for (int i1 = 0; !flag && i1 <= 5 && i1 >= -5; i1 = i1 <= 0 ? 1 - i1 : 0 - i1) { + for (l = 0; !flag && l <= 10 && l >= -10; l = l <= 0 ? 1 - l : 0 - l) { + for (int j1 = 0; !flag && j1 <= 10 && j1 >= -10; j1 = j1 <= 0 ? 1 - j1 : 0 - j1) { + if (this.world.getType(i + l, j + i1, k + j1) == Blocks.MONSTER_EGGS) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(this, i + l, j + i1, k + j1, Blocks.AIR, 0).isCancelled()) { + continue; + } + // CraftBukkit end + if (!this.world.getGameRules().getBoolean("mobGriefing")) { + int k1 = this.world.getData(i + l, j + i1, k + j1); + ImmutablePair immutablepair = BlockMonsterEggs.b(k1); + + this.world.setTypeAndData(i + l, j + i1, k + j1, (Block) immutablepair.getLeft(), ((Integer) immutablepair.getRight()).intValue(), 3); + } else { + this.world.setAir(i + l, j + i1, k + j1, false); + } + + Blocks.MONSTER_EGGS.postBreak(this.world, i + l, j + i1, k + j1, 0); + if (this.random.nextBoolean()) { + flag = true; + break; + } + } + } + } + } + } + } + + if (this.target == null && !this.bS() && !this.isSearchingForAPath()) { // Poweruser + i = MathHelper.floor(this.locX); + j = MathHelper.floor(this.locY + 0.5D); + k = MathHelper.floor(this.locZ); + int l1 = this.random.nextInt(6); + Block block = this.world.getType(i + Facing.b[l1], j + Facing.c[l1], k + Facing.d[l1]); + + l = this.world.getData(i + Facing.b[l1], j + Facing.c[l1], k + Facing.d[l1]); + if (BlockMonsterEggs.a(block)) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(this, i + Facing.b[l1], j + Facing.c[l1], k + Facing.d[l1], Blocks.MONSTER_EGGS, Block.getId(BlockMonsterEggs.getById(l))).isCancelled()) { + return; + } + // CraftBukkit end + + this.world.setTypeAndData(i + Facing.b[l1], j + Facing.c[l1], k + Facing.d[l1], Blocks.MONSTER_EGGS, BlockMonsterEggs.a(block, l), 3); + this.s(); + this.die(); + } else { + this.bQ(); + } + } else if (this.target != null && !this.bS() && !this.isSearchingForAPath()) { // Poweruser + this.target = null; + } + } + } + + public float a(int i, int j, int k) { + return this.world.getType(i, j - 1, k) == Blocks.STONE ? 10.0F : super.a(i, j, k); + } + + protected boolean j_() { + return true; + } + + public boolean canSpawn() { + if (super.canSpawn()) { + EntityHuman entityhuman = this.world.findNearbyPlayer(this, 5.0D); + + return entityhuman == null; + } else { + return false; + } + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.ARTHROPOD; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySkeleton.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySkeleton.java new file mode 100644 index 0000000..c82f3cc --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySkeleton.java @@ -0,0 +1,304 @@ +package net.minecraft.server; + +import java.util.Calendar; + +import org.bukkit.event.entity.EntityCombustEvent; // CraftBukkit + +public class EntitySkeleton extends EntityMonster implements IRangedEntity { + + private PathfinderGoalArrowAttack bp = new PathfinderGoalArrowAttack(this, 1.0D, 20, 60, 15.0F); + private PathfinderGoalMeleeAttack bq = new PathfinderGoalMeleeAttack(this, EntityHuman.class, 1.2D, false); + + public EntitySkeleton(World world) { + super(world); + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalRestrictSun(this)); + this.goalSelector.a(3, new PathfinderGoalFleeSun(this, 1.0D)); + this.goalSelector.a(5, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(6, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false)); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 0, true)); + if (world != null && !world.isStatic) { + this.bZ(); + } + } + + @Override + public void h() { + super.h(); + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.die(); + } + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.d).setValue(0.25D); + } + + protected void c() { + super.c(); + this.datawatcher.a(13, new Byte((byte) 0)); + } + + public boolean bk() { + return true; + } + + protected String t() { + return "mob.skeleton.say"; + } + + protected String aT() { + return "mob.skeleton.hurt"; + } + + protected String aU() { + return "mob.skeleton.death"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.skeleton.step", 0.15F, 1.0F); + } + + public boolean n(Entity entity) { + if (super.n(entity)) { + if (this.getSkeletonType() == 1 && entity instanceof EntityLiving) { + ((EntityLiving) entity).addEffect(new MobEffect(MobEffectList.WITHER.id, 200)); + } + + return true; + } else { + return false; + } + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + public void e() { + if (this.world.w() && !this.world.isStatic) { + float f = this.d(1.0F); + + if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.world.i(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ))) { + boolean flag = true; + ItemStack itemstack = this.getEquipment(4); + + if (itemstack != null) { + if (itemstack.g()) { + itemstack.setData(itemstack.j() + this.random.nextInt(2)); + if (itemstack.j() >= itemstack.l()) { + this.a(itemstack); + this.setEquipment(4, (ItemStack) null); + } + } + + flag = false; + } + + if (flag) { + // CraftBukkit start + EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setOnFire(event.getDuration()); + } + // CraftBukkit end + } + } + } + + if (this.world.isStatic && this.getSkeletonType() == 1) { + this.a(0.72F, 2.34F); + } + + super.e(); + } + + public void ab() { + super.ab(); + if (this.vehicle instanceof EntityCreature) { + EntityCreature entitycreature = (EntityCreature) this.vehicle; + + this.aM = entitycreature.aM; + } + } + + public void die(DamageSource damagesource) { + super.die(damagesource); + if (damagesource.i() instanceof EntityArrow && damagesource.getEntity() instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) damagesource.getEntity(); + double d0 = entityhuman.locX - this.locX; + double d1 = entityhuman.locZ - this.locZ; + + if (d0 * d0 + d1 * d1 >= 2500.0D) { + entityhuman.a((Statistic) AchievementList.v); + } + } + } + + protected Item getLoot() { + return Items.ARROW; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j; + int k; + + if (this.getSkeletonType() == 1) { + j = this.random.nextInt(3 + i) - 1; + + for (k = 0; k < j; ++k) { + this.a(Items.COAL, 1); + } + } else { + j = this.random.nextInt(3 + i); + + for (k = 0; k < j; ++k) { + this.a(Items.ARROW, 1); + } + } + + j = this.random.nextInt(3 + i); + + for (k = 0; k < j; ++k) { + this.a(Items.BONE, 1); + } + } + + protected void getRareDrop(int i) { + if (this.getSkeletonType() == 1) { + this.a(new ItemStack(Items.SKULL, 1, 1), 0.0F); + } + } + + protected void bC() { + super.bC(); + this.setEquipment(0, new ItemStack(Items.BOW)); + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + groupdataentity = super.prepare(groupdataentity); + if (this.world.worldProvider instanceof WorldProviderHell && this.aI().nextInt(5) > 0) { + this.goalSelector.a(4, this.bq); + this.setSkeletonType(1); + this.setEquipment(0, new ItemStack(Items.STONE_SWORD)); + this.getAttributeInstance(GenericAttributes.e).setValue(4.0D); + } else { + this.goalSelector.a(4, this.bp); + this.bC(); + this.bD(); + } + + this.h(this.random.nextFloat() < 0.55F * this.world.b(this.locX, this.locY, this.locZ)); + if (this.getEquipment(4) == null) { + Calendar calendar = this.world.V(); + + if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.random.nextFloat() < 0.25F) { + this.setEquipment(4, new ItemStack(this.random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.PUMPKIN)); + this.dropChances[4] = 0.0F; + } + } + + return groupdataentity; + } + + public void bZ() { + this.goalSelector.a((PathfinderGoal) this.bq); + this.goalSelector.a((PathfinderGoal) this.bp); + ItemStack itemstack = this.be(); + + if (itemstack != null && itemstack.getItem() == Items.BOW) { + this.goalSelector.a(4, this.bp); + } else { + this.goalSelector.a(4, this.bq); + } + } + + public void a(EntityLiving entityliving, float f) { + EntityArrow entityarrow = new EntityArrow(this.world, this, entityliving, 1.6F, (float) (14 - this.world.difficulty.a() * 4)); + int i = EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_DAMAGE.id, this.be()); + int j = EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_KNOCKBACK.id, this.be()); + + entityarrow.b((double) (f * 2.0F) + this.random.nextGaussian() * 0.25D + (double) ((float) this.world.difficulty.a() * 0.11F)); + if (i > 0) { + entityarrow.b(entityarrow.e() + (double) i * 0.5D + 0.5D); + } + + if (j > 0) { + entityarrow.setKnockbackStrength(j); + } + + if (EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_FIRE.id, this.be()) > 0 || this.getSkeletonType() == 1) { + // CraftBukkit start - call EntityCombustEvent + EntityCombustEvent event = new EntityCombustEvent(entityarrow.getBukkitEntity(), 100); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + entityarrow.setOnFire(event.getDuration()); + } + // CraftBukkit end + } + + // CraftBukkit start + org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.be(), entityarrow, 0.8F); + if (event.isCancelled()) { + event.getProjectile().remove(); + return; + } + + if (event.getProjectile() == entityarrow.getBukkitEntity()) { + world.addEntity(entityarrow); + } + // CraftBukkit end + + this.makeSound("random.bow", 1.0F, 1.0F / (this.aI().nextFloat() * 0.4F + 0.8F)); + // this.world.addEntity(entityarrow); // CraftBukkit - moved up + } + + public int getSkeletonType() { + return this.datawatcher.getByte(13); + } + + public void setSkeletonType(int i) { + this.datawatcher.watch(13, Byte.valueOf((byte) i)); + this.fireProof = i == 1; + if (i == 1) { + this.a(0.72F, 2.34F); + } else { + this.a(0.6F, 1.8F); + } + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("SkeletonType", 99)) { + byte b0 = nbttagcompound.getByte("SkeletonType"); + + this.setSkeletonType(b0); + } + + this.bZ(); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setByte("SkeletonType", (byte) this.getSkeletonType()); + } + + public void setEquipment(int i, ItemStack itemstack) { + super.setEquipment(i, itemstack); + if (!this.world.isStatic && i == 0) { + this.bZ(); + } + } + + public double ad() { + return super.ad() - 0.5D; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySlime.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySlime.java new file mode 100644 index 0000000..fe305fc --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySlime.java @@ -0,0 +1,270 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.SlimeSplitEvent; +// CraftBukkit end + +public class EntitySlime extends EntityInsentient implements IMonster { + + public float h; + public float i; + public float bm; + private int jumpDelay; + private Entity lastTarget; // CraftBukkit + + public EntitySlime(World world) { + super(world); + int i = 1 << this.random.nextInt(3); + + this.height = 0.0F; + this.jumpDelay = this.random.nextInt(20) + 10; + this.setSize(i); + } + + protected void c() { + super.c(); + this.datawatcher.a(16, new Byte((byte) 1)); + } + + // CraftBukkit - protected -> public + public void setSize(int i) { + this.datawatcher.watch(16, new Byte((byte) i)); + this.a(0.6F * (float) i, 0.6F * (float) i); + this.setPosition(this.locX, this.locY, this.locZ); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) (i * i)); + this.setHealth(this.getMaxHealth()); + this.b = i; + } + + public int getSize() { + return this.datawatcher.getByte(16); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Size", this.getSize() - 1); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + int i = nbttagcompound.getInt("Size"); + + if (i < 0) { + i = 0; + } + + this.setSize(i + 1); + } + + protected String bP() { + return "slime"; + } + + protected String bV() { + return "mob.slime." + (this.getSize() > 1 ? "big" : "small"); + } + + public void h() { + if (!this.world.isStatic && this.world.difficulty == EnumDifficulty.PEACEFUL && this.getSize() > 0) { + this.dead = true; + } + + // MineHQ - Add mobsEnabled check. + if (!this.world.isStatic && !this.world.spigotConfig.mobsEnabled) { + this.dead = true; + } + + this.i += (this.h - this.i) * 0.5F; + this.bm = this.i; + boolean flag = this.onGround; + + super.h(); + int i; + + if (this.onGround && !flag) { + i = this.getSize(); + + for (int j = 0; j < i * 8; ++j) { + float f = this.random.nextFloat() * 3.1415927F * 2.0F; + float f1 = this.random.nextFloat() * 0.5F + 0.5F; + float f2 = MathHelper.sin(f) * (float) i * 0.5F * f1; + float f3 = MathHelper.cos(f) * (float) i * 0.5F * f1; + + this.world.addParticle(this.bP(), this.locX + (double) f2, this.boundingBox.b, this.locZ + (double) f3, 0.0D, 0.0D, 0.0D); + } + + if (this.bW()) { + this.makeSound(this.bV(), this.bf(), ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) / 0.8F); + } + + this.h = -0.5F; + } else if (!this.onGround && flag) { + this.h = 1.0F; + } + + this.bS(); + if (this.world.isStatic) { + i = this.getSize(); + this.a(0.6F * (float) i, 0.6F * (float) i); + } + } + + protected void bq() { + this.w(); + // CraftBukkit start + Entity entityhuman = this.world.findNearbyVulnerablePlayer(this, 16.0D); // EntityHuman -> Entity + EntityTargetEvent event = null; + + if (entityhuman != null && !entityhuman.equals(lastTarget)) { + event = CraftEventFactory.callEntityTargetEvent(this, entityhuman, EntityTargetEvent.TargetReason.CLOSEST_PLAYER); + } else if (lastTarget != null && entityhuman == null) { + event = CraftEventFactory.callEntityTargetEvent(this, entityhuman, EntityTargetEvent.TargetReason.FORGOT_TARGET); + } + + if (event != null && !event.isCancelled()) { + entityhuman = event.getTarget() == null ? null : ((CraftEntity) event.getTarget()).getHandle(); + } + + this.lastTarget = entityhuman; + // CraftBukkit end + + if (entityhuman != null) { + this.a(entityhuman, 10.0F, 20.0F); + } + + if (this.onGround && this.jumpDelay-- <= 0) { + this.jumpDelay = this.bR(); + if (entityhuman != null) { + this.jumpDelay /= 3; + } + + this.bc = true; + if (this.bY()) { + this.makeSound(this.bV(), this.bf(), ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) * 0.8F); + } + + this.bd = 1.0F - this.random.nextFloat() * 2.0F; + this.be = (float) (1 * this.getSize()); + } else { + this.bc = false; + if (this.onGround) { + this.bd = this.be = 0.0F; + } + } + } + + protected void bS() { + this.h *= 0.6F; + } + + protected int bR() { + return this.random.nextInt(20) + 10; + } + + protected EntitySlime bQ() { + return new EntitySlime(this.world); + } + + public void die() { + int i = this.getSize(); + + if (!this.world.isStatic && i > 1 && this.getHealth() <= 0.0F) { + int j = 2 + this.random.nextInt(3); + + // CraftBukkit start + SlimeSplitEvent event = new SlimeSplitEvent((org.bukkit.entity.Slime) this.getBukkitEntity(), j); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled() && event.getCount() > 0) { + j = event.getCount(); + } else { + super.die(); + return; + } + // CraftBukkit end + + for (int k = 0; k < j; ++k) { + float f = ((float) (k % 2) - 0.5F) * (float) i / 4.0F; + float f1 = ((float) (k / 2) - 0.5F) * (float) i / 4.0F; + EntitySlime entityslime = this.bQ(); + + entityslime.setSize(i / 2); + entityslime.setPositionRotation(this.locX + (double) f, this.locY + 0.5D, this.locZ + (double) f1, this.random.nextFloat() * 360.0F, 0.0F); + this.world.addEntity(entityslime, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SLIME_SPLIT); // CraftBukkit - SpawnReason + } + } + + super.die(); + } + + public void b_(EntityHuman entityhuman) { + if (this.bT()) { + int i = this.getSize(); + + if (this.hasLineOfSight(entityhuman) && this.f(entityhuman) < 0.6D * (double) i * 0.6D * (double) i && entityhuman.damageEntity(DamageSource.mobAttack(this), (float) this.bU())) { + this.makeSound("mob.attack", 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + } + } + } + + protected boolean bT() { + return this.getSize() > 1; + } + + protected int bU() { + return this.getSize(); + } + + protected String aT() { + return "mob.slime." + (this.getSize() > 1 ? "big" : "small"); + } + + protected String aU() { + return "mob.slime." + (this.getSize() > 1 ? "big" : "small"); + } + + protected Item getLoot() { + return this.getSize() == 1 ? Items.SLIME_BALL : Item.getById(0); + } + + public boolean canSpawn() { + Chunk chunk = this.world.getChunkAtWorldCoords(MathHelper.floor(this.locX), MathHelper.floor(this.locZ)); + + if (this.world.getWorldData().getType() == WorldType.FLAT && this.random.nextInt(4) != 1) { + return false; + } else { + if (this.getSize() == 1 || this.world.difficulty != EnumDifficulty.PEACEFUL) { + BiomeBase biomebase = this.world.getBiome(MathHelper.floor(this.locX), MathHelper.floor(this.locZ)); + + if (biomebase == BiomeBase.SWAMPLAND && this.locY > 50.0D && this.locY < 70.0D && this.random.nextFloat() < 0.5F && this.random.nextFloat() < this.world.y() && this.world.getLightLevel(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)) <= this.random.nextInt(8)) { + return super.canSpawn(); + } + + if (this.random.nextInt(10) == 0 && chunk.a(987234911L).nextInt(10) == 0 && this.locY < 40.0D) { + return super.canSpawn(); + } + } + + return false; + } + } + + protected float bf() { + return 0.4F * (float) this.getSize(); + } + + public int x() { + return 0; + } + + protected boolean bY() { + return this.getSize() > 0; + } + + protected boolean bW() { + return this.getSize() > 2; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySmallFireball.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySmallFireball.java new file mode 100644 index 0000000..4d61eec --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySmallFireball.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityCombustByEntityEvent; // CraftBukkit + +public class EntitySmallFireball extends EntityFireball { + + public EntitySmallFireball(World world) { + super(world); + this.a(0.3125F, 0.3125F); + } + + public EntitySmallFireball(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(world, entityliving, d0, d1, d2); + this.a(0.3125F, 0.3125F); + } + + public EntitySmallFireball(World world, double d0, double d1, double d2, double d3, double d4, double d5) { + super(world, d0, d1, d2, d3, d4, d5); + this.a(0.3125F, 0.3125F); + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isStatic) { + if (movingobjectposition.entity != null) { + if (!movingobjectposition.entity.isFireproof() && movingobjectposition.entity.damageEntity(DamageSource.fireball(this, this.shooter), 5.0F)) { + // CraftBukkit start - Entity damage by entity event + combust event + EntityCombustByEntityEvent event = new EntityCombustByEntityEvent((org.bukkit.entity.Projectile) this.getBukkitEntity(), movingobjectposition.entity.getBukkitEntity(), 5); + movingobjectposition.entity.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + movingobjectposition.entity.setOnFire(event.getDuration()); + } + // CraftBukkit end + } + } else { + int i = movingobjectposition.b; + int j = movingobjectposition.c; + int k = movingobjectposition.d; + + switch (movingobjectposition.face) { + case 0: + --j; + break; + + case 1: + ++j; + break; + + case 2: + --k; + break; + + case 3: + ++k; + break; + + case 4: + --i; + break; + + case 5: + ++i; + } + + if (this.world.isEmpty(i, j, k)) { + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, i, j, k, this).isCancelled()) { + this.world.setTypeUpdate(i, j, k, Blocks.FIRE); + } + // CraftBukkit end + } + } + + this.die(); + } + } + + public boolean R() { + return false; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySnowman.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySnowman.java new file mode 100644 index 0000000..2811fb4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySnowman.java @@ -0,0 +1,89 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.event.block.EntityBlockFormEvent; +// CraftBukkit end + +public class EntitySnowman extends EntityGolem implements IRangedEntity { + + public EntitySnowman(World world) { + super(world); + this.a(0.4F, 1.8F); + this.getNavigation().a(true); + this.goalSelector.a(1, new PathfinderGoalArrowAttack(this, 1.25D, 20, 10.0F)); + this.goalSelector.a(2, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(4, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalNearestAttackableTarget(this, EntityInsentient.class, 0, true, false, IMonster.a)); + } + + public boolean bk() { + return true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(4.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.20000000298023224D); + } + + public void e() { + super.e(); + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (this.L()) { + this.damageEntity(DamageSource.DROWN, 1.0F); + } + + if (this.world.getBiome(i, k).a(i, j, k) > 1.0F) { + this.damageEntity(CraftEventFactory.MELTING, 1.0F); // CraftBukkit - DamageSource.BURN -> CraftEventFactory.MELTING + } + + for (int l = 0; l < 4; ++l) { + i = MathHelper.floor(this.locX + (double) ((float) (l % 2 * 2 - 1) * 0.25F)); + j = MathHelper.floor(this.locY); + k = MathHelper.floor(this.locZ + (double) ((float) (l / 2 % 2 * 2 - 1) * 0.25F)); + if (this.world.getType(i, j, k).getMaterial() == Material.AIR && this.world.getBiome(i, k).a(i, j, k) < 0.8F && Blocks.SNOW.canPlace(this.world, i, j, k)) { + // CraftBukkit start + org.bukkit.block.BlockState blockState = this.world.getWorld().getBlockAt(i, j, k).getState(); + blockState.setType(CraftMagicNumbers.getMaterial(Blocks.SNOW)); + + EntityBlockFormEvent event = new EntityBlockFormEvent(this.getBukkitEntity(), blockState.getBlock(), blockState); + this.world.getServer().getPluginManager().callEvent(event); + + if(!event.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } + } + } + + protected Item getLoot() { + return Items.SNOW_BALL; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(16); + + for (int k = 0; k < j; ++k) { + this.a(Items.SNOW_BALL, 1); + } + } + + public void a(EntityLiving entityliving, float f) { + EntitySnowball entitysnowball = new EntitySnowball(this.world, this); + double d0 = entityliving.locX - this.locX; + double d1 = entityliving.locY + (double) entityliving.getHeadHeight() - 1.100000023841858D - entitysnowball.locY; + double d2 = entityliving.locZ - this.locZ; + float f1 = MathHelper.sqrt(d0 * d0 + d2 * d2) * 0.2F; + + entitysnowball.shoot(d0, d1 + (double) f1, d2, 1.6F, 12.0F); + this.makeSound("random.bow", 1.0F, 1.0F / (this.aI().nextFloat() * 0.4F + 0.8F)); + this.world.addEntity(entitysnowball); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySpider.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySpider.java new file mode 100644 index 0000000..b8be8ba --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySpider.java @@ -0,0 +1,167 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + +public class EntitySpider extends EntityMonster { + + public EntitySpider(World world) { + super(world); + this.a(1.4F, 0.9F); + } + + protected void c() { + super.c(); + this.datawatcher.a(16, new Byte((byte) 0)); + } + + public void h() { + super.h(); + if (!this.world.isStatic) { + // MineHQ - Add mobsEnabled check. + if (!this.world.spigotConfig.mobsEnabled) { + this.die(); + } + + this.a(this.positionChanged); + } + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(16.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.800000011920929D); + } + + protected Entity findTarget() { + float f = this.d(1.0F); + + if (f < 0.5F) { + double d0 = 16.0D; + + return this.world.findNearbyVulnerablePlayer(this, d0); + } else { + return null; + } + } + + protected String t() { + return "mob.spider.say"; + } + + protected String aT() { + return "mob.spider.say"; + } + + protected String aU() { + return "mob.spider.death"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.spider.step", 0.15F, 1.0F); + } + + protected void a(Entity entity, float f) { + float f1 = this.d(1.0F); + + if (f1 > 0.5F && this.random.nextInt(100) == 0) { + // CraftBukkit start + EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), null, EntityTargetEvent.TargetReason.FORGOT_TARGET); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (event.getTarget() == null) { + this.target = null; + } else { + this.target = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); + } + return; + } + // CraftBukkit end + } else { + if (f > 2.0F && f < 6.0F && this.random.nextInt(10) == 0) { + if (this.onGround) { + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1); + + this.motX = d0 / (double) f2 * 0.5D * 0.800000011920929D + this.motX * 0.20000000298023224D; + this.motZ = d1 / (double) f2 * 0.5D * 0.800000011920929D + this.motZ * 0.20000000298023224D; + this.motY = 0.4000000059604645D; + } + } else { + super.a(entity, f); + } + } + } + + protected Item getLoot() { + return Items.STRING; + } + + protected void dropDeathLoot(boolean flag, int i) { + super.dropDeathLoot(flag, i); + if (flag && (this.random.nextInt(3) == 0 || this.random.nextInt(1 + i) > 0)) { + this.a(Items.SPIDER_EYE, 1); + } + } + + public boolean h_() { + return this.bZ(); + } + + public void as() {} + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.ARTHROPOD; + } + + public boolean d(MobEffect mobeffect) { + return mobeffect.getEffectId() == MobEffectList.POISON.id ? false : super.d(mobeffect); + } + + public boolean bZ() { + return (this.datawatcher.getByte(16) & 1) != 0; + } + + public void a(boolean flag) { + byte b0 = this.datawatcher.getByte(16); + + if (flag) { + b0 = (byte) (b0 | 1); + } else { + b0 &= -2; + } + + this.datawatcher.watch(16, Byte.valueOf(b0)); + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + Object object = super.prepare(groupdataentity); + + if (this.world.random.nextInt(100) == 0) { + EntitySkeleton entityskeleton = new EntitySkeleton(this.world); + + entityskeleton.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entityskeleton.prepare((GroupDataEntity) null); + this.world.addEntity(entityskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.JOCKEY); // CraftBukkit - add SpawnReason + entityskeleton.mount(this); + } + + if (object == null) { + object = new GroupDataSpider(); + if (this.world.difficulty == EnumDifficulty.HARD && this.world.random.nextFloat() < 0.1F * this.world.b(this.locX, this.locY, this.locZ)) { + ((GroupDataSpider) object).a(this.world.random); + } + } + + if (object instanceof GroupDataSpider) { + int i = ((GroupDataSpider) object).a; + + if (i > 0 && MobEffectList.byId[i] != null) { + this.addEffect(new MobEffect(i, Integer.MAX_VALUE)); + } + } + + return (GroupDataEntity) object; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntitySquid.java b/vspigot-server/src/main/java/net/minecraft/server/EntitySquid.java new file mode 100644 index 0000000..bf95300 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntitySquid.java @@ -0,0 +1,152 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.TrigMath; // CraftBukkit + +public class EntitySquid extends EntityWaterAnimal { + + public float bp; + public float bq; + public float br; + public float bs; + public float bt; + public float bu; + public float bv; + public float bw; + private float bx; + private float by; + private float bz; + private float bA; + private float bB; + private float bC; + + public EntitySquid(World world) { + super(world); + this.a(0.95F, 0.95F); + this.by = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + } + + protected String t() { + return null; + } + + protected String aT() { + return null; + } + + protected String aU() { + return null; + } + + protected float bf() { + return 0.4F; + } + + protected Item getLoot() { + return Item.getById(0); + } + + protected boolean g_() { + return false; + } + + protected void dropDeathLoot(boolean flag, int i) { + int j = this.random.nextInt(3 + i) + 1; + + for (int k = 0; k < j; ++k) { + this.a(new ItemStack(Items.INK_SACK, 1, 0), 0.0F); + } + } + + /* CraftBukkit start - Delegate to Entity to use existing inWater value + public boolean M() { + return this.world.a(this.boundingBox.grow(0.0D, -0.6000000238418579D, 0.0D), Material.WATER, (Entity) this); + } + // CraftBukkit end */ + + public void e() { + super.e(); + this.bq = this.bp; + this.bs = this.br; + this.bu = this.bt; + this.bw = this.bv; + this.bt += this.by; + if (this.bt > 6.2831855F) { + this.bt -= 6.2831855F; + if (this.random.nextInt(10) == 0) { + this.by = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; + } + } + + if (this.M()) { + float f; + + if (this.bt < 3.1415927F) { + f = this.bt / 3.1415927F; + this.bv = MathHelper.sin(f * f * 3.1415927F) * 3.1415927F * 0.25F; + if ((double) f > 0.75D) { + this.bx = 1.0F; + this.bz = 1.0F; + } else { + this.bz *= 0.8F; + } + } else { + this.bv = 0.0F; + this.bx *= 0.9F; + this.bz *= 0.99F; + } + + if (!this.world.isStatic) { + this.motX = (double) (this.bA * this.bx); + this.motY = (double) (this.bB * this.bx); + this.motZ = (double) (this.bC * this.bx); + } + + f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + // CraftBukkit - Math -> TrigMath + this.aM += (-((float) TrigMath.atan2(this.motX, this.motZ)) * 180.0F / 3.1415927F - this.aM) * 0.1F; + this.yaw = this.aM; + this.br += 3.1415927F * this.bz * 1.5F; + // CraftBukkit - Math -> TrigMath + this.bp += (-((float) TrigMath.atan2((double) f, this.motY)) * 180.0F / 3.1415927F - this.bp) * 0.1F; + } else { + this.bv = MathHelper.abs(MathHelper.sin(this.bt)) * 3.1415927F * 0.25F; + if (!this.world.isStatic) { + this.motX = 0.0D; + this.motY -= 0.08D; + this.motY *= 0.9800000190734863D; + this.motZ = 0.0D; + } + + this.bp = (float) ((double) this.bp + (double) (-90.0F - this.bp) * 0.02D); + } + } + + public void e(float f, float f1) { + this.move(this.motX, this.motY, this.motZ); + } + + protected void bq() { + ++this.aU; + if (this.aU > 100) { + this.bA = this.bB = this.bC = 0.0F; + } else if (this.random.nextInt(50) == 0 || !this.inWater || this.bA == 0.0F && this.bB == 0.0F && this.bC == 0.0F) { + float f = this.random.nextFloat() * 3.1415927F * 2.0F; + + this.bA = MathHelper.cos(f) * 0.2F; + this.bB = -0.1F + this.random.nextFloat() * 0.2F; + this.bC = MathHelper.sin(f) * 0.2F; + } + + this.w(); + } + + public boolean canSpawn() { + // PaperSpigot - Configurable squid spawn height range + return this.locY > this.world.paperSpigotConfig.squidMinSpawnHeight && this.locY < this.world.paperSpigotConfig.squidMaxSpawnHeight && super.canSpawn(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityTNTPrimed.java b/vspigot-server/src/main/java/net/minecraft/server/EntityTNTPrimed.java new file mode 100644 index 0000000..cf723fb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityTNTPrimed.java @@ -0,0 +1,142 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit + +public class EntityTNTPrimed extends Entity { + + public int fuseTicks; + private EntityLiving source; + public float yield = 4; // CraftBukkit - add field + public boolean isIncendiary = false; // CraftBukkit - add field + public org.bukkit.Location sourceLoc; // PaperSpigot + + // PaperSpigot start - Add FallingBlock and TNT source location API + public EntityTNTPrimed(World world) { + this(null, world); + } + + public EntityTNTPrimed(org.bukkit.Location loc, World world) { + super(world); + sourceLoc = loc; + // PaperSpigot end + this.k = true; + this.a(0.98F, 0.98F); + this.height = this.length / 2.0F; + this.loadChunks = world.paperSpigotConfig.loadUnloadedTNTEntities; // PaperSpigot + } + + // PaperSpigot start - Add FallingBlock and TNT source location API + public EntityTNTPrimed(org.bukkit.Location loc, World world, double d0, double d1, double d2, EntityLiving entityliving) { + this(loc, world); + // PaperSpigot end + this.setPosition(d0, d1, d2); + //float f = (float) (Math.random() * 3.1415927410125732D * 2.0D); // PaperSpigot - Fix directional TNT bias + + this.motX = 0; // PaperSpigot - Fix directional TNT bias //(double) (-((float) Math.sin((double) f)) * 0.02F); + this.motY = 0.20000000298023224D; + this.motZ = 0; // PaperSpigot - Fix directional TNT bias //(double) (-((float) Math.cos((double) f)) * 0.02F); + this.fuseTicks = 80; + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + this.source = entityliving; + } + + protected void c() {} + + protected boolean g_() { + return false; + } + + public boolean R() { + return !this.dead; + } + + public void h() { + if (world.spigotConfig.currentPrimedTnt++ > world.spigotConfig.maxTntTicksPerTick) { return; } // Spigot + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.motY -= 0.03999999910593033D; + this.move(this.motX, this.motY, this.motZ); + // PaperSpigot start - Remove entities in unloaded chunks + if (this.inUnloadedChunk && world.paperSpigotConfig.removeUnloadedTNTEntities) { + this.die(); + this.fuseTicks = 2; + } + // PaperSpigot end + this.motX *= 0.9800000190734863D; + this.motY *= 0.9800000190734863D; + this.motZ *= 0.9800000190734863D; + if (this.onGround) { + this.motX *= 0.699999988079071D; + this.motZ *= 0.699999988079071D; + this.motY *= -0.5D; + } + + if (this.fuseTicks-- <= 0) { + // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event + if (!this.world.isStatic) { + this.explode(); + } + this.die(); + // CraftBukkit end + } else { + this.world.addParticle("smoke", this.locX, this.locY + 0.5D, this.locZ, 0.0D, 0.0D, 0.0D); + } + } + + private void explode() { + // CraftBukkit start + // float f = 4.0F; + // PaperSpigot start - Force load chunks during TNT explosions + ChunkProviderServer chunkProviderServer = ((ChunkProviderServer) world.chunkProvider); + boolean forceChunkLoad = chunkProviderServer.forceChunkLoad; + if (world.paperSpigotConfig.loadUnloadedTNTEntities) { + chunkProviderServer.forceChunkLoad = true; + } + // PaperSpigot end + org.bukkit.craftbukkit.CraftServer server = this.world.getServer(); + + ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(server, this)); + server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + // give 'this' instead of (Entity) null so we know what causes the damage + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), true); + } + // CraftBukkit end + // PaperSpigot start - Force load chunks during TNT explosions + if (world.paperSpigotConfig.loadUnloadedTNTEntities) { + chunkProviderServer.forceChunkLoad = forceChunkLoad; + } + // PaperSpigot end + } + + protected void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setByte("Fuse", (byte) this.fuseTicks); + // PaperSpigot start - Add FallingBlock and TNT source location API + if (sourceLoc != null) { + nbttagcompound.setInt("SourceLoc_x", sourceLoc.getBlockX()); + nbttagcompound.setInt("SourceLoc_y", sourceLoc.getBlockY()); + nbttagcompound.setInt("SourceLoc_z", sourceLoc.getBlockZ()); + } + // PaperSpigot end + } + + protected void a(NBTTagCompound nbttagcompound) { + this.fuseTicks = nbttagcompound.getByte("Fuse"); + // PaperSpigot start - Add FallingBlock and TNT source location API + if (nbttagcompound.hasKey("SourceLoc_x")) { + int srcX = nbttagcompound.getInt("SourceLoc_x"); + int srcY = nbttagcompound.getInt("SourceLoc_y"); + int srcZ = nbttagcompound.getInt("SourceLoc_z"); + sourceLoc = new org.bukkit.Location(world.getWorld(), srcX, srcY, srcZ); + } + // PaperSpigot end + } + + public EntityLiving getSource() { + return this.source; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityThrownExpBottle.java b/vspigot-server/src/main/java/net/minecraft/server/EntityThrownExpBottle.java new file mode 100644 index 0000000..68625a4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityThrownExpBottle.java @@ -0,0 +1,53 @@ +package net.minecraft.server; + +public class EntityThrownExpBottle extends EntityProjectile { + + public EntityThrownExpBottle(World world) { + super(world); + } + + public EntityThrownExpBottle(World world, EntityLiving entityliving) { + super(world, entityliving); + } + + public EntityThrownExpBottle(World world, double d0, double d1, double d2) { + super(world, d0, d1, d2); + } + + protected float i() { + return 0.07F; + } + + protected float e() { + return 0.7F; + } + + protected float f() { + return -20.0F; + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isStatic) { + // CraftBukkit - moved to after event + // this.world.triggerEffect(2002, (int) Math.round(this.locX), (int) Math.round(this.locY), (int) Math.round(this.locZ), 0); + int i = 3 + this.world.random.nextInt(5) + this.world.random.nextInt(5); + + // CraftBukkit start + org.bukkit.event.entity.ExpBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExpBottleEvent(this, i); + i = event.getExperience(); + if (event.getShowEffect()) { + this.world.triggerEffect(2002, (int) Math.round(this.locX), (int) Math.round(this.locY), (int) Math.round(this.locZ), 0); + } + // CraftBukkit end + + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); + + i -= j; + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j)); + } + + this.die(); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityTracker.java b/vspigot-server/src/main/java/net/minecraft/server/EntityTracker.java new file mode 100644 index 0000000..5db079c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityTracker.java @@ -0,0 +1,236 @@ +package net.minecraft.server; + +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import net.valorhcf.util.IndexedLinkedHashSet; + +public class EntityTracker { + + // MineHQ start + private IndexedLinkedHashSet c = new IndexedLinkedHashSet(); + public IntHashMap trackedEntities = new IntHashMap(); // CraftBukkit - private -> public + + private int noTrackDistance = 0; + + public int getNoTrackDistance() { + return this.noTrackDistance; + } + + public void setNoTrackDistance(int noTrackDistance) { + this.noTrackDistance = noTrackDistance; + } + // MineHQ end + + private WorldServer worldServer; + private int e; + + public EntityTracker(WorldServer worldserver) { + this.worldServer = worldserver; + this.e = 128; // MineHQ + } + + public WorldServer getWorldServer() { + return worldServer; + } + + public void track(Entity entity) { + if (entity instanceof EntityPlayer) { + this.addEntity(entity, 512, 2); + // MineHQ start + /* + EntityPlayer entityplayer = (EntityPlayer) entity; + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + if (entitytrackerentry.tracker != entityplayer) { + entitytrackerentry.updatePlayer(entityplayer); + } + } + */ + // MineHQ end + } else if (entity instanceof EntityFishingHook) { + this.addEntity(entity, 64, 5, true); + } else if (entity instanceof EntityArrow) { + this.addEntity(entity, 64, 20, false); + } else if (entity instanceof EntitySmallFireball) { + this.addEntity(entity, 64, 10, false); + } else if (entity instanceof EntityFireball) { + this.addEntity(entity, 64, 10, false); + } else if (entity instanceof EntitySnowball) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityEnderPearl) { + this.addEntity(entity, 64, 2, true); + } else if (entity instanceof EntityEnderSignal) { + this.addEntity(entity, 64, 4, true); + } else if (entity instanceof EntityEgg) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityPotion) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityThrownExpBottle) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityFireworks) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityItem) { + this.addEntity(entity, 64, 20, true); + } else if (entity instanceof EntityMinecartAbstract) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityBoat) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntitySquid) { + this.addEntity(entity, 64, 3, true); + } else if (entity instanceof EntityWither) { + this.addEntity(entity, 80, 3, false); + } else if (entity instanceof EntityBat) { + this.addEntity(entity, 80, 3, false); + } else if (entity instanceof IAnimal) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityEnderDragon) { + this.addEntity(entity, 160, 3, true); + } else if (entity instanceof EntityTNTPrimed) { + this.addEntity(entity, 160, 10, true); + } else if (entity instanceof EntityFallingBlock) { + this.addEntity(entity, 160, 20, true); + } else if (entity instanceof EntityHanging) { + this.addEntity(entity, 160, Integer.MAX_VALUE, false); + } else if (entity instanceof EntityExperienceOrb) { + this.addEntity(entity, 160, 20, true); + } else if (entity instanceof EntityEnderCrystal) { + this.addEntity(entity, 256, Integer.MAX_VALUE, false); + } + } + + public void addEntity(Entity entity, int i, int j) { + this.addEntity(entity, i, j, false); + } + + public void addEntity(Entity entity, int i, int j, boolean flag) { + org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot + i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot + if (i > this.e) { + i = this.e; + } + + try { + if (this.trackedEntities.b(entity.getId())) { + throw new IllegalStateException("Entity is already tracked!"); + } + + EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(this, entity, i, j, flag); // MineHQ + + this.c.add(entitytrackerentry); + this.trackedEntities.a(entity.getId(), entitytrackerentry); + // entitytrackerentry.scanPlayers(this.world.players); // MineHQ + entitytrackerentry.addNearPlayers(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + + public void untrackEntity(Entity entity) { + org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot + if (entity instanceof EntityPlayer) { + EntityPlayer entityplayer = (EntityPlayer) entity; + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.a(entityplayer); + } + } + + EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) this.trackedEntities.d(entity.getId()); + + if (entitytrackerentry1 != null) { + this.c.remove(entitytrackerentry1); + entitytrackerentry1.a(); + } + } + + // MineHQ start - parallel tracking + private static int trackerThreads = 4; // <-- 3 non-this threads, one this + private static ExecutorService pool = Executors.newFixedThreadPool(trackerThreads - 1, new ThreadFactoryBuilder().setNameFormat("entity-tracker-%d").build()); + public void updatePlayers() { + int offset = 0; + final CountDownLatch latch = new CountDownLatch(trackerThreads); + for (int i = 1; i <= trackerThreads; i++) { + final int localOffset = offset++; + + Runnable runnable = new Runnable() { + @Override + public void run() { + for (int i = localOffset; i < c.size(); i += trackerThreads) { + c.get(i).update(); + } + latch.countDown(); + } + }; + + if (i < trackerThreads) { + pool.execute(runnable); + } else { + runnable.run(); + } + } + + try { + latch.await(); + } catch (Exception e) { + e.printStackTrace(); + } + } + // MineHQ end + + public void a(Entity entity, Packet packet) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId()); + + if (entitytrackerentry != null) { + entitytrackerentry.broadcast(packet); + } + } + + public void sendPacketToEntity(Entity entity, Packet packet) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId()); + + if (entitytrackerentry != null) { + entitytrackerentry.broadcastIncludingSelf(packet); + } + } + + public void untrackPlayer(EntityPlayer entityplayer) { + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.clear(entityplayer); + } + } + + // MineHQ start - nope + /* + public void a(EntityPlayer entityplayer, Chunk chunk) { + // Kohi start - Optimized EntityTracker + for (List slice : chunk.entitySlices) { + for (Entity entity : slice) { + if (entity != entityplayer) { + EntityTrackerEntry entry = (EntityTrackerEntry) trackedEntities.get(entity.getId()); + + if (entry != null) { + entry.updatePlayer(entityplayer); + } + } + } + } + // Kohi end + } + */ + +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/vspigot-server/src/main/java/net/minecraft/server/EntityTrackerEntry.java new file mode 100644 index 0000000..51099de --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityTrackerEntry.java @@ -0,0 +1,686 @@ +package net.minecraft.server; + +import java.util.*; +import java.util.function.Consumer; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerVelocityEvent; +// CraftBukkit end + +import org.spigotmc.SpigotConfig; + +public class EntityTrackerEntry { + + private static final Logger p = LogManager.getLogger(); + public Entity tracker; + public int b; + public int c; + public int xLoc; + public int yLoc; + public int zLoc; + public int yRot; + public int xRot; + public int i; + public double j; + public double k; + public double l; + public int m; + private double q; + private double r; + private double s; + private boolean isMoving; + private boolean u; + private int v; + private Entity w; + private boolean x; + public boolean n; + public Set trackedPlayers = new LinkedHashSet(); // MineHQ - LHS has faster iteration + + // MineHQ start + private List toRemove = new ArrayList<>(); + private EntityTracker entityTracker; + private int addRemoveRate; + private int addRemoveCooldown; + private boolean withinNoTrack = false; + // MineHQ end + + public EntityTrackerEntry(EntityTracker entityTracker, Entity entity, int i, int j, boolean flag) { // MineHQ + this.entityTracker = entityTracker; // MineHQ + this.tracker = entity; + this.b = i; + this.c = j; + this.u = flag; + this.xLoc = MathHelper.floor(entity.locX * 32.0D); + this.yLoc = MathHelper.floor(entity.locY * 32.0D); + this.zLoc = MathHelper.floor(entity.locZ * 32.0D); + this.yRot = MathHelper.d(entity.yaw * 256.0F / 360.0F); + this.xRot = MathHelper.d(entity.pitch * 256.0F / 360.0F); + this.i = MathHelper.d(entity.getHeadRotation() * 256.0F / 360.0F); + + // MineHQ start + if (SpigotConfig.disableTracking) { + this.addRemoveRate = 100; + } else if (this.tracker instanceof EntityArrow || this.tracker instanceof EntityProjectile) { + this.addRemoveRate = 5; // projectile things + } else if (this.tracker instanceof EntityPlayer) { + this.addRemoveRate = 5; // players + } else { + this.addRemoveRate = 10; // default + } + this.addRemoveCooldown = this.tracker.getId() % addRemoveRate; + // MineHQ end + } + + public boolean equals(Object object) { + return object instanceof EntityTrackerEntry ? ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId() : false; + } + + public int hashCode() { + return this.tracker.getId(); + } + + // MineHQ start + public void update() { + this.withinNoTrack = this.withinNoTrack(); + if (--this.addRemoveCooldown <= 0) { + this.removeFarPlayers(); + this.addNearPlayers(); + this.addRemoveCooldown = this.addRemoveRate; + } + + this.track(null); + } + + private void removeFarPlayers() { + if (this.withinNoTrack) { + toRemove.addAll(this.trackedPlayers); + processToRemove(); + return; + } + + for (EntityPlayer entityplayer : (Collection) trackedPlayers) { + double d0 = entityplayer.locX - this.tracker.locX; + double d1 = entityplayer.locZ - this.tracker.locZ; + int range = this.getRange(); + + if (!(d0 >= (double) (-range) && d0 <= (double) range && d1 >= (double) (-range) && d1 <= (double) range) || withinNoTrack()) { + toRemove.add(entityplayer); + } + } + + this.processToRemove(); + } + + public void processToRemove() { + for (EntityPlayer entityPlayer : toRemove) { + entityPlayer.d(this.tracker); + this.trackedPlayers.remove(entityPlayer); + } + + toRemove.clear(); + } + + public void addNearPlayers() { + addNearPlayers(false); + } + + private void addNearPlayers(boolean updateCooldown) { + if (this.withinNoTrack) return; + if (updateCooldown) this.addRemoveCooldown = addRemoveRate; + this.tracker.world.playerMap.forEachNearby(this.tracker.locX, this.tracker.locY, this.tracker.locZ, this.getRange(), false, addNearPlayersConsumer); + } + + private boolean withinNoTrack() { + return this.withinNoTrack(this.tracker); + } + + private boolean withinNoTrack(Entity entity) { + if (!(entity instanceof EntityPlayer)) return false; // ensure all non-players are always tracked + double xDistSqrd = entity.locX * entity.locX; + double zDistSqrd = entity.locZ * entity.locZ; + + int noTrackDistanceSqrd = entityTracker.getNoTrackDistance() * entityTracker.getNoTrackDistance(); + return noTrackDistanceSqrd != 0 && xDistSqrd <= noTrackDistanceSqrd && zDistSqrd <= noTrackDistanceSqrd; + } + + private final Consumer addNearPlayersConsumer = new Consumer() { + + @Override + public void accept(EntityPlayer entityPlayer) { + if (!SpigotConfig.disableTracking || tracker.passenger == entityPlayer) updatePlayer(entityPlayer); + } + }; + // MineHQ end + + public void track(List list) { + this.n = false; + if (!this.isMoving || this.tracker.e(this.q, this.r, this.s) > 16.0D) { + this.q = this.tracker.locX; + this.r = this.tracker.locY; + this.s = this.tracker.locZ; + this.isMoving = true; + this.n = true; + // this.scanPlayers(list); // MineHQ + } + + if (this.w != this.tracker.vehicle || this.tracker.vehicle != null && this.m % 60 == 0) { + this.w = this.tracker.vehicle; + this.broadcast(new PacketPlayOutAttachEntity(0, this.tracker, this.tracker.vehicle)); + } + + if (this.tracker instanceof EntityItemFrame /*&& this.m % 10 == 0*/) { // CraftBukkit - Moved below, should always enter this block + EntityItemFrame i3 = (EntityItemFrame) this.tracker; + ItemStack i4 = i3.getItem(); + + if (this.m % 10 == 0 && i4 != null && i4.getItem() instanceof ItemWorldMap) { // CraftBukkit - Moved this.m % 10 logic here so item frames do not enter the other blocks + WorldMap i6 = Items.MAP.getSavedMap(i4, this.tracker.world); + Iterator i7 = this.trackedPlayers.iterator(); // CraftBukkit + + while (i7.hasNext()) { + EntityHuman i8 = (EntityHuman) i7.next(); + EntityPlayer i9 = (EntityPlayer) i8; + + i6.a(i9, i4); + Packet j0 = Items.MAP.c(i4, this.tracker.world, i9); + + if (j0 != null) { + i9.playerConnection.sendPacket(j0); + } + } + } + + this.b(); + } else if (this.m % this.c == 0 || this.tracker.al || this.tracker.getDataWatcher().a()) { + int i; + int j; + + if (this.tracker.vehicle == null) { + ++this.v; + i = this.tracker.as.a(this.tracker.locX); + j = MathHelper.floor(this.tracker.locY * 32.0D); + int k = this.tracker.as.a(this.tracker.locZ); + int l = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F); + int i1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F); + int j1 = i - this.xLoc; + int k1 = j - this.yLoc; + int l1 = k - this.zLoc; + Object object = null; + boolean flag = Math.abs(j1) >= 4 || Math.abs(k1) >= 4 || Math.abs(l1) >= 4 || this.m % 60 == 0; + boolean flag1 = Math.abs(l - this.yRot) >= 4 || Math.abs(i1 - this.xRot) >= 4; + + // CraftBukkit start - Code moved from below + if (flag) { + this.xLoc = i; + this.yLoc = j; + this.zLoc = k; + } + + if (flag1) { + this.yRot = l; + this.xRot = i1; + } + // CraftBukkit end + + if (this.m > 0 || this.tracker instanceof EntityArrow) { + if (j1 >= -128 && j1 < 128 && k1 >= -128 && k1 < 128 && l1 >= -128 && l1 < 128 && this.v <= 50 && !this.x) { // Kohi - greatly reduce forced teleport interval + if (flag && flag1) { + object = new PacketPlayOutRelEntityMoveLook(this.tracker.getId(), (byte) j1, (byte) k1, (byte) l1, (byte) l, (byte) i1, tracker.onGround); // Spigot - protocol patch + } else if (flag) { + object = new PacketPlayOutRelEntityMove(this.tracker.getId(), (byte) j1, (byte) k1, (byte) l1, tracker.onGround); // Spigot - protocol patch + } else if (flag1) { + object = new PacketPlayOutEntityLook(this.tracker.getId(), (byte) l, (byte) i1, tracker.onGround); // Spigot - protocol patch + } + } else { + this.v = 0; + object = new PacketPlayOutEntityTeleport(this.tracker.getId(), i, j, k, (byte) l, (byte) i1, tracker.onGround, tracker instanceof EntityFallingBlock || tracker instanceof EntityTNTPrimed); // Spigot - protocol patch // Spigot Update - 20140916a + } + } + + if (this.u) { + double d0 = this.tracker.motX - this.j; + double d1 = this.tracker.motY - this.k; + double d2 = this.tracker.motZ - this.l; + double d3 = 0.02D; + double d4 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d4 > d3 * d3 || d4 > 0.0D && this.tracker.motX == 0.0D && this.tracker.motY == 0.0D && this.tracker.motZ == 0.0D) { + this.j = this.tracker.motX; + this.k = this.tracker.motY; + this.l = this.tracker.motZ; + //if (this.tracker instanceof EntityArrow || this.tracker instanceof EntityProjectile) { + this.broadcast(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.j, this.k, this.l)); + //} + } + } + + if (object != null) { + this.broadcast((Packet) object); + } + + this.b(); + /* CraftBukkit start - Code moved up + if (flag) { + this.xLoc = i; + this.yLoc = j; + this.zLoc = k; + } + + if (flag1) { + this.yRot = l; + this.xRot = i1; + } + // CraftBukkit end */ + + this.x = false; + } else { + i = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F); + j = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F); + boolean flag2 = Math.abs(i - this.yRot) >= 4 || Math.abs(j - this.xRot) >= 4; + + if (flag2) { + this.broadcast(new PacketPlayOutEntityLook(this.tracker.getId(), (byte) i, (byte) j, tracker.onGround)); // Spigot - protocol patch + this.yRot = i; + this.xRot = j; + } + + this.xLoc = this.tracker.as.a(this.tracker.locX); + this.yLoc = MathHelper.floor(this.tracker.locY * 32.0D); + this.zLoc = this.tracker.as.a(this.tracker.locZ); + this.b(); + this.x = true; + } + + i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + if (Math.abs(i - this.i) >= 4) { + this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i)); + this.i = i; + } + + this.tracker.al = false; + } + + ++this.m; + if (this.tracker.velocityChanged) { + // CraftBukkit start - Create PlayerVelocity event + boolean cancelled = false; + + if (this.tracker instanceof EntityPlayer) { + Player player = (Player) this.tracker.getBukkitEntity(); + org.bukkit.util.Vector velocity = player.getVelocity(); + + PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity); + this.tracker.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + cancelled = true; + } else if (!velocity.equals(event.getVelocity())) { + player.setVelocity(velocity); + } + } + + if (!cancelled) { + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker)); + } else if (this.tracker instanceof EntityArrow || this.tracker instanceof EntityProjectile) { + this.broadcast(new PacketPlayOutEntityVelocity(this.tracker)); + } + } + // CraftBukkit end + + this.tracker.velocityChanged = false; + } + } + + private void b() { + DataWatcher datawatcher = this.tracker.getDataWatcher(); + + if (datawatcher.a()) { + // MineHQ start + List changedMetadata = datawatcher.b(); + if (this.doHealthObfuscation()) { + PacketPlayOutEntityMetadata metadataPacket = new PacketPlayOutEntityMetadata(this.tracker.getId(), new ArrayList(changedMetadata), false).obfuscateHealth(); + if (!metadataPacket.didFindHealth() || 1 < metadataPacket.getMetadata().size()) this.broadcast(metadataPacket); + } else { + this.broadcast(new PacketPlayOutEntityMetadata(this.tracker.getId(), changedMetadata, false)); + } + + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), changedMetadata, false)); + } + // MineHQ end + } + + if (this.tracker instanceof EntityLiving) { + AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap(); + Set set = attributemapserver.getAttributes(); + + if (!set.isEmpty()) { + // CraftBukkit start - Send scaled max health + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false); + ((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); // MineHQ + } + // CraftBukkit end + + // MineHQ start + // this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); // CraftBukkit + if (this.tracker.passenger instanceof EntityPlayer) { + ((EntityPlayer) this.tracker.passenger).playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); + } + // MineHQ end + } + + set.clear(); + } + } + + public void broadcast(Packet packet) { + Iterator iterator = this.trackedPlayers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + } + + public void broadcastIncludingSelf(Packet packet) { + this.broadcast(packet); + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).playerConnection.sendPacket(packet); + } + } + + public void a() { + Iterator iterator = this.trackedPlayers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + entityplayer.d(this.tracker); + } + } + + public void a(EntityPlayer entityplayer) { + if (this.trackedPlayers.contains(entityplayer)) { + entityplayer.d(this.tracker); + this.trackedPlayers.remove(entityplayer); + } + } + + public void updatePlayer(EntityPlayer entityplayer) { + // org.spigotmc.AsyncCatcher.catchOp( "player tracker update"); // Spigot // MineHQ + if (entityplayer != this.tracker) { + // MineHQ start - this.tracker.locN / 32 -> this.tracker.locN + double d0 = entityplayer.locX - this.tracker.locX; + double d1 = entityplayer.locZ - this.tracker.locZ; + // MineHQ end + int range = this.getRange(); + + if (d0 >= (double) (-range) && d0 <= (double) range && d1 >= (double) (-range) && d1 <= (double) range) { + if (!this.trackedPlayers.contains(entityplayer) && (this.d(entityplayer) || this.tracker.attachedToPlayer)) { + if (this.tracker instanceof EntityPlayer && withinNoTrack()) return; // MineHQ + // CraftBukkit start - respect vanish API + if (this.tracker instanceof EntityPlayer) { + Player player = ((EntityPlayer) this.tracker).getBukkitEntity(); + if (!entityplayer.getBukkitEntity().canSee(player)) { + return; + } + } + + // entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId())); // + // MineHQ + // CraftBukkit end + + this.trackedPlayers.add(entityplayer); + Packet packet = this.c(); + + // Spigot start - protocol patch + // MineHQ start - tablist stuff + boolean isTarget18 = entityplayer.playerConnection.networkManager.getVersion() > 28; // MineHQ + boolean trackerInstanceOf = this.tracker instanceof EntityPlayer; + if (isTarget18) { + if (trackerInstanceOf) { + entityplayer.playerConnection.sendPacket(PacketPlayOutPlayerInfo.addPlayer((EntityPlayer) tracker)); + entityplayer.playerConnection.sendPacket(PacketPlayOutPlayerInfo.updateDisplayName((EntityPlayer) this.tracker)); + } + entityplayer.playerConnection.sendPacket(packet); + if (trackerInstanceOf && SpigotConfig.onlyCustomTab) entityplayer.playerConnection.sendPacket(PacketPlayOutPlayerInfo.removePlayer((EntityPlayer) tracker)); + } else { + if (tracker instanceof EntityPlayer && !SpigotConfig.onlyCustomTab) { + entityplayer.playerConnection.sendPacket(PacketPlayOutPlayerInfo.addPlayer((EntityPlayer) tracker)); + } + + entityplayer.playerConnection.sendPacket(packet); + } + + // MineHQ end + // Spigot end + + if (!this.tracker.getDataWatcher().d()) { + // MineHQ start + PacketPlayOutEntityMetadata metadataPacket = new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true); + + if (this.doHealthObfuscation()) { + metadataPacket.obfuscateHealth(); + } + + entityplayer.playerConnection.sendPacket(metadataPacket); + // MineHQ end + } + // MineHQ end + + this.j = this.tracker.motX; + this.k = this.tracker.motY; + this.l = this.tracker.motZ; + if (this.u && !(packet instanceof PacketPlayOutSpawnEntityLiving)) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ)); + } + + if (this.tracker.vehicle != null) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(0, this.tracker, this.tracker.vehicle)); + } + + // CraftBukkit start + if (this.tracker.passenger != null) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(0, this.tracker.passenger, this.tracker)); + } + // CraftBukkit end + + if (this.tracker instanceof EntityInsentient && ((EntityInsentient) this.tracker).getLeashHolder() != null) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, this.tracker, ((EntityInsentient) this.tracker).getLeashHolder())); + } + + if (this.tracker instanceof EntityLiving) { + for (int i = 0; i < 5; ++i) { + ItemStack itemstack = ((EntityLiving) this.tracker).getEquipment(i); + + if (itemstack != null) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEquipment(this.tracker.getId(), i, itemstack)); + } + } + } + + if (this.tracker instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) this.tracker; + + if (entityhuman.isSleeping()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutBed(entityhuman, MathHelper.floor(this.tracker.locX), MathHelper.floor(this.tracker.locY), MathHelper.floor(this.tracker.locZ))); + } + } + + // CraftBukkit start - Fix for nonsensical head yaw + // MineHQ start - fix head rotation packet spam (properly) + if (this.tracker instanceof EntityLiving) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F))); + } + // MineHQ end + // CraftBukkit end + + if (this.tracker instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) this.tracker; + Iterator iterator = entityliving.getEffects().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect)); + } + } + } + } else if (this.trackedPlayers.contains(entityplayer)) { + this.trackedPlayers.remove(entityplayer); + entityplayer.d(this.tracker); + } + } + } + + private boolean d(EntityPlayer entityplayer) { + return entityplayer.r().getPlayerChunkMap().a(entityplayer, this.tracker.ah, this.tracker.aj); + } + + // CavePvP start + public void scanPlayers(List list) { + for (int i = 0; i < list.size(); ++i) { + this.updatePlayer((EntityPlayer) list.get(i)); + } + } + // CavePvP end + + // MineHQ start + //public void scanPlayers(List list) { + // for (int i = 0; i < list.size(); ++i) { + // this.updatePlayer((EntityPlayer) list.get(i)); + // } + //} + // MineHQ end + + private Packet c() { + if (this.tracker.dead) { + // CraftBukkit start - Remove useless error spam, just return + // p.warn("Fetching addPacket for removed entity"); + return null; + // CraftBukkit end + } + + if (this.tracker instanceof EntityItem) { + return new PacketPlayOutSpawnEntity(this.tracker, 2, 1); + } else if (this.tracker instanceof EntityPlayer) { + return new PacketPlayOutNamedEntitySpawn((EntityHuman) this.tracker); + } else if (this.tracker instanceof EntityMinecartAbstract) { + EntityMinecartAbstract entityminecartabstract = (EntityMinecartAbstract) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 10, entityminecartabstract.m()); + } else if (this.tracker instanceof EntityBoat) { + return new PacketPlayOutSpawnEntity(this.tracker, 1); + } else if (!(this.tracker instanceof IAnimal) && !(this.tracker instanceof EntityEnderDragon)) { + if (this.tracker instanceof EntityFishingHook) { + EntityHuman entityhuman = ((EntityFishingHook) this.tracker).owner; + + return new PacketPlayOutSpawnEntity(this.tracker, 90, entityhuman != null ? entityhuman.getId() : this.tracker.getId()); + } else if (this.tracker instanceof EntityArrow) { + Entity entity = ((EntityArrow) this.tracker).shooter; + + return new PacketPlayOutSpawnEntity(this.tracker, 60, entity != null ? entity.getId() : this.tracker.getId()); + } else if (this.tracker instanceof EntitySnowball) { + return new PacketPlayOutSpawnEntity(this.tracker, 61); + } else if (this.tracker instanceof EntityPotion) { + return new PacketPlayOutSpawnEntity(this.tracker, 73, ((EntityPotion) this.tracker).getPotionValue()); + } else if (this.tracker instanceof EntityThrownExpBottle) { + return new PacketPlayOutSpawnEntity(this.tracker, 75); + } else if (this.tracker instanceof EntityEnderPearl) { + return new PacketPlayOutSpawnEntity(this.tracker, 65); + } else if (this.tracker instanceof EntityEnderSignal) { + return new PacketPlayOutSpawnEntity(this.tracker, 72); + } else if (this.tracker instanceof EntityFireworks) { + return new PacketPlayOutSpawnEntity(this.tracker, 76); + } else { + PacketPlayOutSpawnEntity packetplayoutspawnentity; + + if (this.tracker instanceof EntityFireball) { + EntityFireball entityfireball = (EntityFireball) this.tracker; + + packetplayoutspawnentity = null; + byte b0 = 63; + + if (this.tracker instanceof EntitySmallFireball) { + b0 = 64; + } else if (this.tracker instanceof EntityWitherSkull) { + b0 = 66; + } + + if (entityfireball.shooter != null) { + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, ((EntityFireball) this.tracker).shooter.getId()); + } else { + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, 0); + } + + packetplayoutspawnentity.d((int) (entityfireball.dirX * 8000.0D)); + packetplayoutspawnentity.e((int) (entityfireball.dirY * 8000.0D)); + packetplayoutspawnentity.f((int) (entityfireball.dirZ * 8000.0D)); + return packetplayoutspawnentity; + } else if (this.tracker instanceof EntityEgg) { + return new PacketPlayOutSpawnEntity(this.tracker, 62); + } else if (this.tracker instanceof EntityTNTPrimed) { + return new PacketPlayOutSpawnEntity(this.tracker, 50); + } else if (this.tracker instanceof EntityEnderCrystal) { + return new PacketPlayOutSpawnEntity(this.tracker, 51); + } else if (this.tracker instanceof EntityFallingBlock) { + EntityFallingBlock entityfallingblock = (EntityFallingBlock) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 70, Block.getId(entityfallingblock.f()) | entityfallingblock.data << 16); + } else if (this.tracker instanceof EntityPainting) { + return new PacketPlayOutSpawnEntityPainting((EntityPainting) this.tracker); + } else if (this.tracker instanceof EntityItemFrame) { + EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker; + + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, 71, entityitemframe.direction); + packetplayoutspawnentity.a(MathHelper.d((float) (entityitemframe.x * 32))); + packetplayoutspawnentity.b(MathHelper.d((float) (entityitemframe.y * 32))); + packetplayoutspawnentity.c(MathHelper.d((float) (entityitemframe.z * 32))); + return packetplayoutspawnentity; + } else if (this.tracker instanceof EntityLeash) { + EntityLeash entityleash = (EntityLeash) this.tracker; + + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, 77); + packetplayoutspawnentity.a(MathHelper.d((float) (entityleash.x * 32))); + packetplayoutspawnentity.b(MathHelper.d((float) (entityleash.y * 32))); + packetplayoutspawnentity.c(MathHelper.d((float) (entityleash.z * 32))); + return packetplayoutspawnentity; + } else if (this.tracker instanceof EntityExperienceOrb) { + return new PacketPlayOutSpawnEntityExperienceOrb((EntityExperienceOrb) this.tracker); + } else { + throw new IllegalArgumentException("Don\'t know how to add " + this.tracker.getClass() + "!"); + } + } + } else { + this.i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + return new PacketPlayOutSpawnEntityLiving((EntityLiving) this.tracker); + } + } + + public void clear(EntityPlayer entityplayer) { + org.spigotmc.AsyncCatcher.catchOp( "player tracker clear"); // Spigot + if (this.trackedPlayers.remove(entityplayer)) { // MineHQ + entityplayer.d(this.tracker); + } + } + + public boolean doHealthObfuscation() { + return this.tracker.isAlive() && (this.tracker instanceof EntityPlayer); + } + + // MineHQ start + public int getRange() { + if (this.tracker.passenger == null) { + return this.b; + } + return Math.max(this.b, org.spigotmc.TrackingRange.getEntityTrackingRange(this.tracker.passenger, 0)); + } + // MineHQ end + +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityWither.java b/vspigot-server/src/main/java/net/minecraft/server/EntityWither.java new file mode 100644 index 0000000..f004e9a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityWither.java @@ -0,0 +1,478 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; +// CraftBukkit end + +public class EntityWither extends EntityMonster implements IRangedEntity { + + private float[] bp = new float[2]; + private float[] bq = new float[2]; + private float[] br = new float[2]; + private float[] bs = new float[2]; + private int[] bt = new int[2]; + private int[] bu = new int[2]; + private int bv; + private static final IEntitySelector bw = new EntitySelectorNotUndead(); + + public EntityWither(World world) { + super(world); + this.setHealth(this.getMaxHealth()); + this.a(0.9F, 4.0F); + this.fireProof = true; + this.getNavigation().e(true); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalArrowAttack(this, 1.0D, 40, 20.0F)); + this.goalSelector.a(5, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false)); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityInsentient.class, 0, false, false, bw)); + this.b = 50; + } + + protected void c() { + super.c(); + this.datawatcher.a(17, new Integer(0)); + this.datawatcher.a(18, new Integer(0)); + this.datawatcher.a(19, new Integer(0)); + this.datawatcher.a(20, new Integer(0)); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Invul", this.ca()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.s(nbttagcompound.getInt("Invul")); + } + + protected String t() { + return "mob.wither.idle"; + } + + protected String aT() { + return "mob.wither.hurt"; + } + + protected String aU() { + return "mob.wither.death"; + } + + public void e() { + this.motY *= 0.6000000238418579D; + double d0; + double d1; + double d2; + + if (!this.world.isStatic && this.t(0) > 0) { + Entity entity = this.world.getEntity(this.t(0)); + + if (entity != null) { + if (this.locY < entity.locY || !this.cb() && this.locY < entity.locY + 5.0D) { + if (this.motY < 0.0D) { + this.motY = 0.0D; + } + + this.motY += (0.5D - this.motY) * 0.6000000238418579D; + } + + double d3 = entity.locX - this.locX; + + d0 = entity.locZ - this.locZ; + d1 = d3 * d3 + d0 * d0; + if (d1 > 9.0D) { + d2 = (double) MathHelper.sqrt(d1); + this.motX += (d3 / d2 * 0.5D - this.motX) * 0.6000000238418579D; + this.motZ += (d0 / d2 * 0.5D - this.motZ) * 0.6000000238418579D; + } + } + } + + if (this.motX * this.motX + this.motZ * this.motZ > 0.05000000074505806D) { + this.yaw = (float) Math.atan2(this.motZ, this.motX) * 57.295776F - 90.0F; + } + + super.e(); + + int i; + + for (i = 0; i < 2; ++i) { + this.bs[i] = this.bq[i]; + this.br[i] = this.bp[i]; + } + + int j; + + for (i = 0; i < 2; ++i) { + j = this.t(i + 1); + Entity entity1 = null; + + if (j > 0) { + entity1 = this.world.getEntity(j); + } + + if (entity1 != null) { + d0 = this.u(i + 1); + d1 = this.v(i + 1); + d2 = this.w(i + 1); + double d4 = entity1.locX - d0; + double d5 = entity1.locY + (double) entity1.getHeadHeight() - d1; + double d6 = entity1.locZ - d2; + double d7 = (double) MathHelper.sqrt(d4 * d4 + d6 * d6); + float f = (float) (Math.atan2(d6, d4) * 180.0D / 3.1415927410125732D) - 90.0F; + float f1 = (float) (-(Math.atan2(d5, d7) * 180.0D / 3.1415927410125732D)); + + this.bp[i] = this.b(this.bp[i], f1, 40.0F); + this.bq[i] = this.b(this.bq[i], f, 10.0F); + } else { + this.bq[i] = this.b(this.bq[i], this.aM, 10.0F); + } + } + + boolean flag = this.cb(); + + for (j = 0; j < 3; ++j) { + double d8 = this.u(j); + double d9 = this.v(j); + double d10 = this.w(j); + + this.world.addParticle("smoke", d8 + this.random.nextGaussian() * 0.30000001192092896D, d9 + this.random.nextGaussian() * 0.30000001192092896D, d10 + this.random.nextGaussian() * 0.30000001192092896D, 0.0D, 0.0D, 0.0D); + if (flag && this.world.random.nextInt(4) == 0) { + this.world.addParticle("mobSpell", d8 + this.random.nextGaussian() * 0.30000001192092896D, d9 + this.random.nextGaussian() * 0.30000001192092896D, d10 + this.random.nextGaussian() * 0.30000001192092896D, 0.699999988079071D, 0.699999988079071D, 0.5D); + } + } + + if (this.ca() > 0) { + for (j = 0; j < 3; ++j) { + this.world.addParticle("mobSpell", this.locX + this.random.nextGaussian() * 1.0D, this.locY + (double) (this.random.nextFloat() * 3.3F), this.locZ + this.random.nextGaussian() * 1.0D, 0.699999988079071D, 0.699999988079071D, 0.8999999761581421D); + } + } + } + + protected void bn() { + int i; + + if (this.ca() > 0) { + i = this.ca() - 1; + if (i <= 0) { + // CraftBukkit start + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 7.0F, false); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.world.createExplosion(this, this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, event.getRadius(), event.getFire(), this.world.getGameRules().getBoolean("mobGriefing")); + } + // CraftBukkit end + + //this.world.createExplosion(this, this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, 7.0F, false, this.world.getGameRules().getBoolean("mobGriefing")); // Poweruser - already done in the event handling + // CraftBukkit start - Use relative location for far away sounds + //this.world.b(1013, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; + for (EntityPlayer player : (List) this.world.players) { + double deltaX = this.locX - player.locX; + double deltaZ = this.locZ - player.locZ; + double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; + if ( world.spigotConfig.witherSpawnSoundRadius > 0 && distanceSquared > world.spigotConfig.witherSpawnSoundRadius * world.spigotConfig.witherSpawnSoundRadius ) continue; // Spigot + if (distanceSquared > viewDistance * viewDistance) { + double deltaLength = Math.sqrt(distanceSquared); + double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; + double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1013, (int) relativeX, (int) this.locY, (int) relativeZ, 0, true)); + } else { + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1013, (int) this.locX, (int) this.locY, (int) this.locZ, 0, true)); + } + } + // CraftBukkit end + } + + this.s(i); + if (this.ticksLived % 10 == 0) { + this.heal(10.0F, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit + } + } else { + super.bn(); + + int j; + + for (i = 1; i < 3; ++i) { + if (this.ticksLived >= this.bt[i - 1]) { + this.bt[i - 1] = this.ticksLived + 10 + this.random.nextInt(10); + if (this.world.difficulty == EnumDifficulty.NORMAL || this.world.difficulty == EnumDifficulty.HARD) { + int i1001 = i - 1; + int i1003 = this.bu[i - 1]; + + this.bu[i1001] = this.bu[i - 1] + 1; + if (i1003 > 15) { + float f = 10.0F; + float f1 = 5.0F; + double d0 = MathHelper.a(this.random, this.locX - (double) f, this.locX + (double) f); + double d1 = MathHelper.a(this.random, this.locY - (double) f1, this.locY + (double) f1); + double d2 = MathHelper.a(this.random, this.locZ - (double) f, this.locZ + (double) f); + + this.a(i + 1, d0, d1, d2, true); + this.bu[i - 1] = 0; + } + } + + j = this.t(i); + if (j > 0) { + Entity entity = this.world.getEntity(j); + + if (entity != null && entity.isAlive() && this.f(entity) <= 900.0D && this.hasLineOfSight(entity)) { + this.a(i + 1, (EntityLiving) entity); + this.bt[i - 1] = this.ticksLived + 40 + this.random.nextInt(20); + this.bu[i - 1] = 0; + } else { + this.b(i, 0); + } + } else { + List list = this.world.a(EntityLiving.class, this.boundingBox.grow(20.0D, 8.0D, 20.0D), bw); + + for (int i1 = 0; i1 < 10 && !list.isEmpty(); ++i1) { + EntityLiving entityliving = (EntityLiving) list.get(this.random.nextInt(list.size())); + + if (entityliving != this && entityliving.isAlive() && this.hasLineOfSight(entityliving)) { + if (entityliving instanceof EntityHuman) { + if (!((EntityHuman) entityliving).abilities.isInvulnerable) { + this.b(i, entityliving.getId()); + } + } else { + this.b(i, entityliving.getId()); + } + break; + } + + list.remove(entityliving); + } + } + } + } + + if (this.getGoalTarget() != null) { + this.b(0, this.getGoalTarget().getId()); + } else { + this.b(0, 0); + } + + if (this.bv > 0) { + --this.bv; + if (this.bv == 0 && this.world.getGameRules().getBoolean("mobGriefing")) { + i = MathHelper.floor(this.locY); + j = MathHelper.floor(this.locX); + int j1 = MathHelper.floor(this.locZ); + boolean flag = false; + + for (int k1 = -1; k1 <= 1; ++k1) { + for (int l1 = -1; l1 <= 1; ++l1) { + for (int i2 = 0; i2 <= 3; ++i2) { + int j2 = j + k1; + int k2 = i + i2; + int l2 = j1 + l1; + Block block = this.world.getType(j2, k2, l2); + + if (block.getMaterial() != Material.AIR && block != Blocks.BEDROCK && block != Blocks.ENDER_PORTAL && block != Blocks.ENDER_PORTAL_FRAME && block != Blocks.COMMAND) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(this, j2, k2, l2, Blocks.AIR, 0).isCancelled()) { + continue; + } + // CraftBukkit end + + flag = this.world.setAir(j2, k2, l2, true) || flag; + } + } + } + } + + if (flag) { + this.world.a((EntityHuman) null, 1012, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + } + } + } + + if (this.ticksLived % 20 == 0) { + this.heal(1.0F, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit + } + } + } + + public void bZ() { + this.s(220); + this.setHealth(this.getMaxHealth() / 3.0F); + } + + public void as() {} + + public int aV() { + return 4; + } + + private double u(int i) { + if (i <= 0) { + return this.locX; + } else { + float f = (this.aM + (float) (180 * (i - 1))) / 180.0F * 3.1415927F; + float f1 = MathHelper.cos(f); + + return this.locX + (double) f1 * 1.3D; + } + } + + private double v(int i) { + return i <= 0 ? this.locY + 3.0D : this.locY + 2.2D; + } + + private double w(int i) { + if (i <= 0) { + return this.locZ; + } else { + float f = (this.aM + (float) (180 * (i - 1))) / 180.0F * 3.1415927F; + float f1 = MathHelper.sin(f); + + return this.locZ + (double) f1 * 1.3D; + } + } + + private float b(float f, float f1, float f2) { + float f3 = MathHelper.g(f1 - f); + + if (f3 > f2) { + f3 = f2; + } + + if (f3 < -f2) { + f3 = -f2; + } + + return f + f3; + } + + private void a(int i, EntityLiving entityliving) { + this.a(i, entityliving.locX, entityliving.locY + (double) entityliving.getHeadHeight() * 0.5D, entityliving.locZ, i == 0 && this.random.nextFloat() < 0.001F); + } + + private void a(int i, double d0, double d1, double d2, boolean flag) { + this.world.a((EntityHuman) null, 1014, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + double d3 = this.u(i); + double d4 = this.v(i); + double d5 = this.w(i); + double d6 = d0 - d3; + double d7 = d1 - d4; + double d8 = d2 - d5; + EntityWitherSkull entitywitherskull = new EntityWitherSkull(this.world, this, d6, d7, d8); + + if (flag) { + entitywitherskull.setCharged(true); + } + + entitywitherskull.locY = d4; + entitywitherskull.locX = d3; + entitywitherskull.locZ = d5; + this.world.addEntity(entitywitherskull); + } + + public void a(EntityLiving entityliving, float f) { + this.a(0, entityliving); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else if (damagesource == DamageSource.DROWN) { + return false; + } else if (this.ca() > 0) { + return false; + } else { + Entity entity; + + if (this.cb()) { + entity = damagesource.i(); + if (entity instanceof EntityArrow) { + return false; + } + } + + entity = damagesource.getEntity(); + if (entity != null && !(entity instanceof EntityHuman) && entity instanceof EntityLiving && ((EntityLiving) entity).getMonsterType() == this.getMonsterType()) { + return false; + } else { + if (this.bv <= 0) { + this.bv = 20; + } + + for (int i = 0; i < this.bu.length; ++i) { + this.bu[i] += 3; + } + + return super.damageEntity(damagesource, f); + } + } + } + + protected void dropDeathLoot(boolean flag, int i) { + this.a(Items.NETHER_STAR, 1); + if (!this.world.isStatic) { + Iterator iterator = this.world.a(EntityHuman.class, this.boundingBox.grow(50.0D, 100.0D, 50.0D)).iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + entityhuman.a((Statistic) AchievementList.J); + } + } + } + + protected void w() { + this.aU = 0; + } + + protected void b(float f) {} + + public void addEffect(MobEffect mobeffect) {} + + protected boolean bk() { + return true; + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(300.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.6000000238418579D); + this.getAttributeInstance(GenericAttributes.b).setValue(40.0D); + } + + public int ca() { + return this.datawatcher.getInt(20); + } + + public void s(int i) { + this.datawatcher.watch(20, Integer.valueOf(i)); + } + + public int t(int i) { + return this.datawatcher.getInt(17 + i); + } + + public void b(int i, int j) { + this.datawatcher.watch(17 + i, Integer.valueOf(j)); + } + + public boolean cb() { + return this.getHealth() <= this.getMaxHealth() / 2.0F; + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + public void mount(Entity entity) { + this.vehicle = null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityWitherSkull.java b/vspigot-server/src/main/java/net/minecraft/server/EntityWitherSkull.java new file mode 100644 index 0000000..144ed17 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityWitherSkull.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit + +public class EntityWitherSkull extends EntityFireball { + + public EntityWitherSkull(World world) { + super(world); + this.a(0.3125F, 0.3125F); + } + + public EntityWitherSkull(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(world, entityliving, d0, d1, d2); + this.a(0.3125F, 0.3125F); + } + + protected float e() { + return this.isCharged() ? 0.73F : super.e(); + } + + public boolean isBurning() { + return false; + } + + public float a(Explosion explosion, World world, int i, int j, int k, Block block) { + float f = super.a(explosion, world, i, j, k, block); + + if (this.isCharged() && block != Blocks.BEDROCK && block != Blocks.ENDER_PORTAL && block != Blocks.ENDER_PORTAL_FRAME && block != Blocks.COMMAND) { + f = Math.min(0.8F, f); + } + + return f; + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isStatic) { + if (movingobjectposition.entity != null) { + // Spigot start + boolean didDamage = false; + if (this.shooter != null) { + didDamage = movingobjectposition.entity.damageEntity(DamageSource.mobAttack(this.shooter), 8.0F); + if (didDamage && !movingobjectposition.entity.isAlive()) { + this.shooter.heal(5.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER); // CraftBukkit + } + } else { + didDamage = movingobjectposition.entity.damageEntity(DamageSource.MAGIC, 5.0F); + } + + if (didDamage && movingobjectposition.entity instanceof EntityLiving) { + // Spigot end + byte b0 = 0; + + if (this.world.difficulty == EnumDifficulty.NORMAL) { + b0 = 10; + } else if (this.world.difficulty == EnumDifficulty.HARD) { + b0 = 40; + } + + if (b0 > 0) { + ((EntityLiving) movingobjectposition.entity).addEffect(new MobEffect(MobEffectList.WITHER.id, 20 * b0, 1)); + } + } + } + + // CraftBukkit start + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), this.world.getGameRules().getBoolean("mobGriefing")); + } + // CraftBukkit end + + this.die(); + } + } + + public boolean R() { + return false; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + protected void c() { + this.datawatcher.a(10, Byte.valueOf((byte) 0)); + } + + public boolean isCharged() { + return this.datawatcher.getByte(10) == 1; + } + + public void setCharged(boolean flag) { + this.datawatcher.watch(10, Byte.valueOf((byte) (flag ? 1 : 0))); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityWolf.java b/vspigot-server/src/main/java/net/minecraft/server/EntityWolf.java new file mode 100644 index 0000000..aed6496 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityWolf.java @@ -0,0 +1,379 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTargetEvent.TargetReason; +// CraftBukkit end + +public class EntityWolf extends EntityTameableAnimal { + + private float bq; + private float br; + private boolean bs; + private boolean bt; + private float bu; + private float bv; + + public EntityWolf(World world) { + super(world); + this.a(0.6F, 0.8F); + this.getNavigation().a(true); + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, this.bp); + this.goalSelector.a(3, new PathfinderGoalLeapAtTarget(this, 0.4F)); + this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, 1.0D, true)); + this.goalSelector.a(5, new PathfinderGoalFollowOwner(this, 1.0D, 10.0F, 2.0F)); + this.goalSelector.a(6, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(8, new PathfinderGoalBeg(this, 8.0F)); + this.goalSelector.a(9, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(9, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalOwnerHurtByTarget(this)); + this.targetSelector.a(2, new PathfinderGoalOwnerHurtTarget(this)); + this.targetSelector.a(3, new PathfinderGoalHurtByTarget(this, true)); + this.targetSelector.a(4, new PathfinderGoalRandomTargetNonTamed(this, EntitySheep.class, 200, false)); + this.setTamed(false); + } + + protected void aD() { + super.aD(); + this.getAttributeInstance(GenericAttributes.d).setValue(0.30000001192092896D); + if (this.isTamed()) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(20.0D); + } else { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + } + } + + public boolean bk() { + return true; + } + + public void setGoalTarget(EntityLiving entityliving) { + super.setGoalTarget(entityliving); + if (entityliving == null) { + this.setAngry(false); + } else if (!this.isTamed()) { + this.setAngry(true); + } + } + + protected void bp() { + this.datawatcher.watch(18, Float.valueOf(this.getHealth())); + } + + protected void c() { + super.c(); + this.datawatcher.a(18, new Float(this.getHealth())); + this.datawatcher.a(19, new Byte((byte) 0)); + this.datawatcher.a(20, new Byte((byte) BlockCloth.b(1))); + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.wolf.step", 0.15F, 1.0F); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("Angry", this.isAngry()); + nbttagcompound.setByte("CollarColor", (byte) this.getCollarColor()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setAngry(nbttagcompound.getBoolean("Angry")); + if (nbttagcompound.hasKeyOfType("CollarColor", 99)) { + this.setCollarColor(nbttagcompound.getByte("CollarColor")); + } + } + + protected String t() { + // CraftBukkit - (getFloat(18) < 10) -> (getFloat(18) < this.getMaxHealth() / 2) + return this.isAngry() ? "mob.wolf.growl" : (this.random.nextInt(3) == 0 ? (this.isTamed() && this.datawatcher.getFloat(18) < (this.getMaxHealth() / 2) ? "mob.wolf.whine" : "mob.wolf.panting") : "mob.wolf.bark"); + } + + protected String aT() { + return "mob.wolf.hurt"; + } + + protected String aU() { + return "mob.wolf.death"; + } + + protected float bf() { + return 0.4F; + } + + protected Item getLoot() { + return Item.getById(-1); + } + + public void e() { + super.e(); + if (!this.world.isStatic && this.bs && !this.bt && !this.bS() && this.onGround && !this.isSearchingForAPath()) { // Poweruser + this.bt = true; + this.bu = 0.0F; + this.bv = 0.0F; + this.world.broadcastEntityEffect(this, (byte) 8); + } + } + + public void h() { + super.h(); + this.br = this.bq; + if (this.ck()) { + this.bq += (1.0F - this.bq) * 0.4F; + } else { + this.bq += (0.0F - this.bq) * 0.4F; + } + + if (this.ck()) { + this.g = 10; + } + + if (this.L()) { + this.bs = true; + this.bt = false; + this.bu = 0.0F; + this.bv = 0.0F; + } else if ((this.bs || this.bt) && this.bt) { + if (this.bu == 0.0F) { + this.makeSound("mob.wolf.shake", this.bf(), (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + } + + this.bv = this.bu; + this.bu += 0.05F; + if (this.bv >= 2.0F) { + this.bs = false; + this.bt = false; + this.bv = 0.0F; + this.bu = 0.0F; + } + + if (this.bu > 0.4F) { + float f = (float) this.boundingBox.b; + int i = (int) (MathHelper.sin((this.bu - 0.4F) * 3.1415927F) * 7.0F); + + for (int j = 0; j < i; ++j) { + float f1 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F; + float f2 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F; + + this.world.addParticle("splash", this.locX + (double) f1, (double) (f + 0.8F), this.locZ + (double) f2, this.motX, this.motY, this.motZ); + } + } + } + } + + public float getHeadHeight() { + return this.length * 0.8F; + } + + public int x() { + return this.isSitting() ? 20 : super.x(); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable()) { + return false; + } else { + Entity entity = damagesource.getEntity(); + + this.bp.setSitting(false); + if (entity != null && !(entity instanceof EntityHuman) && !(entity instanceof EntityArrow)) { + f = (f + 1.0F) / 2.0F; + } + + return super.damageEntity(damagesource, f); + } + } + + public boolean n(Entity entity) { + int i = this.isTamed() ? 4 : 2; + + return entity.damageEntity(DamageSource.mobAttack(this), (float) i); + } + + public void setTamed(boolean flag) { + super.setTamed(flag); + if (flag) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(20.0D); + } else { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + } + } + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + if (this.isTamed()) { + if (itemstack != null) { + if (itemstack.getItem() instanceof ItemFood) { + ItemFood itemfood = (ItemFood) itemstack.getItem(); + + if (itemfood.i() && this.datawatcher.getFloat(18) < 20.0F) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + this.heal((float) itemfood.getNutrition(itemstack), org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // CraftBukkit + if (itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + + return true; + } + } else if (itemstack.getItem() == Items.INK_SACK) { + int i = BlockCloth.b(itemstack.getData()); + + if (i != this.getCollarColor()) { + this.setCollarColor(i); + if (!entityhuman.abilities.canInstantlyBuild && --itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + + return true; + } + } + } + + if (this.e(entityhuman) && !this.world.isStatic && !this.c(itemstack)) { + this.bp.setSitting(!this.isSitting()); + this.bc = false; + this.setPathEntity((PathEntity) null); + this.setTarget((Entity) null); + // CraftBukkit start + if (this.getGoalTarget() != null) { + CraftEventFactory.callEntityTargetEvent(this, null, TargetReason.FORGOT_TARGET); + } + // CraftBukkit end + this.setGoalTarget((EntityLiving) null); + } + } else if (itemstack != null && itemstack.getItem() == Items.BONE && !this.isAngry()) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + if (itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + + if (!this.world.isStatic) { + // CraftBukkit - added event call and isCancelled check. + if (this.random.nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) { + this.setTamed(true); + this.setPathEntity((PathEntity) null); + // CraftBukkit start + if (this.getGoalTarget() != null) { + CraftEventFactory.callEntityTargetEvent(this, null, TargetReason.FORGOT_TARGET); + } + // CraftBukkit end + this.setGoalTarget((EntityLiving) null); + this.bp.setSitting(true); + this.setHealth(this.getMaxHealth()); // CraftBukkit - 20.0 -> getMaxHealth() + this.setOwnerUUID(entityhuman.getUniqueID().toString()); + this.i(true); + this.world.broadcastEntityEffect(this, (byte) 7); + } else { + this.i(false); + this.world.broadcastEntityEffect(this, (byte) 6); + } + } + + return true; + } + + return super.a(entityhuman); + } + + public boolean c(ItemStack itemstack) { + return itemstack == null ? false : (!(itemstack.getItem() instanceof ItemFood) ? false : ((ItemFood) itemstack.getItem()).i()); + } + + public int bB() { + return 8; + } + + public boolean isAngry() { + return (this.datawatcher.getByte(16) & 2) != 0; + } + + public void setAngry(boolean flag) { + byte b0 = this.datawatcher.getByte(16); + + if (flag) { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 | 2))); + } else { + this.datawatcher.watch(16, Byte.valueOf((byte) (b0 & -3))); + } + } + + public int getCollarColor() { + return this.datawatcher.getByte(20) & 15; + } + + public void setCollarColor(int i) { + this.datawatcher.watch(20, Byte.valueOf((byte) (i & 15))); + } + + public EntityWolf b(EntityAgeable entityageable) { + EntityWolf entitywolf = new EntityWolf(this.world); + String s = this.getOwnerUUID(); + + if (s != null && s.trim().length() > 0) { + entitywolf.setOwnerUUID(s); + entitywolf.setTamed(true); + } + + return entitywolf; + } + + public void m(boolean flag) { + if (flag) { + this.datawatcher.watch(19, Byte.valueOf((byte) 1)); + } else { + this.datawatcher.watch(19, Byte.valueOf((byte) 0)); + } + } + + public boolean mate(EntityAnimal entityanimal) { + if (entityanimal == this) { + return false; + } else if (!this.isTamed()) { + return false; + } else if (!(entityanimal instanceof EntityWolf)) { + return false; + } else { + EntityWolf entitywolf = (EntityWolf) entityanimal; + + return !entitywolf.isTamed() ? false : (entitywolf.isSitting() ? false : this.ce() && entitywolf.ce()); + } + } + + public boolean ck() { + return this.datawatcher.getByte(19) == 1; + } + + protected boolean isTypeNotPersistent() { + return !this.isTamed() /*&& this.ticksLived > 2400*/; // CraftBukkit + } + + public boolean a(EntityLiving entityliving, EntityLiving entityliving1) { + if (!(entityliving instanceof EntityCreeper) && !(entityliving instanceof EntityGhast)) { + if (entityliving instanceof EntityWolf) { + EntityWolf entitywolf = (EntityWolf) entityliving; + + if (entitywolf.isTamed() && entitywolf.getOwner() == entityliving1) { + return false; + } + } + + return entityliving instanceof EntityHuman && entityliving1 instanceof EntityHuman && !((EntityHuman) entityliving1).a((EntityHuman) entityliving) ? false : !(entityliving instanceof EntityHorse) || !((EntityHorse) entityliving).isTame(); + } else { + return false; + } + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.b(entityageable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/EntityZombie.java b/vspigot-server/src/main/java/net/minecraft/server/EntityZombie.java new file mode 100644 index 0000000..3454a05 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/EntityZombie.java @@ -0,0 +1,526 @@ +package net.minecraft.server; + +import java.util.Calendar; +import java.util.List; +import java.util.UUID; + + +//CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityTargetEvent; +//CraftBukkit end + +public class EntityZombie extends EntityMonster { + + protected static final IAttribute bp = (new AttributeRanged("zombie.spawnReinforcements", 0.0D, 0.0D, 1.0D)).a("Spawn Reinforcements Chance"); + private static final UUID bq = UUID.fromString("B9766B59-9566-4402-BC1F-2EE2A276D836"); + // PaperSpigot - Configurable baby zombie movement speed + private static final AttributeModifier br = new AttributeModifier(bq, "Baby speed boost", org.github.paperspigot.PaperSpigotConfig.babyZombieMovementSpeed, 1); + private final PathfinderGoalBreakDoor bs = new PathfinderGoalBreakDoor(this); + private int bt; + private boolean bu = false; + private float bv = -1.0F; + private float bw; + private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field + + public EntityZombie(World world) { + super(world); + this.getNavigation().b(true); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, EntityHuman.class, 1.0D, false)); + if ( world.spigotConfig.zombieAggressiveTowardsVillager ) { this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityVillager.class, 1.0D, true)); } // Spigot + this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalMoveThroughVillage(this, 1.0D, false)); + this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true)); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 0, true)); + if ( world.spigotConfig.zombieAggressiveTowardsVillager ) { this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityVillager.class, 0, false)); } // Spigot + this.a(0.6F, 1.8F); + } + + protected void aD() { + super.aD(); + // Kohi - change follow range from 40.0 to 16.0 for performance + this.getAttributeInstance(GenericAttributes.b).setValue(16.0D); + this.getAttributeInstance(GenericAttributes.d).setValue(0.23000000417232513D); + this.getAttributeInstance(GenericAttributes.e).setValue(3.0D); + this.getAttributeMap().b(bp).setValue(this.random.nextDouble() * 0.10000000149011612D); + } + + protected void c() { + super.c(); + this.getDataWatcher().a(12, Byte.valueOf((byte) 0)); + this.getDataWatcher().a(13, Byte.valueOf((byte) 0)); + this.getDataWatcher().a(14, Byte.valueOf((byte) 0)); + } + + public int aV() { + int i = super.aV() + 2; + + if (i > 20) { + i = 20; + } + + return i; + } + + protected boolean bk() { + return true; + } + + public boolean bZ() { + return this.bu; + } + + public void a(boolean flag) { + if (this.bu != flag) { + this.bu = flag; + if (flag) { + this.goalSelector.a(1, this.bs); + } else { + this.goalSelector.a((PathfinderGoal) this.bs); + } + } + } + + public boolean isBaby() { + return this.getDataWatcher().getByte(12) == 1; + } + + protected int getExpValue(EntityHuman entityhuman) { + if (this.isBaby()) { + this.b = (int) ((float) this.b * 2.5F); + } + + return super.getExpValue(entityhuman); + } + + public void setBaby(boolean flag) { + this.getDataWatcher().watch(12, Byte.valueOf((byte) (flag ? 1 : 0))); + if (this.world != null && !this.world.isStatic) { + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.d); + + attributeinstance.b(br); + if (flag) { + attributeinstance.a(br); + } + } + + this.k(flag); + } + + public boolean isVillager() { + return this.getDataWatcher().getByte(13) == 1; + } + + public void setVillager(boolean flag) { + this.getDataWatcher().watch(13, Byte.valueOf((byte) (flag ? 1 : 0))); + } + + public void e() { + if (this.world.w() && !this.world.isStatic && !this.isBaby()) { + float f = this.d(1.0F); + + if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.world.i(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ))) { + boolean flag = true; + ItemStack itemstack = this.getEquipment(4); + + if (itemstack != null) { + if (itemstack.g()) { + itemstack.setData(itemstack.j() + this.random.nextInt(2)); + if (itemstack.j() >= itemstack.l()) { + this.a(itemstack); + this.setEquipment(4, (ItemStack) null); + } + } + + flag = false; + } + + if (flag) { + // CraftBukkit start + EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setOnFire(event.getDuration()); + } + // CraftBukkit end + } + } + } + + if (this.am() && this.getGoalTarget() != null && this.vehicle instanceof EntityChicken) { + ((EntityInsentient) this.vehicle).getNavigation().a(this.getNavigation().e(), 1.5D); + } + + super.e(); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (!super.damageEntity(damagesource, f)) { + return false; + } else { + EntityLiving entityliving = this.getGoalTarget(); + + if (entityliving == null && this.bT() instanceof EntityLiving) { + entityliving = (EntityLiving) this.bT(); + } + + if (entityliving == null && damagesource.getEntity() instanceof EntityLiving) { + entityliving = (EntityLiving) damagesource.getEntity(); + } + + if (entityliving != null && this.world.difficulty == EnumDifficulty.HARD && (double) this.random.nextFloat() < this.getAttributeInstance(bp).getValue()) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + EntityZombie entityzombie = new EntityZombie(this.world); + + for (int l = 0; l < 50; ++l) { + int i1 = i + MathHelper.nextInt(this.random, 7, 40) * MathHelper.nextInt(this.random, -1, 1); + int j1 = j + MathHelper.nextInt(this.random, 7, 40) * MathHelper.nextInt(this.random, -1, 1); + int k1 = k + MathHelper.nextInt(this.random, 7, 40) * MathHelper.nextInt(this.random, -1, 1); + + if (World.a((IBlockAccess) this.world, i1, j1 - 1, k1) && !this.world.isLightLevel(i1, j1, k1, 10)) { // MineHQ + entityzombie.setPosition((double) i1, (double) j1, (double) k1); + if (this.world.b(entityzombie.boundingBox) && this.world.getCubes(entityzombie, entityzombie.boundingBox).isEmpty() && !this.world.containsLiquid(entityzombie.boundingBox)) { + this.world.addEntity(entityzombie, CreatureSpawnEvent.SpawnReason.REINFORCEMENTS); // CraftBukkit + // CraftBukkit start - call EntityTargetEvent + org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(entityzombie, entityliving, EntityTargetEvent.TargetReason.REINFORCEMENT_TARGET); + if (!event.isCancelled()) { + if (event.getTarget() == null) { + entityzombie.setGoalTarget(null); + } else { + entityzombie.setGoalTarget(((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle()); + } + } + // CraftBukkit end + entityzombie.prepare((GroupDataEntity) null); + this.getAttributeInstance(bp).a(new AttributeModifier("Zombie reinforcement caller charge", -0.05000000074505806D, 0)); + entityzombie.getAttributeInstance(bp).a(new AttributeModifier("Zombie reinforcement callee charge", -0.05000000074505806D, 0)); + break; + } + } + } + } + + return true; + } + } + + public void h() { + if (!this.world.isStatic && this.cc()) { + int i = this.ce(); + + // CraftBukkit start - Use wall time instead of ticks for villager conversion + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.lastTick = MinecraftServer.currentTick; + i *= elapsedTicks; + // CraftBukkit end + + this.bt -= i; + if (this.bt <= 0) { + this.cd(); + } + } + + super.h(); + } + + public boolean n(Entity entity) { + boolean flag = super.n(entity); + + if (flag) { + int i = this.world.difficulty.a(); + + if (this.be() == null && this.isBurning() && this.random.nextFloat() < (float) i * 0.3F) { + // CraftBukkit start + EntityCombustByEntityEvent event = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 2 * i); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + entity.setOnFire(event.getDuration()); + } + // CraftBukkit end + } + } + + return flag; + } + + protected String t() { + return "mob.zombie.say"; + } + + protected String aT() { + return "mob.zombie.hurt"; + } + + protected String aU() { + return "mob.zombie.death"; + } + + protected void a(int i, int j, int k, Block block) { + this.makeSound("mob.zombie.step", 0.15F, 1.0F); + } + + protected Item getLoot() { + return Items.ROTTEN_FLESH; + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + protected void getRareDrop(int i) { + switch (this.random.nextInt(3)) { + case 0: + this.a(Items.IRON_INGOT, 1); + break; + + case 1: + this.a(Items.CARROT, 1); + break; + + case 2: + this.a(Items.POTATO, 1); + } + } + + protected void bC() { + super.bC(); + if (this.random.nextFloat() < (this.world.difficulty == EnumDifficulty.HARD ? 0.05F : 0.01F)) { + int i = this.random.nextInt(3); + + if (i == 0) { + this.setEquipment(0, new ItemStack(Items.IRON_SWORD)); + } else { + this.setEquipment(0, new ItemStack(Items.IRON_SPADE)); + } + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.isBaby()) { + nbttagcompound.setBoolean("IsBaby", true); + } + + if (this.isVillager()) { + nbttagcompound.setBoolean("IsVillager", true); + } + + nbttagcompound.setInt("ConversionTime", this.cc() ? this.bt : -1); + nbttagcompound.setBoolean("CanBreakDoors", this.bZ()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.getBoolean("IsBaby")) { + this.setBaby(true); + } + + if (nbttagcompound.getBoolean("IsVillager")) { + this.setVillager(true); + } + + if (nbttagcompound.hasKeyOfType("ConversionTime", 99) && nbttagcompound.getInt("ConversionTime") > -1) { + this.a(nbttagcompound.getInt("ConversionTime")); + } + + this.a(nbttagcompound.getBoolean("CanBreakDoors")); + } + + public void a(EntityLiving entityliving) { + super.a(entityliving); + if ((this.world.difficulty == EnumDifficulty.NORMAL || this.world.difficulty == EnumDifficulty.HARD) && entityliving instanceof EntityVillager) { + if (this.world.difficulty != EnumDifficulty.HARD && this.random.nextBoolean()) { + return; + } + + EntityZombie entityzombie = new EntityZombie(this.world); + + entityzombie.k(entityliving); + this.world.kill(entityliving); + entityzombie.prepare((GroupDataEntity) null); + entityzombie.setVillager(true); + if (entityliving.isBaby()) { + entityzombie.setBaby(true); + } + + this.world.addEntity(entityzombie, CreatureSpawnEvent.SpawnReason.INFECTION); // CraftBukkit - add SpawnReason + this.world.a((EntityHuman) null, 1016, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + } + } + + public GroupDataEntity prepare(GroupDataEntity groupdataentity) { + Object object = super.prepare(groupdataentity); + float f = this.world.b(this.locX, this.locY, this.locZ); + + this.h(this.random.nextFloat() < 0.55F * f); + if (object == null) { + object = new GroupDataZombie(this, this.world.random.nextFloat() < 0.05F, this.world.random.nextFloat() < 0.05F, (EmptyClassZombie) null); + } + + if (object instanceof GroupDataZombie) { + GroupDataZombie groupdatazombie = (GroupDataZombie) object; + + if (groupdatazombie.b) { + this.setVillager(true); + } + + if (groupdatazombie.a) { + this.setBaby(true); + if ((double) this.world.random.nextFloat() < 0.05D) { + List list = this.world.a(EntityChicken.class, this.boundingBox.grow(5.0D, 3.0D, 5.0D), IEntitySelector.b); + + if (!list.isEmpty()) { + EntityChicken entitychicken = (EntityChicken) list.get(0); + + entitychicken.i(true); + this.mount(entitychicken); + } + } else if ((double) this.world.random.nextFloat() < 0.05D) { + EntityChicken entitychicken1 = new EntityChicken(this.world); + + entitychicken1.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entitychicken1.prepare((GroupDataEntity) null); + entitychicken1.i(true); + this.world.addEntity(entitychicken1, CreatureSpawnEvent.SpawnReason.MOUNT); + this.mount(entitychicken1); + } + } + } + + this.a(this.random.nextFloat() < f * 0.1F); + this.bC(); + this.bD(); + if (this.getEquipment(4) == null) { + Calendar calendar = this.world.V(); + + if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.random.nextFloat() < 0.25F) { + this.setEquipment(4, new ItemStack(this.random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.PUMPKIN)); + this.dropChances[4] = 0.0F; + } + } + + this.getAttributeInstance(GenericAttributes.c).a(new AttributeModifier("Random spawn bonus", this.random.nextDouble() * 0.05000000074505806D, 0)); + double d0 = this.random.nextDouble() * 1.5D * (double) this.world.b(this.locX, this.locY, this.locZ); + + if (d0 > 1.0D) { + this.getAttributeInstance(GenericAttributes.b).a(new AttributeModifier("Random zombie-spawn bonus", d0, 2)); + } + + if (this.random.nextFloat() < f * 0.05F) { + this.getAttributeInstance(bp).a(new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 0.25D + 0.5D, 0)); + this.getAttributeInstance(GenericAttributes.maxHealth).a(new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 3.0D + 1.0D, 2)); + this.a(true); + } + + return (GroupDataEntity) object; + } + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.bF(); + + if (itemstack != null && itemstack.getItem() == Items.GOLDEN_APPLE && itemstack.getData() == 0 && this.isVillager() && this.hasEffect(MobEffectList.WEAKNESS)) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + if (itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + + if (!this.world.isStatic) { + this.a(this.random.nextInt(2401) + 3600); + } + + return true; + } else { + return false; + } + } + + protected void a(int i) { + this.bt = i; + this.getDataWatcher().watch(14, Byte.valueOf((byte) 1)); + this.removeEffect(MobEffectList.WEAKNESS.id); + this.addEffect(new MobEffect(MobEffectList.INCREASE_DAMAGE.id, i, Math.min(this.world.difficulty.a() - 1, 0))); + this.world.broadcastEntityEffect(this, (byte) 16); + } + + protected boolean isTypeNotPersistent() { + return !this.cc(); + } + + public boolean cc() { + return this.getDataWatcher().getByte(14) == 1; + } + + protected void cd() { + EntityVillager entityvillager = new EntityVillager(this.world); + + entityvillager.k(this); + entityvillager.prepare((GroupDataEntity) null); + entityvillager.cd(); + if (this.isBaby()) { + entityvillager.setAge(-24000); + } + + this.world.kill(this); + this.world.addEntity(entityvillager, CreatureSpawnEvent.SpawnReason.CURED); // CraftBukkit - add SpawnReason + entityvillager.addEffect(new MobEffect(MobEffectList.CONFUSION.id, 200, 0)); + this.world.a((EntityHuman) null, 1017, (int) this.locX, (int) this.locY, (int) this.locZ, 0); + } + + protected int ce() { + int i = 1; + + if (this.random.nextFloat() < 0.01F) { + int j = 0; + + for (int k = (int) this.locX - 4; k < (int) this.locX + 4 && j < 14; ++k) { + for (int l = (int) this.locY - 4; l < (int) this.locY + 4 && j < 14; ++l) { + for (int i1 = (int) this.locZ - 4; i1 < (int) this.locZ + 4 && j < 14; ++i1) { + Block block = this.world.getType(k, l, i1); + + if (block == Blocks.IRON_FENCE || block == Blocks.BED) { + if (this.random.nextFloat() < 0.3F) { + ++i; + } + + ++j; + } + } + } + } + } + + return i; + } + + public void k(boolean flag) { + this.a(flag ? 0.5F : 1.0F); + } + + protected final void a(float f, float f1) { + boolean flag = this.bv > 0.0F && this.bw > 0.0F; + + this.bv = f; + this.bw = f1; + if (!flag) { + this.a(1.0F); + } + } + + protected final void a(float f) { + super.a(this.bv * f, this.bw * f); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ExpirableListEntry.java b/vspigot-server/src/main/java/net/minecraft/server/ExpirableListEntry.java new file mode 100644 index 0000000..2cf6e23 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ExpirableListEntry.java @@ -0,0 +1,95 @@ +package net.minecraft.server; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import net.minecraft.util.com.google.gson.JsonObject; + +public abstract class ExpirableListEntry extends JsonListEntry { + + public static final SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + protected final Date b; + protected final String c; + protected final Date d; + protected final String e; + + public ExpirableListEntry(Object object, Date date, String s, Date date1, String s1) { + super(object); + this.b = date == null ? new Date() : date; + this.c = s == null ? "(Unknown)" : s; + this.d = date1; + this.e = s1 == null ? "Banned by an operator." : s1; + } + + protected ExpirableListEntry(Object object, JsonObject jsonobject) { + super(checkExpiry(object, jsonobject), jsonobject); // CraftBukkit - check expiry + + Date date; + + try { + date = jsonobject.has("created") ? a.parse(jsonobject.get("created").getAsString()) : new Date(); + } catch (ParseException parseexception) { + date = new Date(); + } + + this.b = date; + this.c = jsonobject.has("source") ? jsonobject.get("source").getAsString() : "(Unknown)"; + + Date date1; + + try { + date1 = jsonobject.has("expires") ? a.parse(jsonobject.get("expires").getAsString()) : null; + } catch (ParseException parseexception1) { + date1 = null; + } + + this.d = date1; + this.e = jsonobject.has("reason") ? jsonobject.get("reason").getAsString() : "Banned by an operator."; + } + + public Date getExpires() { + return this.d; + } + + public String getReason() { + return this.e; + } + + boolean hasExpired() { + return this.d == null ? false : this.d.before(new Date()); + } + + protected void a(JsonObject jsonobject) { + jsonobject.addProperty("created", a.format(this.b)); + jsonobject.addProperty("source", this.c); + jsonobject.addProperty("expires", this.d == null ? "forever" : a.format(this.d)); + jsonobject.addProperty("reason", this.e); + } + + // CraftBukkit start + public String getSource() { + return this.c; + } + + public Date getCreated() { + return this.b; + } + + private static Object checkExpiry(Object object, JsonObject jsonobject) { + Date expires = null; + + try { + expires = jsonobject.has("expires") ? a.parse(jsonobject.get("expires").getAsString()) : null; + } catch (ParseException ex) { + // Guess we don't have a date + } + + if (expires == null || expires.after(new Date())) { + return object; + } else { + return null; + } + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Explosion.java b/vspigot-server/src/main/java/net/minecraft/server/Explosion.java new file mode 100644 index 0000000..4e5c7a5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Explosion.java @@ -0,0 +1,359 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.Location; +// CraftBukkit end + +public class Explosion { + + public boolean a; + public boolean b = true; + private int i = 16; + private Random j = new Random(); + private World world; + public double posX; + public double posY; + public double posZ; + public Entity source; + public float size; + public List blocks = new ArrayList(); + private Map l = new HashMap(); + public boolean wasCanceled = false; // CraftBukkit - add field + + public Explosion(World world, Entity entity, double d0, double d1, double d2, float f) { + this.world = world; + this.source = entity; + this.size = (float) Math.max(f, 0.0); // CraftBukkit - clamp bad values + this.posX = d0; + this.posY = d1; + this.posZ = d2; + } + + public void a() { + // CraftBukkit start + if (this.size < 0.1F) { + return; + } + // CraftBukkit end + + float f = this.size; + HashSet hashset = new HashSet(); + + int i; + int j; + int k; + double d0; + double d1; + double d2; + + for (i = 0; i < this.i; ++i) { + for (j = 0; j < this.i; ++j) { + for (k = 0; k < this.i; ++k) { + if (i == 0 || i == this.i - 1 || j == 0 || j == this.i - 1 || k == 0 || k == this.i - 1) { + double d3 = (double) ((float) i / ((float) this.i - 1.0F) * 2.0F - 1.0F); + double d4 = (double) ((float) j / ((float) this.i - 1.0F) * 2.0F - 1.0F); + double d5 = (double) ((float) k / ((float) this.i - 1.0F) * 2.0F - 1.0F); + double d6 = Math.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + d3 /= d6; + d4 /= d6; + d5 /= d6; + float f1 = this.size * (0.7F + this.world.random.nextFloat() * 0.6F); + + d0 = this.posX; + d1 = this.posY; + d2 = this.posZ; + + for (float f2 = 0.3F; f1 > 0.0F; f1 -= f2 * 0.75F) { + int l = MathHelper.floor(d0); + int i1 = MathHelper.floor(d1); + int j1 = MathHelper.floor(d2); + Block block = this.world.getType(l, i1, j1); + + if (block.getMaterial() != Material.AIR) { + float f3 = this.source != null ? this.source.a(this, this.world, l, i1, j1, block) : block.a(this.source); + + f1 -= (f3 + 0.3F) * f2; + } + + if (f1 > 0.0F && (this.source == null || this.source.a(this, this.world, l, i1, j1, block, f1)) && i1 < 256 && i1 >= 0) { // CraftBukkit - don't wrap explosions + hashset.add(new ChunkPosition(l, i1, j1)); + } + + d0 += d3 * (double) f2; + d1 += d4 * (double) f2; + d2 += d5 * (double) f2; + } + } + } + } + } + + this.blocks.addAll(hashset); + this.size *= 2.0F; + i = MathHelper.floor(this.posX - (double) this.size - 1.0D); + j = MathHelper.floor(this.posX + (double) this.size + 1.0D); + k = MathHelper.floor(this.posY - (double) this.size - 1.0D); + int k1 = MathHelper.floor(this.posY + (double) this.size + 1.0D); + int l1 = MathHelper.floor(this.posZ - (double) this.size - 1.0D); + int i2 = MathHelper.floor(this.posZ + (double) this.size + 1.0D); + // PaperSpigot start - Fix lag from explosions processing dead entities + List list = this.world.getEntities(this.source, AxisAlignedBB.a((double) i, (double) k, (double) l1, (double) j, (double) k1, (double) i2), new IEntitySelector() { + @Override + public boolean a(Entity entity) { + return !entity.dead; + } + }); + // PaperSpigot end + Vec3D vec3d = Vec3D.a(this.posX, this.posY, this.posZ); + + for (int j2 = 0; j2 < list.size(); ++j2) { + Entity entity = (Entity) list.get(j2); + double d7 = entity.f(this.posX, this.posY, this.posZ) / (double) this.size; + + if (d7 <= 1.0D) { + d0 = entity.locX - this.posX; + d1 = entity.locY + (double) entity.getHeadHeight() - this.posY; + d2 = entity.locZ - this.posZ; + double d8 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + if (d8 != 0.0D) { + d0 /= d8; + d1 /= d8; + d2 /= d8; + double d9 = this.getBlockDensity(vec3d, entity.boundingBox); // PaperSpigot - Optimize explosions + double d10 = (1.0D - d7) * d9; + + // CraftBukkit start + CraftEventFactory.entityDamage = source; + if (!entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d10 * d10 + d10) / 2.0D * 8.0D * (double) this.size + 1.0D)))) { + + } + CraftEventFactory.entityDamage = null; + // CraftBukkit end + double d11 = EnchantmentProtection.a(entity, d10); + + entity.motX += d0 * d11; + entity.motY += d1 * d11; + entity.motZ += d2 * d11; + if (entity instanceof EntityHuman) { + this.l.put((EntityHuman) entity, Vec3D.a(d0 * d10, d1 * d10, d2 * d10)); + } + } + } + } + + this.size = f; + } + + public void a(boolean flag) { + this.world.makeSound(this.posX, this.posY, this.posZ, "random.explode", 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F); + if (this.size >= 2.0F && this.b) { + this.world.addParticle("hugeexplosion", this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D); + } else { + this.world.addParticle("largeexplode", this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D); + } + + Iterator iterator; + ChunkPosition chunkposition; + int i; + int j; + int k; + Block block; + + if (this.b) { + // CraftBukkit start + org.bukkit.World bworld = this.world.getWorld(); + org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity(); + Location location = new Location(bworld, this.posX, this.posY, this.posZ); + + List blockList = new ArrayList(); + for (int i1 = this.blocks.size() - 1; i1 >= 0; i1--) { + ChunkPosition cpos = (ChunkPosition) this.blocks.get(i1); + org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.x, cpos.y, cpos.z); + if (bblock.getType() != org.bukkit.Material.AIR) { + blockList.add(bblock); + } + } + + EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 0.3F); + this.world.getServer().getPluginManager().callEvent(event); + + this.blocks.clear(); + + for (org.bukkit.block.Block bblock : event.blockList()) { + ChunkPosition coords = new ChunkPosition(bblock.getX(), bblock.getY(), bblock.getZ()); + blocks.add(coords); + } + + if (event.isCancelled()) { + this.wasCanceled = true; + return; + } + // CraftBukkit end + + iterator = this.blocks.iterator(); + + while (iterator.hasNext()) { + chunkposition = (ChunkPosition) iterator.next(); + i = chunkposition.x; + j = chunkposition.y; + k = chunkposition.z; + block = this.world.getType(i, j, k); + world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, i, j, k); // Spigot + if (flag) { + double d0 = (double) ((float) i + this.world.random.nextFloat()); + double d1 = (double) ((float) j + this.world.random.nextFloat()); + double d2 = (double) ((float) k + this.world.random.nextFloat()); + double d3 = d0 - this.posX; + double d4 = d1 - this.posY; + double d5 = d2 - this.posZ; + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + d3 /= d6; + d4 /= d6; + d5 /= d6; + double d7 = 0.5D / (d6 / (double) this.size + 0.1D); + + d7 *= (double) (this.world.random.nextFloat() * this.world.random.nextFloat() + 0.3F); + d3 *= d7; + d4 *= d7; + d5 *= d7; + this.world.addParticle("explode", (d0 + this.posX * 1.0D) / 2.0D, (d1 + this.posY * 1.0D) / 2.0D, (d2 + this.posZ * 1.0D) / 2.0D, d3, d4, d5); + this.world.addParticle("smoke", d0, d1, d2, d3, d4, d5); + } + + if (block.getMaterial() != Material.AIR) { + if (block.a(this)) { + // CraftBukkit - add yield + block.dropNaturally(this.world, i, j, k, this.world.getData(i, j, k), event.getYield(), 0); + } + + this.world.setTypeAndData(i, j, k, Blocks.AIR, 0, 3); + block.wasExploded(this.world, i, j, k, this); + } + } + } + + if (this.a) { + iterator = this.blocks.iterator(); + + while (iterator.hasNext()) { + chunkposition = (ChunkPosition) iterator.next(); + i = chunkposition.x; + j = chunkposition.y; + k = chunkposition.z; + block = this.world.getType(i, j, k); + Block block1 = this.world.getType(i, j - 1, k); + + if (block.getMaterial() == Material.AIR && block1.j() && this.j.nextInt(3) == 0) { + // CraftBukkit start - Ignition by explosion + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.world, i, j, k, this).isCancelled()) { + this.world.setTypeUpdate(i, j, k, Blocks.FIRE); + } + // CraftBukkit end + } + } + } + } + + public Map b() { + return this.l; + } + + public EntityLiving c() { + return this.source == null ? null : (this.source instanceof EntityTNTPrimed ? ((EntityTNTPrimed) this.source).getSource() : (this.source instanceof EntityLiving ? (EntityLiving) this.source : null)); + } + + // PaperSpigot start - Optimize explosions + private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) { + if (!this.world.paperSpigotConfig.optimizeExplosions) { + return this.world.a(vec3d, aabb); + } + + CacheKey key = new CacheKey(this, aabb); + Float blockDensity = this.world.explosionDensityCache.get(key); + if (blockDensity == null) { + blockDensity = this.world.a(vec3d, aabb); + this.world.explosionDensityCache.put(key, blockDensity); + } + + return blockDensity; + } + + static class CacheKey { + private final World world; + private final double posX, posY, posZ; + private final double minX, minY, minZ; + private final double maxX, maxY, maxZ; + + public CacheKey(Explosion explosion, AxisAlignedBB aabb) { + this.world = explosion.world; + this.posX = explosion.posX; + this.posY = explosion.posY; + this.posZ = explosion.posZ; + this.minX = aabb.a; + this.minY = aabb.b; + this.minZ = aabb.c; + this.maxX = aabb.d; + this.maxY = aabb.e; + this.maxZ = aabb.f; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CacheKey cacheKey = (CacheKey) o; + + if (Double.compare(cacheKey.posX, posX) != 0) return false; + if (Double.compare(cacheKey.posY, posY) != 0) return false; + if (Double.compare(cacheKey.posZ, posZ) != 0) return false; + if (Double.compare(cacheKey.minX, minX) != 0) return false; + if (Double.compare(cacheKey.minY, minY) != 0) return false; + if (Double.compare(cacheKey.minZ, minZ) != 0) return false; + if (Double.compare(cacheKey.maxX, maxX) != 0) return false; + if (Double.compare(cacheKey.maxY, maxY) != 0) return false; + if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false; + return world.equals(cacheKey.world); + } + + @Override + public int hashCode() { + int result; + long temp; + result = world.hashCode(); + temp = Double.doubleToLongBits(posX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(posY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(posZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + } + // PaperSpigot end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/FileConversionException.java b/vspigot-server/src/main/java/net/minecraft/server/FileConversionException.java new file mode 100644 index 0000000..4b189bf --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/FileConversionException.java @@ -0,0 +1,22 @@ +package net.minecraft.server; + +// CraftBukkit - Imported because it's package private + +class FileConversionException extends RuntimeException { + + private FileConversionException(String s, Throwable throwable) { + super(s, throwable); + } + + private FileConversionException(String s) { + super(s); + } + + FileConversionException(String s, PredicateEmptyList predicateemptylist) { + this(s); + } + + FileConversionException(String s, Throwable throwable, PredicateEmptyList predicateemptylist) { + this(s, throwable); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/FileIOThread.java b/vspigot-server/src/main/java/net/minecraft/server/FileIOThread.java new file mode 100644 index 0000000..a095e10 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/FileIOThread.java @@ -0,0 +1,83 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class FileIOThread implements Runnable { + + public static final FileIOThread a = new FileIOThread(); + private List b = Collections.synchronizedList(new ArrayList()); + private volatile long c; + private volatile long d; + private volatile boolean e; + + private FileIOThread() { + Thread thread = new Thread(this, "File IO Thread"); + + thread.setPriority(1); + thread.start(); + } + + public void run() { + while (true) { + this.b(); + } + } + + private void b() { + for (int i = 0; i < this.b.size(); ++i) { + IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.b.get(i); + boolean flag = iasyncchunksaver.c(); + + if (!flag) { + this.b.remove(i--); + ++this.d; + } + + // MineHQ - don't sleep + /* + try { + Thread.sleep(this.e ? 0L : 10L); + } catch (InterruptedException interruptedexception) { + interruptedexception.printStackTrace(); + } + */ + } + + if (this.b.isEmpty()) { + try { + Thread.sleep(25L); + } catch (InterruptedException interruptedexception1) { + interruptedexception1.printStackTrace(); + } + } + } + + public void a(IAsyncChunkSaver iasyncchunksaver) { + if (!this.b.contains(iasyncchunksaver)) { + ++this.c; + this.b.add(iasyncchunksaver); + } + } + + public void a() throws InterruptedException { + this.e = true; + + while (this.c != this.d) { + Thread.sleep(10L); + } + + this.e = false; + } + + // Poweruser start + public boolean isDone() { + return this.c == this.d; + } + + public void setNoDelay(boolean active) { + this.e = active; + } + // Poweruser end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/FoodMetaData.java b/vspigot-server/src/main/java/net/minecraft/server/FoodMetaData.java new file mode 100644 index 0000000..4a7f1e5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/FoodMetaData.java @@ -0,0 +1,116 @@ +package net.minecraft.server; + +public class FoodMetaData { + + // CraftBukkit start - All made public + public int foodLevel = 20; + public float saturationLevel = 5.0F; + public float exhaustionLevel; + public int foodTickTimer; + private EntityHuman entityhuman; + // CraftBukkit end + private int e = 20; + + public FoodMetaData() { throw new AssertionError("Whoopsie, we missed the bukkit."); } // CraftBukkit start - throw an error + + // CraftBukkit start - added EntityHuman constructor + public FoodMetaData(EntityHuman entityhuman) { + org.apache.commons.lang.Validate.notNull(entityhuman); + this.entityhuman = entityhuman; + } + // CraftBukkit end + + public void eat(int i, float f) { + this.foodLevel = Math.min(i + this.foodLevel, 20); + this.saturationLevel = Math.min(this.saturationLevel + (float) i * f * 2.0F, (float) this.foodLevel); + } + + public void a(ItemFood itemfood, ItemStack itemstack) { + // CraftBukkit start + int oldFoodLevel = foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, itemfood.getNutrition(itemstack) + oldFoodLevel); + + if (!event.isCancelled()) { + this.eat(event.getFoodLevel() - oldFoodLevel, itemfood.getSaturationModifier(itemstack)); + } + + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel)); + // CraftBukkit end + } + + public void a(EntityHuman entityhuman) { + EnumDifficulty enumdifficulty = entityhuman.world.difficulty; + + this.e = this.foodLevel; + if (this.exhaustionLevel > 4.0F) { + this.exhaustionLevel -= 4.0F; + if (this.saturationLevel > 0.0F) { + this.saturationLevel = Math.max(this.saturationLevel - 1.0F, 0.0F); + } else if (enumdifficulty != EnumDifficulty.PEACEFUL) { + // CraftBukkit start + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, Math.max(this.foodLevel - 1, 0)); + + if (!event.isCancelled()) { + this.foodLevel = event.getFoodLevel(); + } + + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), this.foodLevel, this.saturationLevel)); + // CraftBukkit end + } + } + + if (entityhuman.world.getGameRules().getBoolean("naturalRegeneration") && this.foodLevel >= 18 && entityhuman.bR()) { + ++this.foodTickTimer; + if (this.foodTickTimer >= 80) { + // CraftBukkit - added RegainReason + entityhuman.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED); + this.a(entityhuman.world.spigotConfig.regenExhaustion); // Spigot - Change to use configurable value + this.foodTickTimer = 0; + } + } else if (this.foodLevel <= 0) { + ++this.foodTickTimer; + if (this.foodTickTimer >= 80) { + if (entityhuman.getHealth() > 10.0F || enumdifficulty == EnumDifficulty.HARD || entityhuman.getHealth() > 1.0F && enumdifficulty == EnumDifficulty.NORMAL) { + entityhuman.damageEntity(DamageSource.STARVE, 1.0F); + } + + this.foodTickTimer = 0; + } + } else { + this.foodTickTimer = 0; + } + } + + public void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("foodLevel", 99)) { + this.foodLevel = nbttagcompound.getInt("foodLevel"); + this.foodTickTimer = nbttagcompound.getInt("foodTickTimer"); + this.saturationLevel = nbttagcompound.getFloat("foodSaturationLevel"); + this.exhaustionLevel = nbttagcompound.getFloat("foodExhaustionLevel"); + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("foodLevel", this.foodLevel); + nbttagcompound.setInt("foodTickTimer", this.foodTickTimer); + nbttagcompound.setFloat("foodSaturationLevel", this.saturationLevel); + nbttagcompound.setFloat("foodExhaustionLevel", this.exhaustionLevel); + } + + public int getFoodLevel() { + return this.foodLevel; + } + + public boolean c() { + return this.foodLevel < 20; + } + + public void a(float f) { + this.exhaustionLevel = Math.min(this.exhaustionLevel + f, 40.0F); + } + + public float getSaturationLevel() { + return this.saturationLevel; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/GameProfileBanEntry.java b/vspigot-server/src/main/java/net/minecraft/server/GameProfileBanEntry.java new file mode 100644 index 0000000..7a7bf83 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/GameProfileBanEntry.java @@ -0,0 +1,57 @@ +package net.minecraft.server; + +import java.util.Date; +import java.util.UUID; + +import net.minecraft.util.com.google.gson.JsonObject; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +public class GameProfileBanEntry extends ExpirableListEntry { + + public GameProfileBanEntry(GameProfile gameprofile) { + this(gameprofile, (Date) null, (String) null, (Date) null, (String) null); + } + + public GameProfileBanEntry(GameProfile gameprofile, Date date, String s, Date date1, String s1) { + super(gameprofile, date, s, date1, s1); // Spigot + } + + public GameProfileBanEntry(JsonObject jsonobject) { + super(b(jsonobject), jsonobject); + } + + protected void a(JsonObject jsonobject) { + if (this.getKey() != null) { + jsonobject.addProperty("uuid", ((GameProfile) this.getKey()).getId() == null ? "" : ((GameProfile) this.getKey()).getId().toString()); + jsonobject.addProperty("name", ((GameProfile) this.getKey()).getName()); + super.a(jsonobject); + } + } + + private static GameProfile b(JsonObject jsonobject) { + // Spigot start + // this whole method has to be reworked to account for the fact Bukkit only accepts UUID bans and gives no way for usernames to be stored! + UUID uuid = null; + String name = null; + if (jsonobject.has("uuid")) { + String s = jsonobject.get("uuid").getAsString(); + + try { + uuid = UUID.fromString(s); + } catch (Throwable throwable) { + } + + } + if ( jsonobject.has("name")) + { + name = jsonobject.get("name").getAsString(); + } + if ( uuid != null || name != null ) + { + return new GameProfile( uuid, name ); + } else { + return null; + } + // Spigot End + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/GenLayer.java b/vspigot-server/src/main/java/net/minecraft/server/GenLayer.java new file mode 100644 index 0000000..386464f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/GenLayer.java @@ -0,0 +1,177 @@ +package net.minecraft.server; + +import net.valorhcf.generator.GenLayerRemoveSpawnRivers; +import net.valorhcf.generator.GenLayerSpawnBiome; + +import java.util.concurrent.Callable; + +public abstract class GenLayer { + + private long c; + protected GenLayer a; + private long d; + protected long b; + + public static GenLayer[] a(long i, WorldType worldtype, World world) { // MineHQ - add world + boolean flag = false; + LayerIsland layerisland = new LayerIsland(1L, world); // MineHQ - add world + GenLayerZoomFuzzy genlayerzoomfuzzy = new GenLayerZoomFuzzy(2000L, layerisland); + GenLayerIsland genlayerisland = new GenLayerIsland(1L, genlayerzoomfuzzy); + GenLayerZoom genlayerzoom = new GenLayerZoom(2001L, genlayerisland); + + genlayerisland = new GenLayerIsland(2L, genlayerzoom); + genlayerisland = new GenLayerIsland(50L, genlayerisland); + genlayerisland = new GenLayerIsland(70L, genlayerisland); + GenLayerIcePlains genlayericeplains = new GenLayerIcePlains(2L, genlayerisland); + GenLayerTopSoil genlayertopsoil = new GenLayerTopSoil(2L, genlayericeplains); + + genlayerisland = new GenLayerIsland(3L, genlayertopsoil); + GenLayerSpecial genlayerspecial = new GenLayerSpecial(2L, genlayerisland, EnumGenLayerSpecial.COOL_WARM); + + genlayerspecial = new GenLayerSpecial(2L, genlayerspecial, EnumGenLayerSpecial.HEAT_ICE); + genlayerspecial = new GenLayerSpecial(3L, genlayerspecial, EnumGenLayerSpecial.PUFFERFISH); + genlayerzoom = new GenLayerZoom(2002L, genlayerspecial); + genlayerzoom = new GenLayerZoom(2003L, genlayerzoom); + genlayerisland = new GenLayerIsland(4L, genlayerzoom); + GenLayerMushroomIsland genlayermushroomisland = new GenLayerMushroomIsland(5L, genlayerisland); + GenLayerDeepOcean genlayerdeepocean = new GenLayerDeepOcean(4L, genlayermushroomisland); + GenLayer genlayer = GenLayerZoom.b(1000L, genlayerdeepocean, 0); + byte b0 = 4; + + if (worldtype == WorldType.LARGE_BIOMES) { + b0 = 6; + } + + if (flag) { + b0 = 4; + } + + GenLayer genlayer1 = GenLayerZoom.b(1000L, genlayer, 0); + GenLayerCleaner genlayercleaner = new GenLayerCleaner(100L, genlayer1); + Object object = new GenLayerBiome(200L, genlayer, worldtype, world); // MineHQ - add world + + if (!flag) { + GenLayer genlayer2 = GenLayerZoom.b(1000L, (GenLayer) object, 2); + + object = new GenLayerDesert(1000L, genlayer2); + } + + GenLayer genlayer3 = GenLayerZoom.b(1000L, genlayercleaner, 2); + GenLayerRegionHills genlayerregionhills = new GenLayerRegionHills(1000L, (GenLayer) object, genlayer3, world); // MineHQ - add world + + genlayer1 = GenLayerZoom.b(1000L, genlayercleaner, 2); + genlayer1 = GenLayerZoom.b(1000L, genlayer1, b0); + GenLayer genlayerriver = new GenLayerRiver(1L, genlayer1); + if (world.generatorConfig.spawnBiomeRadius > 0 && !world.generatorConfig.spawnBiomeRivers) { + genlayerriver = new GenLayerRemoveSpawnRivers(genlayerriver, world); + } + GenLayerSmooth genlayersmooth = new GenLayerSmooth(1000L, genlayerriver); + + object = new GenLayerPlains(1001L, genlayerregionhills); + if (world.generatorConfig.spawnBiomeRadius > 0) { + object = new GenLayerSpawnBiome((GenLayer) object, b0, world); + } + + for (int j = 0; j < b0; ++j) { + object = new GenLayerZoom((long) (1000 + j), (GenLayer) object); + if (j == 0) { + object = new GenLayerIsland(3L, (GenLayer) object); + } + + if (j == 1) { + object = new GenLayerMushroomShore(1000L, (GenLayer) object); + } + } + + GenLayerSmooth genlayersmooth1 = new GenLayerSmooth(1000L, (GenLayer) object); + GenLayerRiverMix genlayerrivermix = new GenLayerRiverMix(100L, genlayersmooth1, genlayersmooth); + GenLayerZoomVoronoi genlayerzoomvoronoi = new GenLayerZoomVoronoi(10L, genlayerrivermix); + + genlayerrivermix.a(i); + genlayerzoomvoronoi.a(i); + return new GenLayer[] { genlayerrivermix, genlayerzoomvoronoi, genlayerrivermix}; + } + + public GenLayer(long i) { + this.b = i; + this.b *= this.b * 6364136223846793005L + 1442695040888963407L; + this.b += i; + this.b *= this.b * 6364136223846793005L + 1442695040888963407L; + this.b += i; + this.b *= this.b * 6364136223846793005L + 1442695040888963407L; + this.b += i; + } + + public void a(long i) { + this.c = i; + if (this.a != null) { + this.a.a(i); + } + + this.c *= this.c * 6364136223846793005L + 1442695040888963407L; + this.c += this.b; + this.c *= this.c * 6364136223846793005L + 1442695040888963407L; + this.c += this.b; + this.c *= this.c * 6364136223846793005L + 1442695040888963407L; + this.c += this.b; + } + + public void a(long i, long j) { + this.d = this.c; + this.d *= this.d * 6364136223846793005L + 1442695040888963407L; + this.d += i; + this.d *= this.d * 6364136223846793005L + 1442695040888963407L; + this.d += j; + this.d *= this.d * 6364136223846793005L + 1442695040888963407L; + this.d += i; + this.d *= this.d * 6364136223846793005L + 1442695040888963407L; + this.d += j; + } + + protected int a(int i) { + int j = (int) ((this.d >> 24) % (long) i); + + if (j < 0) { + j += i; + } + + this.d *= this.d * 6364136223846793005L + 1442695040888963407L; + this.d += this.c; + return j; + } + + public abstract int[] a(int i, int j, int k, int l); + + protected static boolean a(int i, int j) { + if (i == j) { + return true; + } else if (i != BiomeBase.MESA_PLATEAU_F.id && i != BiomeBase.MESA_PLATEAU.id) { + try { + return BiomeBase.getBiome(i) != null && BiomeBase.getBiome(j) != null ? BiomeBase.getBiome(i).a(BiomeBase.getBiome(j)) : false; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Comparing biomes"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Biomes being compared"); + + crashreportsystemdetails.a("Biome A ID", Integer.valueOf(i)); + crashreportsystemdetails.a("Biome B ID", Integer.valueOf(j)); + crashreportsystemdetails.a("Biome A", (Callable) (new CrashReportGenLayer1(i))); + crashreportsystemdetails.a("Biome B", (Callable) (new CrashReportGenLayer2(j))); + throw new ReportedException(crashreport); + } + } else { + return j == BiomeBase.MESA_PLATEAU_F.id || j == BiomeBase.MESA_PLATEAU.id; + } + } + + protected static boolean b(int i) { + return i == BiomeBase.OCEAN.id || i == BiomeBase.DEEP_OCEAN.id || i == BiomeBase.FROZEN_OCEAN.id; + } + + protected int a(int... aint) { + return aint[this.a(aint.length)]; + } + + protected int b(int i, int j, int k, int l) { + return j == k && k == l ? j : (i == j && i == k ? i : (i == j && i == l ? i : (i == k && i == l ? i : (i == j && k != l ? i : (i == k && j != l ? i : (i == l && j != k ? i : (j == k && i != l ? j : (j == l && i != k ? j : (k == l && i != j ? k : this.a(new int[] { i, j, k, l})))))))))); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/GenLayerBiome.java b/vspigot-server/src/main/java/net/minecraft/server/GenLayerBiome.java new file mode 100644 index 0000000..6c078fa --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/GenLayerBiome.java @@ -0,0 +1,152 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; + +public class GenLayerBiome extends GenLayer { + + private final World world; + private BiomeBase[] c; + private BiomeBase[] d; + private BiomeBase[] e; + private BiomeBase[] f; + + public GenLayerBiome(long i, GenLayer genlayer, WorldType worldtype, World world) { + super(i); + this.world = world; + List list = new ArrayList(); + //this.c = new BiomeBase[] { BiomeBase.DESERT, BiomeBase.DESERT, BiomeBase.DESERT, BiomeBase.SAVANNA, BiomeBase.SAVANNA, BiomeBase.PLAINS}; + if (world.generatorConfig.biomeDesert) { + list.add(BiomeBase.DESERT); + list.add(BiomeBase.DESERT); + list.add(BiomeBase.DESERT); + } + if (world.generatorConfig.biomeSavanna) { + list.add(BiomeBase.SAVANNA); + list.add(BiomeBase.SAVANNA); + } + if (world.generatorConfig.biomePlains) { + list.add(BiomeBase.PLAINS); + } + this.c = list.toArray(new BiomeBase[list.size()]); + list.clear(); + //this.d = new BiomeBase[] { BiomeBase.FOREST, BiomeBase.ROOFED_FOREST, BiomeBase.EXTREME_HILLS, BiomeBase.PLAINS, BiomeBase.BIRCH_FOREST, BiomeBase.SWAMPLAND}; + if (world.generatorConfig.biomeForest) { + list.add(BiomeBase.FOREST); + } + if (world.generatorConfig.biomeRoofedForest) { + list.add(BiomeBase.ROOFED_FOREST); + } + if (world.generatorConfig.biomeExtremeHills) { + list.add(BiomeBase.EXTREME_HILLS); + } + if (world.generatorConfig.biomePlains) { + list.add(BiomeBase.PLAINS); + } + if (world.generatorConfig.biomeBirchForest) { + list.add(BiomeBase.BIRCH_FOREST); + } + if (world.generatorConfig.biomeSwampland) { + list.add(BiomeBase.SWAMPLAND); + } + if (list.isEmpty() && world.generatorConfig.biomeJungle) { + list.add(BiomeBase.JUNGLE); + } + this.d = list.toArray(new BiomeBase[list.size()]); + list.clear(); + //this.e = new BiomeBase[] { BiomeBase.FOREST, BiomeBase.EXTREME_HILLS, BiomeBase.TAIGA, BiomeBase.PLAINS}; + if (world.generatorConfig.biomeForest) { + list.add(BiomeBase.FOREST); + } + if (world.generatorConfig.biomeExtremeHills) { + list.add(BiomeBase.EXTREME_HILLS); + } + if (world.generatorConfig.biomeTaiga) { + list.add(BiomeBase.TAIGA); + } + if (world.generatorConfig.biomePlains) { + list.add(BiomeBase.PLAINS); + } + this.e = list.toArray(new BiomeBase[list.size()]); + list.clear(); + //this.f = new BiomeBase[] { BiomeBase.ICE_PLAINS, BiomeBase.ICE_PLAINS, BiomeBase.ICE_PLAINS, BiomeBase.COLD_TAIGA}; + if (world.generatorConfig.biomeIcePlains) { + list.add(BiomeBase.ICE_PLAINS); + list.add(BiomeBase.ICE_PLAINS); + list.add(BiomeBase.ICE_PLAINS); + } + if (world.generatorConfig.biomeColdTaiga) { + list.add(BiomeBase.COLD_TAIGA); + } + this.f = list.toArray(new BiomeBase[list.size()]); + this.a = genlayer; + if (worldtype == WorldType.NORMAL_1_1) { + this.c = new BiomeBase[] { BiomeBase.DESERT, BiomeBase.FOREST, BiomeBase.EXTREME_HILLS, BiomeBase.SWAMPLAND, BiomeBase.PLAINS, BiomeBase.TAIGA}; + } + } + + public int[] a(int i, int j, int k, int l) { + int[] aint = this.a.a(i, j, k, l); + int[] aint1 = IntCache.a(k * l); + + for (int i1 = 0; i1 < l; ++i1) { + for (int j1 = 0; j1 < k; ++j1) { + this.a((long) (j1 + i), (long) (i1 + j)); + int k1 = aint[j1 + i1 * k]; + int l1 = (k1 & 3840) >> 8; + + k1 &= -3841; + if (b(k1)) { + aint1[j1 + i1 * k] = k1; + } else if (k1 == BiomeBase.MUSHROOM_ISLAND.id) { + aint1[j1 + i1 * k] = k1; + } else if (k1 == 1) { + BiomeBase[] biomes = this.firstNonEmpty(this.c, this.d, this.e, this.f); + if (l1 > 0) { + if (this.a(3) == 0 && this.world.generatorConfig.biomeMesaPlateau) { + aint1[j1 + i1 * k] = BiomeBase.MESA_PLATEAU.id; + } else if (this.world.generatorConfig.biomeMesaPlateauF) { + aint1[j1 + i1 * k] = BiomeBase.MESA_PLATEAU_F.id; + } else if (this.world.generatorConfig.biomeMesa) { + aint1[j1 + i1 * k] = BiomeBase.MESA.id; + } else { + aint1[j1 + i1 * k] = biomes[this.a(biomes.length)].id; + } + } else { + aint1[j1 + i1 * k] = biomes[this.a(biomes.length)].id; + } + } else if (k1 == 2) { + BiomeBase[] biomes = this.firstNonEmpty(this.d, this.e, this.f, this.c); + if (l1 > 0 && this.world.generatorConfig.biomeJungle) { + aint1[j1 + i1 * k] = BiomeBase.JUNGLE.id; + } else { + aint1[j1 + i1 * k] = biomes[this.a(biomes.length)].id; + } + } else if (k1 == 3) { + BiomeBase[] biomes = this.firstNonEmpty(this.e, this.f, this.c, this.d); + if (l1 > 0 && this.world.generatorConfig.biomeMegaTaiga) { + aint1[j1 + i1 * k] = BiomeBase.MEGA_TAIGA.id; + } else { + aint1[j1 + i1 * k] = biomes[this.a(biomes.length)].id; + } + } else if (k1 == 4) { + BiomeBase[] biomes = this.firstNonEmpty(this.f, this.c, this.d, this.e); + aint1[j1 + i1 * k] = biomes[this.a(biomes.length)].id; + } else { + aint1[j1 + i1 * k] = BiomeBase.MUSHROOM_ISLAND.id; + } + } + } + + return aint1; + } + + private BiomeBase[] firstNonEmpty(BiomeBase[]... options) { + for (BiomeBase[] option : options) { + if (option.length > 0) { + return option; + } + } + return new BiomeBase[]{BiomeBase.PLAINS}; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/GenLayerRegionHills.java b/vspigot-server/src/main/java/net/minecraft/server/GenLayerRegionHills.java new file mode 100644 index 0000000..ae53a82 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/GenLayerRegionHills.java @@ -0,0 +1,133 @@ +package net.minecraft.server; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class GenLayerRegionHills extends GenLayer { + + private static final Logger c = LogManager.getLogger(); + private final World world; + private GenLayer d; + + public GenLayerRegionHills(long i, GenLayer genlayer, GenLayer genlayer1, World world) { + super(i); + this.world = world; + this.a = genlayer; + this.d = genlayer1; + } + + public int[] a(int i, int j, int k, int l) { + int[] aint = this.a.a(i - 1, j - 1, k + 2, l + 2); + int[] aint1 = this.d.a(i - 1, j - 1, k + 2, l + 2); + int[] aint2 = IntCache.a(k * l); + + for (int i1 = 0; i1 < l; ++i1) { + for (int j1 = 0; j1 < k; ++j1) { + this.a((long) (j1 + i), (long) (i1 + j)); + int k1 = aint[j1 + 1 + (i1 + 1) * (k + 2)]; + int l1 = aint1[j1 + 1 + (i1 + 1) * (k + 2)]; + boolean flag = (l1 - 2) % 29 == 0; + + if (k1 > 255) { + c.debug("old! " + k1); + } + + if (k1 != 0 && l1 >= 2 && (l1 - 2) % 29 == 1 && k1 < 128) { + if (BiomeBase.getBiome(k1 + 128) != null) { + aint2[j1 + i1 * k] = k1 + 128; + } else { + aint2[j1 + i1 * k] = k1; + } + } else if (this.a(3) != 0 && !flag) { + aint2[j1 + i1 * k] = k1; + } else { + int i2 = k1; + int j2; + + if (k1 == BiomeBase.DESERT.id && this.world.generatorConfig.biomeDesertHills) { + i2 = BiomeBase.DESERT_HILLS.id; + } else if (k1 == BiomeBase.FOREST.id && this.world.generatorConfig.biomeForestHills) { + i2 = BiomeBase.FOREST_HILLS.id; + } else if (k1 == BiomeBase.BIRCH_FOREST.id && this.world.generatorConfig.biomeBirchForestHills) { + i2 = BiomeBase.BIRCH_FOREST_HILLS.id; + } else if (k1 == BiomeBase.ROOFED_FOREST.id && this.world.generatorConfig.biomePlains) { + i2 = BiomeBase.PLAINS.id; + } else if (k1 == BiomeBase.TAIGA.id && this.world.generatorConfig.biomeTaigaHills) { + i2 = BiomeBase.TAIGA_HILLS.id; + } else if (k1 == BiomeBase.MEGA_TAIGA.id && this.world.generatorConfig.biomeMegaTaigaHills) { + i2 = BiomeBase.MEGA_TAIGA_HILLS.id; + } else if (k1 == BiomeBase.COLD_TAIGA.id && this.world.generatorConfig.biomeColdTaigaHills) { + i2 = BiomeBase.COLD_TAIGA_HILLS.id; + } else if (k1 == BiomeBase.PLAINS.id) { + if (this.a(3) == 0 && this.world.generatorConfig.biomeForestHills) { + i2 = BiomeBase.FOREST_HILLS.id; + } else if (this.world.generatorConfig.biomeForest) { + i2 = BiomeBase.FOREST.id; + } + } else if (k1 == BiomeBase.ICE_PLAINS.id && this.world.generatorConfig.biomeIceMountains) { + i2 = BiomeBase.ICE_MOUNTAINS.id; + } else if (k1 == BiomeBase.JUNGLE.id && this.world.generatorConfig.biomeJungleHills) { + i2 = BiomeBase.JUNGLE_HILLS.id; + } else if (k1 == BiomeBase.OCEAN.id) { + i2 = BiomeBase.DEEP_OCEAN.id; + } else if (k1 == BiomeBase.EXTREME_HILLS.id && this.world.generatorConfig.biomeExtremeHillsPlus) { + i2 = BiomeBase.EXTREME_HILLS_PLUS.id; + } else if (k1 == BiomeBase.SAVANNA.id && this.world.generatorConfig.biomeSavannaPlateau) { + i2 = BiomeBase.SAVANNA_PLATEAU.id; + } else if (a(k1, BiomeBase.MESA_PLATEAU_F.id) && this.world.generatorConfig.biomeMesa) { + i2 = BiomeBase.MESA.id; + } else if (k1 == BiomeBase.DEEP_OCEAN.id && this.a(3) == 0) { + j2 = this.a(2); + if (j2 == 0 && this.world.generatorConfig.biomePlains) { + i2 = BiomeBase.PLAINS.id; + } else if (this.world.generatorConfig.biomeForest) { + i2 = BiomeBase.FOREST.id; + } + } + + if (flag && i2 != k1) { + if (BiomeBase.getBiome(i2 + 128) != null) { + i2 += 128; + } else { + i2 = k1; + } + } + + if (i2 == k1) { + aint2[j1 + i1 * k] = k1; + } else { + j2 = aint[j1 + 1 + (i1 + 1 - 1) * (k + 2)]; + int k2 = aint[j1 + 1 + 1 + (i1 + 1) * (k + 2)]; + int l2 = aint[j1 + 1 - 1 + (i1 + 1) * (k + 2)]; + int i3 = aint[j1 + 1 + (i1 + 1 + 1) * (k + 2)]; + int j3 = 0; + + if (a(j2, k1)) { + ++j3; + } + + if (a(k2, k1)) { + ++j3; + } + + if (a(l2, k1)) { + ++j3; + } + + if (a(i3, k1)) { + ++j3; + } + + if (j3 >= 3) { + aint2[j1 + i1 * k] = i2; + } else { + aint2[j1 + i1 * k] = k1; + } + } + } + } + } + + return aint2; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/GenericAttributes.java b/vspigot-server/src/main/java/net/minecraft/server/GenericAttributes.java new file mode 100644 index 0000000..7ad88f5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/GenericAttributes.java @@ -0,0 +1,107 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.Iterator; +import java.util.UUID; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class GenericAttributes { + + private static final Logger f = LogManager.getLogger(); + // Spigot Start + public static final IAttribute maxHealth = (new AttributeRanged("generic.maxHealth", 20.0D, 0.1D, org.spigotmc.SpigotConfig.maxHealth)).a("Max Health").a(true); // Spigot + public static final IAttribute b = (new AttributeRanged("generic.followRange", 32.0D, 0.0D, 2048.0D)).a("Follow Range"); + public static final IAttribute c = (new AttributeRanged("generic.knockbackResistance", 0.0D, 0.0D, 1.0D)).a("Knockback Resistance"); + public static final IAttribute d = (new AttributeRanged("generic.movementSpeed", 0.699999988079071D, 0.0D, org.spigotmc.SpigotConfig.movementSpeed)).a("Movement Speed").a(true); + public static final IAttribute e = new AttributeRanged("generic.attackDamage", 2.0D, 0.0D, org.spigotmc.SpigotConfig.attackDamage); + // Spigot End + + public static NBTTagList a(AttributeMapBase attributemapbase) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = attributemapbase.a().iterator(); + + while (iterator.hasNext()) { + AttributeInstance attributeinstance = (AttributeInstance) iterator.next(); + + nbttaglist.add(a(attributeinstance)); + } + + return nbttaglist; + } + + private static NBTTagCompound a(AttributeInstance attributeinstance) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + IAttribute iattribute = attributeinstance.getAttribute(); + + nbttagcompound.setString("Name", iattribute.getName()); + nbttagcompound.setDouble("Base", attributeinstance.b()); + Collection collection = attributeinstance.c(); + + if (collection != null && !collection.isEmpty()) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + AttributeModifier attributemodifier = (AttributeModifier) iterator.next(); + + if (attributemodifier.e()) { + nbttaglist.add(a(attributemodifier)); + } + } + + nbttagcompound.set("Modifiers", nbttaglist); + } + + return nbttagcompound; + } + + private static NBTTagCompound a(AttributeModifier attributemodifier) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("Name", attributemodifier.b()); + nbttagcompound.setDouble("Amount", attributemodifier.d()); + nbttagcompound.setInt("Operation", attributemodifier.c()); + nbttagcompound.setLong("UUIDMost", attributemodifier.a().getMostSignificantBits()); + nbttagcompound.setLong("UUIDLeast", attributemodifier.a().getLeastSignificantBits()); + return nbttagcompound; + } + + public static void a(AttributeMapBase attributemapbase, NBTTagList nbttaglist) { + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.get(i); + AttributeInstance attributeinstance = attributemapbase.a(nbttagcompound.getString("Name")); + + if (attributeinstance != null) { + a(attributeinstance, nbttagcompound); + } else { + f.warn("Ignoring unknown attribute \'" + nbttagcompound.getString("Name") + "\'"); + } + } + } + + private static void a(AttributeInstance attributeinstance, NBTTagCompound nbttagcompound) { + attributeinstance.setValue(nbttagcompound.getDouble("Base")); + if (nbttagcompound.hasKeyOfType("Modifiers", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Modifiers", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + AttributeModifier attributemodifier = a(nbttaglist.get(i)); + AttributeModifier attributemodifier1 = attributeinstance.a(attributemodifier.a()); + + if (attributemodifier1 != null) { + attributeinstance.b(attributemodifier1); + } + + attributeinstance.a(attributemodifier); + } + } + } + + public static AttributeModifier a(NBTTagCompound nbttagcompound) { + UUID uuid = new UUID(nbttagcompound.getLong("UUIDMost"), nbttagcompound.getLong("UUIDLeast")); + + return new AttributeModifier(uuid, nbttagcompound.getString("Name"), nbttagcompound.getDouble("Amount"), nbttagcompound.getInt("Operation")); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/GroupDataZombie.java b/vspigot-server/src/main/java/net/minecraft/server/GroupDataZombie.java new file mode 100644 index 0000000..340af90 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/GroupDataZombie.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +// CraftBukkit - package-private import +class GroupDataZombie implements GroupDataEntity { + + public boolean a; + public boolean b; + final EntityZombie c; + + private GroupDataZombie(EntityZombie entityzombie, boolean flag, boolean flag1) { + this.c = entityzombie; + this.a = false; + this.b = false; + this.a = flag; + this.b = flag1; + } + + GroupDataZombie(EntityZombie entityzombie, boolean flag, boolean flag1, EmptyClassZombie emptyclasszombie) { + this(entityzombie, flag, flag1); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/HandshakeListener.java b/vspigot-server/src/main/java/net/minecraft/server/HandshakeListener.java new file mode 100644 index 0000000..df93c9c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/HandshakeListener.java @@ -0,0 +1,128 @@ +package net.minecraft.server; + +import net.minecraft.util.com.mojang.authlib.properties.Property; // Spigot +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; + +// CraftBukkit start +import java.net.InetAddress; +import java.util.HashMap; +import net.minecraft.util.com.mojang.util.UUIDTypeAdapter; +// CraftBukkit end + +public class HandshakeListener implements PacketHandshakingInListener { + + private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot + // CraftBukkit start - add fields + private static final HashMap throttleTracker = new HashMap(); + private static int throttleCounter = 0; + // CraftBukkit end + + private final MinecraftServer a; + private final NetworkManager b; + + public HandshakeListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { + this.a = minecraftserver; + this.b = networkmanager; + } + + public void a(PacketHandshakingInSetProtocol packethandshakinginsetprotocol) { + // Spigot start + if ( NetworkManager.SUPPORTED_VERSIONS.contains( packethandshakinginsetprotocol.d() ) ) + { + NetworkManager.a( this.b ).attr( NetworkManager.protocolVersion ).set( packethandshakinginsetprotocol.d() ); + } + // Spigot end + switch (ProtocolOrdinalWrapper.a[packethandshakinginsetprotocol.c().ordinal()]) { + case 1: + this.b.a(EnumProtocol.LOGIN); + ChatComponentText chatcomponenttext; + + // CraftBukkit start - Connection throttle + try { + long currentTime = System.currentTimeMillis(); + long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle(); + InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress(); + + synchronized (throttleTracker) { + if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) { + throttleTracker.put(address, currentTime); + chatcomponenttext = new ChatComponentText("Connection throttled! Please wait before reconnecting."); + this.b.handle(new PacketLoginOutDisconnect(chatcomponenttext), NetworkManager.emptyListenerArray); // Poweruser + this.b.close(chatcomponenttext); + return; + } + + throttleTracker.put(address, currentTime); + throttleCounter++; + if (throttleCounter > 200) { + throttleCounter = 0; + + // Cleanup stale entries + java.util.Iterator iter = throttleTracker.entrySet().iterator(); + while (iter.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iter.next(); + if (entry.getValue() > connectionThrottle) { + iter.remove(); + } + } + } + } + } catch (Throwable t) { + org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t); + } + // CraftBukkit end + + if (packethandshakinginsetprotocol.d() > 5 && packethandshakinginsetprotocol.d() != 47) { // Spigot + chatcomponenttext = new ChatComponentText( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage, "1.7.10" ) ); // Spigot + this.b.handle(new PacketLoginOutDisconnect(chatcomponenttext), NetworkManager.emptyListenerArray); // Poweruser + this.b.close(chatcomponenttext); + } else if (packethandshakinginsetprotocol.d() < 4) { + chatcomponenttext = new ChatComponentText( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage, "1.7.10" ) ); // Spigot + this.b.handle(new PacketLoginOutDisconnect(chatcomponenttext), NetworkManager.emptyListenerArray); // Poweruser + this.b.close(chatcomponenttext); + } else { + this.b.a((PacketListener) (new LoginListener(this.a, this.b))); + // Spigot Start + if (org.spigotmc.SpigotConfig.bungee) { + String[] split = packethandshakinginsetprotocol.b.split("\00"); + if ( split.length == 3 || split.length == 4 ) { + packethandshakinginsetprotocol.b = split[0]; + b.n = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) b.getSocketAddress()).getPort()); + b.spoofedUUID = UUIDTypeAdapter.fromString( split[2] ); + } else + { + chatcomponenttext = new ChatComponentText("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!"); + this.b.handle(new PacketLoginOutDisconnect(chatcomponenttext), NetworkManager.emptyListenerArray); // Poweruser + this.b.close(chatcomponenttext); + return; + } + if ( split.length == 4 ) + { + b.spoofedProfile = gson.fromJson(split[3], Property[].class); + } + } + // Spigot End + ((LoginListener) this.b.getPacketListener()).hostname = packethandshakinginsetprotocol.b + ":" + packethandshakinginsetprotocol.c; // CraftBukkit - set hostname + } + break; + + case 2: + this.b.a(EnumProtocol.STATUS); + this.b.a((PacketListener) (new PacketStatusListener(this.a, this.b))); + break; + + default: + throw new UnsupportedOperationException("Invalid intention " + packethandshakinginsetprotocol.c()); + } + } + + public void a(IChatBaseComponent ichatbasecomponent) {} + + public void a(EnumProtocol enumprotocol, EnumProtocol enumprotocol1) { + if (enumprotocol1 != EnumProtocol.LOGIN && enumprotocol1 != EnumProtocol.STATUS) { + throw new UnsupportedOperationException("Invalid state " + enumprotocol1); + } + } + + public void a() {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/IDataManager.java b/vspigot-server/src/main/java/net/minecraft/server/IDataManager.java new file mode 100644 index 0000000..e5d578d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/IDataManager.java @@ -0,0 +1,28 @@ +package net.minecraft.server; + +import java.io.File; + +public interface IDataManager { + + WorldData getWorldData(); + + void checkSession() throws ExceptionWorldConflict; // CraftBukkit - throws ExceptionWorldConflict + + IChunkLoader createChunkLoader(WorldProvider worldprovider); + + void saveWorldData(WorldData worlddata, NBTTagCompound nbttagcompound); + + void saveWorldData(WorldData worlddata); + + IPlayerFileData getPlayerFileData(); + + void a(); + + File getDirectory(); + + File getDataFile(String s); + + String g(); + + java.util.UUID getUUID(); // CraftBukkit +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/IInventory.java b/vspigot-server/src/main/java/net/minecraft/server/IInventory.java new file mode 100644 index 0000000..bfc5c86 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/IInventory.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; // CraftBukkit + +public interface IInventory { + + int getSize(); + + ItemStack getItem(int i); + + ItemStack splitStack(int i, int j); + + ItemStack splitWithoutUpdate(int i); + + void setItem(int i, ItemStack itemstack); + + String getInventoryName(); + + boolean k_(); + + int getMaxStackSize(); + + void update(); + + boolean a(EntityHuman entityhuman); + + void startOpen(); + + void closeContainer(); + + boolean b(int i, ItemStack itemstack); + + // CraftBukkit start + ItemStack[] getContents(); + + void onOpen(CraftHumanEntity who); + + void onClose(CraftHumanEntity who); + + java.util.List getViewers(); + + org.bukkit.inventory.InventoryHolder getOwner(); + + void setMaxStackSize(int size); + + int MAX_STACK = 64; + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/IRecipe.java b/vspigot-server/src/main/java/net/minecraft/server/IRecipe.java new file mode 100644 index 0000000..c0836e4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/IRecipe.java @@ -0,0 +1,16 @@ +package net.minecraft.server; + +public interface IRecipe { + + boolean a(InventoryCrafting inventorycrafting, World world); + + ItemStack a(InventoryCrafting inventorycrafting); + + int a(); + + ItemStack b(); + + org.bukkit.inventory.Recipe toBukkitRecipe(); // CraftBukkit + + java.util.List getIngredients(); // Spigot +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/IntCache.java b/vspigot-server/src/main/java/net/minecraft/server/IntCache.java new file mode 100644 index 0000000..47e06df --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/IntCache.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; + +public class IntCache { + + private static int a = 256; + private static List b = new ArrayList(); + private static List c = new ArrayList(); + private static List d = new ArrayList(); + private static List e = new ArrayList(); + + public static synchronized int[] a(int i) { + int[] aint; + + if (i <= 256) { + if (b.isEmpty()) { + aint = new int[256]; + if (c.size() < org.spigotmc.SpigotConfig.intCacheLimit) c.add(aint); + return aint; + } else { + aint = (int[]) b.remove(b.size() - 1); + if (c.size() < org.spigotmc.SpigotConfig.intCacheLimit) c.add(aint); + return aint; + } + } else if (i > a) { + a = i; + d.clear(); + e.clear(); + aint = new int[a]; + if (e.size() < org.spigotmc.SpigotConfig.intCacheLimit) e.add(aint); + return aint; + } else if (d.isEmpty()) { + aint = new int[a]; + if (e.size() < org.spigotmc.SpigotConfig.intCacheLimit) e.add(aint); + return aint; + } else { + aint = (int[]) d.remove(d.size() - 1); + if (e.size() < org.spigotmc.SpigotConfig.intCacheLimit) e.add(aint); + return aint; + } + } + + public static synchronized void a() { + if (!d.isEmpty()) { + d.remove(d.size() - 1); + } + + if (!b.isEmpty()) { + b.remove(b.size() - 1); + } + + d.addAll(e); + b.addAll(c); + e.clear(); + c.clear(); + } + + public static synchronized String b() { + return "cache: " + d.size() + ", tcache: " + b.size() + ", allocated: " + e.size() + ", tallocated: " + c.size(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/IntHashMap.java b/vspigot-server/src/main/java/net/minecraft/server/IntHashMap.java new file mode 100644 index 0000000..fff9d7a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/IntHashMap.java @@ -0,0 +1,167 @@ +package net.minecraft.server; + +import java.util.HashSet; +import java.util.Set; + +public class IntHashMap { + + private transient IntHashMapEntry[] a = new IntHashMapEntry[16]; + private transient int b; + private int c = 12; + private final float d = 0.75F; + private transient volatile int e; + // private Set f = new HashSet(); // CraftBukkit - expensive and unused + + public IntHashMap() {} + + private static int g(int i) { + i ^= i >>> 20 ^ i >>> 12; + return i ^ i >>> 7 ^ i >>> 4; + } + + private static int a(int i, int j) { + return i & j - 1; + } + + public Object get(int i) { + int j = g(i); + + for (IntHashMapEntry inthashmapentry = this.a[a(j, this.a.length)]; inthashmapentry != null; inthashmapentry = inthashmapentry.c) { + if (inthashmapentry.a == i) { + return inthashmapentry.b; + } + } + + return null; + } + + public boolean b(int i) { + return this.c(i) != null; + } + + final IntHashMapEntry c(int i) { + int j = g(i); + + for (IntHashMapEntry inthashmapentry = this.a[a(j, this.a.length)]; inthashmapentry != null; inthashmapentry = inthashmapentry.c) { + if (inthashmapentry.a == i) { + return inthashmapentry; + } + } + + return null; + } + + public void a(int i, Object object) { + // this.f.add(Integer.valueOf(i)); // CraftBukkit + int j = g(i); + int k = a(j, this.a.length); + + for (IntHashMapEntry inthashmapentry = this.a[k]; inthashmapentry != null; inthashmapentry = inthashmapentry.c) { + if (inthashmapentry.a == i) { + inthashmapentry.b = object; + return; + } + } + + ++this.e; + this.a(j, i, object, k); + } + + private void h(int i) { + IntHashMapEntry[] ainthashmapentry = this.a; + int j = ainthashmapentry.length; + + if (j == 1073741824) { + this.c = Integer.MAX_VALUE; + } else { + IntHashMapEntry[] ainthashmapentry1 = new IntHashMapEntry[i]; + + this.a(ainthashmapentry1); + this.a = ainthashmapentry1; + this.c = (int) ((float) i * this.d); + } + } + + private void a(IntHashMapEntry[] ainthashmapentry) { + IntHashMapEntry[] ainthashmapentry1 = this.a; + int i = ainthashmapentry.length; + + for (int j = 0; j < ainthashmapentry1.length; ++j) { + IntHashMapEntry inthashmapentry = ainthashmapentry1[j]; + + if (inthashmapentry != null) { + ainthashmapentry1[j] = null; + + IntHashMapEntry inthashmapentry1; + + do { + inthashmapentry1 = inthashmapentry.c; + int k = a(inthashmapentry.d, i); + + inthashmapentry.c = ainthashmapentry[k]; + ainthashmapentry[k] = inthashmapentry; + inthashmapentry = inthashmapentry1; + } while (inthashmapentry1 != null); + } + } + } + + public Object d(int i) { + // this.f.remove(Integer.valueOf(i)); // CraftBukkit + IntHashMapEntry inthashmapentry = this.e(i); + + return inthashmapentry == null ? null : inthashmapentry.b; + } + + final IntHashMapEntry e(int i) { + int j = g(i); + int k = a(j, this.a.length); + IntHashMapEntry inthashmapentry = this.a[k]; + + IntHashMapEntry inthashmapentry1; + IntHashMapEntry inthashmapentry2; + + for (inthashmapentry1 = inthashmapentry; inthashmapentry1 != null; inthashmapentry1 = inthashmapentry2) { + inthashmapentry2 = inthashmapentry1.c; + if (inthashmapentry1.a == i) { + ++this.e; + --this.b; + if (inthashmapentry == inthashmapentry1) { + this.a[k] = inthashmapentry2; + } else { + inthashmapentry.c = inthashmapentry2; + } + + return inthashmapentry1; + } + + inthashmapentry = inthashmapentry1; + } + + return inthashmapentry1; + } + + public void c() { + ++this.e; + IntHashMapEntry[] ainthashmapentry = this.a; + + for (int i = 0; i < ainthashmapentry.length; ++i) { + ainthashmapentry[i] = null; + } + + this.b = 0; + } + + private void a(int i, int j, Object object, int k) { + IntHashMapEntry inthashmapentry = this.a[k]; + + this.a[k] = new IntHashMapEntry(i, j, object, inthashmapentry); + if (this.b++ >= this.c) { + this.h(2 * this.a.length); + } + } + + static int f(int i) { + return g(i); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/InventoryCraftResult.java b/vspigot-server/src/main/java/net/minecraft/server/InventoryCraftResult.java new file mode 100644 index 0000000..7db8290 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/InventoryCraftResult.java @@ -0,0 +1,96 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryCraftResult implements IInventory { + + private ItemStack[] items = new ItemStack[1]; + + // CraftBukkit start + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return null; // Result slots don't get an owner + } + + // Don't need a transaction; the InventoryCrafting keeps track of it for us + public void onOpen(CraftHumanEntity who) {} + public void onClose(CraftHumanEntity who) {} + public java.util.List getViewers() { + return new java.util.ArrayList(); + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public InventoryCraftResult() {} + + public int getSize() { + return 1; + } + + public ItemStack getItem(int i) { + return this.items[0]; + } + + public String getInventoryName() { + return "Result"; + } + + public boolean k_() { + return false; + } + + public ItemStack splitStack(int i, int j) { + if (this.items[0] != null) { + ItemStack itemstack = this.items[0]; + + this.items[0] = null; + return itemstack; + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.items[0] != null) { + ItemStack itemstack = this.items[0]; + + this.items[0] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.items[0] = itemstack; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/InventoryCrafting.java b/vspigot-server/src/main/java/net/minecraft/server/InventoryCrafting.java new file mode 100644 index 0000000..5b46597 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/InventoryCrafting.java @@ -0,0 +1,149 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +// CraftBukkit end + +public class InventoryCrafting implements IInventory { + + private ItemStack[] items; + private int b; + private Container c; + + // CraftBukkit start - add fields + public List transaction = new java.util.ArrayList(); + public IRecipe currentRecipe; + public IInventory resultInventory; + private EntityHuman owner; + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public InventoryType getInvType() { + return items.length == 4 ? InventoryType.CRAFTING : InventoryType.WORKBENCH; + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return owner.getBukkitEntity(); + } + + public void setMaxStackSize(int size) { + maxStack = size; + resultInventory.setMaxStackSize(size); + } + + public InventoryCrafting(Container container, int i, int j, EntityHuman player) { + this(container, i, j); + this.owner = player; + } + // CraftBukkit end + + public InventoryCrafting(Container container, int i, int j) { + int k = i * j; + + this.items = new ItemStack[k]; + this.c = container; + this.b = i; + } + + public int getSize() { + return this.items.length; + } + + public ItemStack getItem(int i) { + return i >= this.getSize() ? null : this.items[i]; + } + + public ItemStack b(int i, int j) { + if (i >= 0 && i < this.b) { + int k = i + j * this.b; + + return this.getItem(k); + } else { + return null; + } + } + + public String getInventoryName() { + return "container.crafting"; + } + + public boolean k_() { + return false; + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.items[i] != null) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public ItemStack splitStack(int i, int j) { + if (this.items[i] != null) { + ItemStack itemstack; + + if (this.items[i].count <= j) { + itemstack = this.items[i]; + this.items[i] = null; + this.c.a((IInventory) this); + return itemstack; + } else { + itemstack = this.items[i].a(j); + if (this.items[i].count == 0) { + this.items[i] = null; + } + + this.c.a((IInventory) this); + return itemstack; + } + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.items[i] = itemstack; + this.c.a((IInventory) this); + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/InventoryEnderChest.java b/vspigot-server/src/main/java/net/minecraft/server/InventoryEnderChest.java new file mode 100644 index 0000000..ed23f78 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/InventoryEnderChest.java @@ -0,0 +1,110 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryEnderChest extends InventorySubcontainer { + + private TileEntityEnderChest a; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + public org.bukkit.entity.Player player; + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return this.player; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + + public int getMaxStackSize() { + return maxStack; + } + // CraftBukkit end + + public InventoryEnderChest() { + super("container.enderchest", false, 27); + } + + public void a(TileEntityEnderChest tileentityenderchest) { + this.a = tileentityenderchest; + } + + public void a(NBTTagList nbttaglist) { + int i; + + for (i = 0; i < this.getSize(); ++i) { + this.setItem(i, (ItemStack) null); + } + + for (i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.get(i); + int j = nbttagcompound.getByte("Slot") & 255; + + if (j >= 0 && j < this.getSize()) { + this.setItem(j, ItemStack.createStack(nbttagcompound)); + } + } + } + + public NBTTagList h() { + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.getSize(); ++i) { + ItemStack itemstack = this.getItem(i); + + if (itemstack != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setByte("Slot", (byte) i); + itemstack.save(nbttagcompound); + nbttaglist.add(nbttagcompound); + } + } + + return nbttaglist; + } + + public boolean a(EntityHuman entityhuman) { + return this.a != null && !this.a.a(entityhuman) ? false : super.a(entityhuman); + } + + public void startOpen() { + if (this.a != null) { + this.a.a(); + } + + super.startOpen(); + } + + public void closeContainer() { + if (this.a != null) { + this.a.b(); + } + + super.closeContainer(); + this.a = null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/InventoryHorseChest.java b/vspigot-server/src/main/java/net/minecraft/server/InventoryHorseChest.java new file mode 100644 index 0000000..0128995 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/InventoryHorseChest.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryHorseChest extends InventorySubcontainer { + + public InventoryHorseChest(String s, int i) { + super(s, false, i); + } + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private EntityHorse horse; + private int maxStack = MAX_STACK; + + public InventoryHorseChest(String s, int i, EntityHorse horse) { + this(s, i); + this.horse = horse; + } + + @Override + public ItemStack[] getContents() { + return this.items; + } + + @Override + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + @Override + public List getViewers() { + return transaction; + } + + @Override + public org.bukkit.inventory.InventoryHolder getOwner() { + return (org.bukkit.entity.Horse) this.horse.getBukkitEntity(); + } + + @Override + public void setMaxStackSize(int size) { + maxStack = size; + } + + @Override + public int getMaxStackSize() { + return maxStack; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/InventoryLargeChest.java b/vspigot-server/src/main/java/net/minecraft/server/InventoryLargeChest.java new file mode 100644 index 0000000..263de7c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/InventoryLargeChest.java @@ -0,0 +1,129 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryLargeChest implements IInventory { + + private String a; + public IInventory left; // CraftBukkit - private -> public + public IInventory right; // CraftBukkit - private -> public + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + + public ItemStack[] getContents() { + ItemStack[] result = new ItemStack[this.getSize()]; + for (int i = 0; i < result.length; i++) { + result[i] = this.getItem(i); + } + return result; + } + + public void onOpen(CraftHumanEntity who) { + this.left.onOpen(who); + this.right.onOpen(who); + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + this.left.onClose(who); + this.right.onClose(who); + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return null; // This method won't be called since CraftInventoryDoubleChest doesn't defer to here + } + + public void setMaxStackSize(int size) { + this.left.setMaxStackSize(size); + this.right.setMaxStackSize(size); + } + // CraftBukkit end + + public InventoryLargeChest(String s, IInventory iinventory, IInventory iinventory1) { + this.a = s; + if (iinventory == null) { + iinventory = iinventory1; + } + + if (iinventory1 == null) { + iinventory1 = iinventory; + } + + this.left = iinventory; + this.right = iinventory1; + } + + public int getSize() { + return this.left.getSize() + this.right.getSize(); + } + + public boolean a(IInventory iinventory) { + return this.left == iinventory || this.right == iinventory; + } + + public String getInventoryName() { + return this.left.k_() ? this.left.getInventoryName() : (this.right.k_() ? this.right.getInventoryName() : this.a); + } + + public boolean k_() { + return this.left.k_() || this.right.k_(); + } + + public ItemStack getItem(int i) { + return i >= this.left.getSize() ? this.right.getItem(i - this.left.getSize()) : this.left.getItem(i); + } + + public ItemStack splitStack(int i, int j) { + return i >= this.left.getSize() ? this.right.splitStack(i - this.left.getSize(), j) : this.left.splitStack(i, j); + } + + public ItemStack splitWithoutUpdate(int i) { + return i >= this.left.getSize() ? this.right.splitWithoutUpdate(i - this.left.getSize()) : this.left.splitWithoutUpdate(i); + } + + public void setItem(int i, ItemStack itemstack) { + if (i >= this.left.getSize()) { + this.right.setItem(i - this.left.getSize(), itemstack); + } else { + this.left.setItem(i, itemstack); + } + } + + public int getMaxStackSize() { + return Math.min(this.left.getMaxStackSize(), this.right.getMaxStackSize()); // CraftBukkit - check both sides + } + + public void update() { + this.left.update(); + this.right.update(); + } + + public boolean a(EntityHuman entityhuman) { + return this.left.a(entityhuman) && this.right.a(entityhuman); + } + + public void startOpen() { + this.left.startOpen(); + this.right.startOpen(); + } + + public void closeContainer() { + this.left.closeContainer(); + this.right.closeContainer(); + } + + public boolean b(int i, ItemStack itemstack) { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/InventoryMerchant.java b/vspigot-server/src/main/java/net/minecraft/server/InventoryMerchant.java new file mode 100644 index 0000000..b5617b3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/InventoryMerchant.java @@ -0,0 +1,192 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryMerchant implements IInventory { + + private final IMerchant merchant; + private ItemStack[] itemsInSlots = new ItemStack[3]; + private final EntityHuman player; + private MerchantRecipe recipe; + private int e; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.itemsInSlots; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int i) { + maxStack = i; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return player.getBukkitEntity(); + } + // CraftBukkit end + + public InventoryMerchant(EntityHuman entityhuman, IMerchant imerchant) { + this.player = entityhuman; + this.merchant = imerchant; + } + + public int getSize() { + return this.itemsInSlots.length; + } + + public ItemStack getItem(int i) { + return this.itemsInSlots[i]; + } + + public ItemStack splitStack(int i, int j) { + if (this.itemsInSlots[i] != null) { + ItemStack itemstack; + + if (i == 2) { + itemstack = this.itemsInSlots[i]; + this.itemsInSlots[i] = null; + return itemstack; + } else if (this.itemsInSlots[i].count <= j) { + itemstack = this.itemsInSlots[i]; + this.itemsInSlots[i] = null; + if (this.d(i)) { + this.h(); + } + + return itemstack; + } else { + itemstack = this.itemsInSlots[i].a(j); + if (this.itemsInSlots[i].count == 0) { + this.itemsInSlots[i] = null; + } + + if (this.d(i)) { + this.h(); + } + + return itemstack; + } + } else { + return null; + } + } + + private boolean d(int i) { + return i == 0 || i == 1; + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.itemsInSlots[i] != null) { + ItemStack itemstack = this.itemsInSlots[i]; + + this.itemsInSlots[i] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.itemsInSlots[i] = itemstack; + if (itemstack != null && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + + if (this.d(i)) { + this.h(); + } + } + + public String getInventoryName() { + return "mob.villager"; + } + + public boolean k_() { + return false; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + return this.merchant.b() == entityhuman; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public void update() { + this.h(); + } + + public void h() { + this.recipe = null; + ItemStack itemstack = this.itemsInSlots[0]; + ItemStack itemstack1 = this.itemsInSlots[1]; + + if (itemstack == null) { + itemstack = itemstack1; + itemstack1 = null; + } + + if (itemstack == null) { + this.setItem(2, (ItemStack) null); + } else { + MerchantRecipeList merchantrecipelist = this.merchant.getOffers(this.player); + + if (merchantrecipelist != null) { + MerchantRecipe merchantrecipe = merchantrecipelist.a(itemstack, itemstack1, this.e); + + if (merchantrecipe != null && !merchantrecipe.g()) { + this.recipe = merchantrecipe; + this.setItem(2, merchantrecipe.getBuyItem3().cloneItemStack()); + } else if (itemstack1 != null) { + merchantrecipe = merchantrecipelist.a(itemstack1, itemstack, this.e); + if (merchantrecipe != null && !merchantrecipe.g()) { + this.recipe = merchantrecipe; + this.setItem(2, merchantrecipe.getBuyItem3().cloneItemStack()); + } else { + this.setItem(2, (ItemStack) null); + } + } else { + this.setItem(2, (ItemStack) null); + } + } + } + + this.merchant.a_(this.getItem(2)); + } + + public MerchantRecipe getRecipe() { + return this.recipe; + } + + public void c(int i) { + this.e = i; + this.h(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/InventorySubcontainer.java b/vspigot-server/src/main/java/net/minecraft/server/InventorySubcontainer.java new file mode 100644 index 0000000..9a775e4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/InventorySubcontainer.java @@ -0,0 +1,120 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; + +public abstract class InventorySubcontainer implements IInventory { // CraftBukkit - abstract + + private String a; + private int b; + protected ItemStack[] items; // CraftBukkit - protected + private List d; + private boolean e; + + public InventorySubcontainer(String s, boolean flag, int i) { + this.a = s; + this.e = flag; + this.b = i; + this.items = new ItemStack[i]; + } + + public void a(IInventoryListener iinventorylistener) { + if (this.d == null) { + this.d = new ArrayList(); + } + + this.d.add(iinventorylistener); + } + + public void b(IInventoryListener iinventorylistener) { + this.d.remove(iinventorylistener); + } + + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.length ? this.items[i] : null; + } + + public ItemStack splitStack(int i, int j) { + if (this.items[i] != null) { + ItemStack itemstack; + + if (this.items[i].count <= j) { + itemstack = this.items[i]; + this.items[i] = null; + this.update(); + return itemstack; + } else { + itemstack = this.items[i].a(j); + if (this.items[i].count == 0) { + this.items[i] = null; + } + + this.update(); + return itemstack; + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.items[i] != null) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.items[i] = itemstack; + if (itemstack != null && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + + this.update(); + } + + public int getSize() { + return this.b; + } + + public String getInventoryName() { + return this.a; + } + + public boolean k_() { + return this.e; + } + + public void a(String s) { + this.e = true; + this.a = s; + } + + public int getMaxStackSize() { + return 64; + } + + public void update() { + if (this.d != null) { + for (int i = 0; i < this.d.size(); ++i) { + ((IInventoryListener) this.d.get(i)).a(this); + } + } + } + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Item.java b/vspigot-server/src/main/java/net/minecraft/server/Item.java new file mode 100644 index 0000000..4382f1a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Item.java @@ -0,0 +1,503 @@ +package net.minecraft.server; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Random; +import java.util.UUID; + +import net.minecraft.util.com.google.common.collect.HashMultimap; +import net.minecraft.util.com.google.common.collect.Multimap; +import net.minecraft.util.com.google.common.collect.Sets; + +public class Item { + + public static final RegistryMaterials REGISTRY = new RegistryMaterials(); + protected static final UUID f = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF"); + private CreativeModeTab a; + protected static Random g = new Random(); + protected int maxStackSize = 64; + private int durability; + protected boolean i; + protected boolean j; + private Item craftingResult; + private String d; + private String name; + protected String l; + + public Item() {} + + public static int getId(Item item) { + return item == null ? 0 : REGISTRY.b(item); + } + + public static Item getById(int i) { + return (Item) REGISTRY.a(i); + } + + public static Item getItemOf(Block block) { + return getById(Block.getId(block)); + } + + public static void l() { + REGISTRY.a(256, "iron_shovel", (new ItemSpade(EnumToolMaterial.IRON)).c("shovelIron").f("iron_shovel")); + REGISTRY.a(257, "iron_pickaxe", (new ItemPickaxe(EnumToolMaterial.IRON)).c("pickaxeIron").f("iron_pickaxe")); + REGISTRY.a(258, "iron_axe", (new ItemAxe(EnumToolMaterial.IRON)).c("hatchetIron").f("iron_axe")); + REGISTRY.a(259, "flint_and_steel", (new ItemFlintAndSteel()).c("flintAndSteel").f("flint_and_steel")); + REGISTRY.a(260, "apple", (new ItemFood(4, 0.3F, false)).c("apple").f("apple")); + REGISTRY.a(261, "bow", (new ItemBow()).c("bow").f("bow")); + REGISTRY.a(262, "arrow", (new Item()).c("arrow").a(CreativeModeTab.j).f("arrow")); + REGISTRY.a(263, "coal", (new ItemCoal()).c("coal").f("coal")); + REGISTRY.a(264, "diamond", (new Item()).c("diamond").a(CreativeModeTab.l).f("diamond")); + REGISTRY.a(265, "iron_ingot", (new Item()).c("ingotIron").a(CreativeModeTab.l).f("iron_ingot")); + REGISTRY.a(266, "gold_ingot", (new Item()).c("ingotGold").a(CreativeModeTab.l).f("gold_ingot")); + REGISTRY.a(267, "iron_sword", (new ItemSword(EnumToolMaterial.IRON)).c("swordIron").f("iron_sword")); + REGISTRY.a(268, "wooden_sword", (new ItemSword(EnumToolMaterial.WOOD)).c("swordWood").f("wood_sword")); + REGISTRY.a(269, "wooden_shovel", (new ItemSpade(EnumToolMaterial.WOOD)).c("shovelWood").f("wood_shovel")); + REGISTRY.a(270, "wooden_pickaxe", (new ItemPickaxe(EnumToolMaterial.WOOD)).c("pickaxeWood").f("wood_pickaxe")); + REGISTRY.a(271, "wooden_axe", (new ItemAxe(EnumToolMaterial.WOOD)).c("hatchetWood").f("wood_axe")); + REGISTRY.a(272, "stone_sword", (new ItemSword(EnumToolMaterial.STONE)).c("swordStone").f("stone_sword")); + REGISTRY.a(273, "stone_shovel", (new ItemSpade(EnumToolMaterial.STONE)).c("shovelStone").f("stone_shovel")); + REGISTRY.a(274, "stone_pickaxe", (new ItemPickaxe(EnumToolMaterial.STONE)).c("pickaxeStone").f("stone_pickaxe")); + REGISTRY.a(275, "stone_axe", (new ItemAxe(EnumToolMaterial.STONE)).c("hatchetStone").f("stone_axe")); + REGISTRY.a(276, "diamond_sword", (new ItemSword(EnumToolMaterial.DIAMOND)).c("swordDiamond").f("diamond_sword")); + REGISTRY.a(277, "diamond_shovel", (new ItemSpade(EnumToolMaterial.DIAMOND)).c("shovelDiamond").f("diamond_shovel")); + REGISTRY.a(278, "diamond_pickaxe", (new ItemPickaxe(EnumToolMaterial.DIAMOND)).c("pickaxeDiamond").f("diamond_pickaxe")); + REGISTRY.a(279, "diamond_axe", (new ItemAxe(EnumToolMaterial.DIAMOND)).c("hatchetDiamond").f("diamond_axe")); + REGISTRY.a(280, "stick", (new Item()).q().c("stick").a(CreativeModeTab.l).f("stick")); + REGISTRY.a(281, "bowl", (new Item()).c("bowl").a(CreativeModeTab.l).f("bowl")); + REGISTRY.a(282, "mushroom_stew", (new ItemSoup(6)).c("mushroomStew").f("mushroom_stew")); + REGISTRY.a(283, "golden_sword", (new ItemSword(EnumToolMaterial.GOLD)).c("swordGold").f("gold_sword")); + REGISTRY.a(284, "golden_shovel", (new ItemSpade(EnumToolMaterial.GOLD)).c("shovelGold").f("gold_shovel")); + REGISTRY.a(285, "golden_pickaxe", (new ItemPickaxe(EnumToolMaterial.GOLD)).c("pickaxeGold").f("gold_pickaxe")); + REGISTRY.a(286, "golden_axe", (new ItemAxe(EnumToolMaterial.GOLD)).c("hatchetGold").f("gold_axe")); + REGISTRY.a(287, "string", (new ItemReed(Blocks.TRIPWIRE)).c("string").a(CreativeModeTab.l).f("string")); + REGISTRY.a(288, "feather", (new Item()).c("feather").a(CreativeModeTab.l).f("feather")); + REGISTRY.a(289, "gunpowder", (new Item()).c("sulphur").e(PotionBrewer.k).a(CreativeModeTab.l).f("gunpowder")); + REGISTRY.a(290, "wooden_hoe", (new ItemHoe(EnumToolMaterial.WOOD)).c("hoeWood").f("wood_hoe")); + REGISTRY.a(291, "stone_hoe", (new ItemHoe(EnumToolMaterial.STONE)).c("hoeStone").f("stone_hoe")); + REGISTRY.a(292, "iron_hoe", (new ItemHoe(EnumToolMaterial.IRON)).c("hoeIron").f("iron_hoe")); + REGISTRY.a(293, "diamond_hoe", (new ItemHoe(EnumToolMaterial.DIAMOND)).c("hoeDiamond").f("diamond_hoe")); + REGISTRY.a(294, "golden_hoe", (new ItemHoe(EnumToolMaterial.GOLD)).c("hoeGold").f("gold_hoe")); + REGISTRY.a(295, "wheat_seeds", (new ItemSeeds(Blocks.CROPS, Blocks.SOIL)).c("seeds").f("seeds_wheat")); + REGISTRY.a(296, "wheat", (new Item()).c("wheat").a(CreativeModeTab.l).f("wheat")); + REGISTRY.a(297, "bread", (new ItemFood(5, 0.6F, false)).c("bread").f("bread")); + REGISTRY.a(298, "leather_helmet", (new ItemArmor(EnumArmorMaterial.CLOTH, 0, 0)).c("helmetCloth").f("leather_helmet")); + REGISTRY.a(299, "leather_chestplate", (new ItemArmor(EnumArmorMaterial.CLOTH, 0, 1)).c("chestplateCloth").f("leather_chestplate")); + REGISTRY.a(300, "leather_leggings", (new ItemArmor(EnumArmorMaterial.CLOTH, 0, 2)).c("leggingsCloth").f("leather_leggings")); + REGISTRY.a(301, "leather_boots", (new ItemArmor(EnumArmorMaterial.CLOTH, 0, 3)).c("bootsCloth").f("leather_boots")); + REGISTRY.a(302, "chainmail_helmet", (new ItemArmor(EnumArmorMaterial.CHAIN, 1, 0)).c("helmetChain").f("chainmail_helmet")); + REGISTRY.a(303, "chainmail_chestplate", (new ItemArmor(EnumArmorMaterial.CHAIN, 1, 1)).c("chestplateChain").f("chainmail_chestplate")); + REGISTRY.a(304, "chainmail_leggings", (new ItemArmor(EnumArmorMaterial.CHAIN, 1, 2)).c("leggingsChain").f("chainmail_leggings")); + REGISTRY.a(305, "chainmail_boots", (new ItemArmor(EnumArmorMaterial.CHAIN, 1, 3)).c("bootsChain").f("chainmail_boots")); + REGISTRY.a(306, "iron_helmet", (new ItemArmor(EnumArmorMaterial.IRON, 2, 0)).c("helmetIron").f("iron_helmet")); + REGISTRY.a(307, "iron_chestplate", (new ItemArmor(EnumArmorMaterial.IRON, 2, 1)).c("chestplateIron").f("iron_chestplate")); + REGISTRY.a(308, "iron_leggings", (new ItemArmor(EnumArmorMaterial.IRON, 2, 2)).c("leggingsIron").f("iron_leggings")); + REGISTRY.a(309, "iron_boots", (new ItemArmor(EnumArmorMaterial.IRON, 2, 3)).c("bootsIron").f("iron_boots")); + REGISTRY.a(310, "diamond_helmet", (new ItemArmor(EnumArmorMaterial.DIAMOND, 3, 0)).c("helmetDiamond").f("diamond_helmet")); + REGISTRY.a(311, "diamond_chestplate", (new ItemArmor(EnumArmorMaterial.DIAMOND, 3, 1)).c("chestplateDiamond").f("diamond_chestplate")); + REGISTRY.a(312, "diamond_leggings", (new ItemArmor(EnumArmorMaterial.DIAMOND, 3, 2)).c("leggingsDiamond").f("diamond_leggings")); + REGISTRY.a(313, "diamond_boots", (new ItemArmor(EnumArmorMaterial.DIAMOND, 3, 3)).c("bootsDiamond").f("diamond_boots")); + REGISTRY.a(314, "golden_helmet", (new ItemArmor(EnumArmorMaterial.GOLD, 4, 0)).c("helmetGold").f("gold_helmet")); + REGISTRY.a(315, "golden_chestplate", (new ItemArmor(EnumArmorMaterial.GOLD, 4, 1)).c("chestplateGold").f("gold_chestplate")); + REGISTRY.a(316, "golden_leggings", (new ItemArmor(EnumArmorMaterial.GOLD, 4, 2)).c("leggingsGold").f("gold_leggings")); + REGISTRY.a(317, "golden_boots", (new ItemArmor(EnumArmorMaterial.GOLD, 4, 3)).c("bootsGold").f("gold_boots")); + REGISTRY.a(318, "flint", (new Item()).c("flint").a(CreativeModeTab.l).f("flint")); + REGISTRY.a(319, "porkchop", (new ItemFood(3, 0.3F, true)).c("porkchopRaw").f("porkchop_raw")); + REGISTRY.a(320, "cooked_porkchop", (new ItemFood(8, 0.8F, true)).c("porkchopCooked").f("porkchop_cooked")); + REGISTRY.a(321, "painting", (new ItemHanging(EntityPainting.class)).c("painting").f("painting")); + REGISTRY.a(322, "golden_apple", (new ItemGoldenApple(4, 1.2F, false)).j().a(MobEffectList.REGENERATION.id, 5, 1, 1.0F).c("appleGold").f("apple_golden")); + REGISTRY.a(323, "sign", (new ItemSign()).c("sign").f("sign")); + REGISTRY.a(324, "wooden_door", (new ItemDoor(Material.WOOD)).c("doorWood").f("door_wood")); + Item item = (new ItemBucket(Blocks.AIR)).c("bucket").e(16).f("bucket_empty"); + + REGISTRY.a(325, "bucket", item); + REGISTRY.a(326, "water_bucket", (new ItemBucket(Blocks.WATER)).c("bucketWater").c(item).f("bucket_water")); + REGISTRY.a(327, "lava_bucket", (new ItemBucket(Blocks.LAVA)).c("bucketLava").c(item).f("bucket_lava")); + REGISTRY.a(328, "minecart", (new ItemMinecart(0)).c("minecart").f("minecart_normal")); + REGISTRY.a(329, "saddle", (new ItemSaddle()).c("saddle").f("saddle")); + REGISTRY.a(330, "iron_door", (new ItemDoor(Material.ORE)).c("doorIron").f("door_iron")); + REGISTRY.a(331, "redstone", (new ItemRedstone()).c("redstone").e(PotionBrewer.i).f("redstone_dust")); + REGISTRY.a(332, "snowball", (new ItemSnowball()).c("snowball").f("snowball")); + REGISTRY.a(333, "boat", (new ItemBoat()).c("boat").f("boat")); + REGISTRY.a(334, "leather", (new Item()).c("leather").a(CreativeModeTab.l).f("leather")); + REGISTRY.a(335, "milk_bucket", (new ItemMilkBucket()).c("milk").c(item).f("bucket_milk")); + REGISTRY.a(336, "brick", (new Item()).c("brick").a(CreativeModeTab.l).f("brick")); + REGISTRY.a(337, "clay_ball", (new Item()).c("clay").a(CreativeModeTab.l).f("clay_ball")); + REGISTRY.a(338, "reeds", (new ItemReed(Blocks.SUGAR_CANE_BLOCK)).c("reeds").a(CreativeModeTab.l).f("reeds")); + REGISTRY.a(339, "paper", (new Item()).c("paper").a(CreativeModeTab.f).f("paper")); + REGISTRY.a(340, "book", (new ItemBook()).c("book").a(CreativeModeTab.f).f("book_normal")); + REGISTRY.a(341, "slime_ball", (new Item()).c("slimeball").a(CreativeModeTab.f).f("slimeball")); + REGISTRY.a(342, "chest_minecart", (new ItemMinecart(1)).c("minecartChest").f("minecart_chest")); + REGISTRY.a(343, "furnace_minecart", (new ItemMinecart(2)).c("minecartFurnace").f("minecart_furnace")); + REGISTRY.a(344, "egg", (new ItemEgg()).c("egg").f("egg")); + REGISTRY.a(345, "compass", (new Item()).c("compass").a(CreativeModeTab.i).f("compass")); + REGISTRY.a(346, "fishing_rod", (new ItemFishingRod()).c("fishingRod").f("fishing_rod")); + REGISTRY.a(347, "clock", (new Item()).c("clock").a(CreativeModeTab.i).f("clock")); + REGISTRY.a(348, "glowstone_dust", (new Item()).c("yellowDust").e(PotionBrewer.j).a(CreativeModeTab.l).f("glowstone_dust")); + REGISTRY.a(349, "fish", (new ItemFish(false)).c("fish").f("fish_raw").a(true)); + REGISTRY.a(350, "cooked_fished", (new ItemFish(true)).c("fish").f("fish_cooked").a(true)); + REGISTRY.a(351, "dye", (new ItemDye()).c("dyePowder").f("dye_powder")); + REGISTRY.a(352, "bone", (new Item()).c("bone").q().a(CreativeModeTab.f).f("bone")); + REGISTRY.a(353, "sugar", (new Item()).c("sugar").e(PotionBrewer.b).a(CreativeModeTab.l).f("sugar")); + REGISTRY.a(354, "cake", (new ItemReed(Blocks.CAKE_BLOCK)).e(1).c("cake").a(CreativeModeTab.h).f("cake")); + REGISTRY.a(355, "bed", (new ItemBed()).e(1).c("bed").f("bed")); + REGISTRY.a(356, "repeater", (new ItemReed(Blocks.DIODE_OFF)).c("diode").a(CreativeModeTab.d).f("repeater")); + REGISTRY.a(357, "cookie", (new ItemFood(2, 0.1F, false)).c("cookie").f("cookie")); + REGISTRY.a(358, "filled_map", (new ItemWorldMap()).c("map").f("map_filled")); + REGISTRY.a(359, "shears", (new ItemShears()).c("shears").f("shears")); + REGISTRY.a(360, "melon", (new ItemFood(2, 0.3F, false)).c("melon").f("melon")); + REGISTRY.a(361, "pumpkin_seeds", (new ItemSeeds(Blocks.PUMPKIN_STEM, Blocks.SOIL)).c("seeds_pumpkin").f("seeds_pumpkin")); + REGISTRY.a(362, "melon_seeds", (new ItemSeeds(Blocks.MELON_STEM, Blocks.SOIL)).c("seeds_melon").f("seeds_melon")); + REGISTRY.a(363, "beef", (new ItemFood(3, 0.3F, true)).c("beefRaw").f("beef_raw")); + REGISTRY.a(364, "cooked_beef", (new ItemFood(8, 0.8F, true)).c("beefCooked").f("beef_cooked")); + REGISTRY.a(365, "chicken", (new ItemFood(2, 0.3F, true)).a(MobEffectList.HUNGER.id, 30, 0, 0.3F).c("chickenRaw").f("chicken_raw")); + REGISTRY.a(366, "cooked_chicken", (new ItemFood(6, 0.6F, true)).c("chickenCooked").f("chicken_cooked")); + REGISTRY.a(367, "rotten_flesh", (new ItemFood(4, 0.1F, true)).a(MobEffectList.HUNGER.id, 30, 0, 0.8F).c("rottenFlesh").f("rotten_flesh")); + REGISTRY.a(368, "ender_pearl", (new ItemEnderPearl()).c("enderPearl").f("ender_pearl")); + REGISTRY.a(369, "blaze_rod", (new Item()).c("blazeRod").a(CreativeModeTab.l).f("blaze_rod")); + REGISTRY.a(370, "ghast_tear", (new Item()).c("ghastTear").e(PotionBrewer.c).a(CreativeModeTab.k).f("ghast_tear")); + REGISTRY.a(371, "gold_nugget", (new Item()).c("goldNugget").a(CreativeModeTab.l).f("gold_nugget")); + REGISTRY.a(372, "nether_wart", (new ItemSeeds(Blocks.NETHER_WART, Blocks.SOUL_SAND)).c("netherStalkSeeds").e("+4").f("nether_wart")); + REGISTRY.a(373, "potion", (new ItemPotion()).c("potion").f("potion")); + REGISTRY.a(374, "glass_bottle", (new ItemGlassBottle()).c("glassBottle").f("potion_bottle_empty")); + REGISTRY.a(375, "spider_eye", (new ItemFood(2, 0.8F, false)).a(MobEffectList.POISON.id, 5, 0, 1.0F).c("spiderEye").e(PotionBrewer.d).f("spider_eye")); + REGISTRY.a(376, "fermented_spider_eye", (new Item()).c("fermentedSpiderEye").e(PotionBrewer.e).a(CreativeModeTab.k).f("spider_eye_fermented")); + REGISTRY.a(377, "blaze_powder", (new Item()).c("blazePowder").e(PotionBrewer.g).a(CreativeModeTab.k).f("blaze_powder")); + REGISTRY.a(378, "magma_cream", (new Item()).c("magmaCream").e(PotionBrewer.h).a(CreativeModeTab.k).f("magma_cream")); + REGISTRY.a(379, "brewing_stand", (new ItemReed(Blocks.BREWING_STAND)).c("brewingStand").a(CreativeModeTab.k).f("brewing_stand")); + REGISTRY.a(380, "cauldron", (new ItemReed(Blocks.CAULDRON)).c("cauldron").a(CreativeModeTab.k).f("cauldron")); + REGISTRY.a(381, "ender_eye", (new ItemEnderEye()).c("eyeOfEnder").f("ender_eye")); + REGISTRY.a(382, "speckled_melon", (new Item()).c("speckledMelon").e(PotionBrewer.f).a(CreativeModeTab.k).f("melon_speckled")); + REGISTRY.a(383, "spawn_egg", (new ItemMonsterEgg()).c("monsterPlacer").f("spawn_egg")); + REGISTRY.a(384, "experience_bottle", (new ItemExpBottle()).c("expBottle").f("experience_bottle")); + REGISTRY.a(385, "fire_charge", (new ItemFireball()).c("fireball").f("fireball")); + REGISTRY.a(386, "writable_book", (new ItemBookAndQuill()).c("writingBook").a(CreativeModeTab.f).f("book_writable")); + REGISTRY.a(387, "written_book", (new ItemWrittenBook()).c("writtenBook").f("book_written").e(16)); + REGISTRY.a(388, "emerald", (new Item()).c("emerald").a(CreativeModeTab.l).f("emerald")); + REGISTRY.a(389, "item_frame", (new ItemHanging(EntityItemFrame.class)).c("frame").f("item_frame")); + REGISTRY.a(390, "flower_pot", (new ItemReed(Blocks.FLOWER_POT)).c("flowerPot").a(CreativeModeTab.c).f("flower_pot")); + REGISTRY.a(391, "carrot", (new ItemSeedFood(4, 0.6F, Blocks.CARROTS, Blocks.SOIL)).c("carrots").f("carrot")); + REGISTRY.a(392, "potato", (new ItemSeedFood(1, 0.3F, Blocks.POTATOES, Blocks.SOIL)).c("potato").f("potato")); + REGISTRY.a(393, "baked_potato", (new ItemFood(6, 0.6F, false)).c("potatoBaked").f("potato_baked")); + REGISTRY.a(394, "poisonous_potato", (new ItemFood(2, 0.3F, false)).a(MobEffectList.POISON.id, 5, 0, 0.6F).c("potatoPoisonous").f("potato_poisonous")); + REGISTRY.a(395, "map", (new ItemMapEmpty()).c("emptyMap").f("map_empty")); + REGISTRY.a(396, "golden_carrot", (new ItemFood(6, 1.2F, false)).c("carrotGolden").e(PotionBrewer.l).f("carrot_golden")); + REGISTRY.a(397, "skull", (new ItemSkull()).c("skull").f("skull")); + REGISTRY.a(398, "carrot_on_a_stick", (new ItemCarrotStick()).c("carrotOnAStick").f("carrot_on_a_stick")); + REGISTRY.a(399, "nether_star", (new ItemNetherStar()).c("netherStar").a(CreativeModeTab.l).f("nether_star")); + REGISTRY.a(400, "pumpkin_pie", (new ItemFood(8, 0.3F, false)).c("pumpkinPie").a(CreativeModeTab.h).f("pumpkin_pie")); + REGISTRY.a(401, "fireworks", (new ItemFireworks()).c("fireworks").f("fireworks")); + REGISTRY.a(402, "firework_charge", (new ItemFireworksCharge()).c("fireworksCharge").a(CreativeModeTab.f).f("fireworks_charge")); + REGISTRY.a(403, "enchanted_book", (new ItemEnchantedBook()).e(1).c("enchantedBook").f("book_enchanted")); + REGISTRY.a(404, "comparator", (new ItemReed(Blocks.REDSTONE_COMPARATOR_OFF)).c("comparator").a(CreativeModeTab.d).f("comparator")); + REGISTRY.a(405, "netherbrick", (new Item()).c("netherbrick").a(CreativeModeTab.l).f("netherbrick")); + REGISTRY.a(406, "quartz", (new Item()).c("netherquartz").a(CreativeModeTab.l).f("quartz")); + REGISTRY.a(407, "tnt_minecart", (new ItemMinecart(3)).c("minecartTnt").f("minecart_tnt")); + REGISTRY.a(408, "hopper_minecart", (new ItemMinecart(5)).c("minecartHopper").f("minecart_hopper")); + REGISTRY.a(417, "iron_horse_armor", (new Item()).c("horsearmormetal").e(1).a(CreativeModeTab.f).f("iron_horse_armor")); + REGISTRY.a(418, "golden_horse_armor", (new Item()).c("horsearmorgold").e(1).a(CreativeModeTab.f).f("gold_horse_armor")); + REGISTRY.a(419, "diamond_horse_armor", (new Item()).c("horsearmordiamond").e(1).a(CreativeModeTab.f).f("diamond_horse_armor")); + REGISTRY.a(420, "lead", (new ItemLeash()).c("leash").f("lead")); + REGISTRY.a(421, "name_tag", (new ItemNameTag()).c("nameTag").f("name_tag")); + REGISTRY.a(422, "command_block_minecart", (new ItemMinecart(6)).c("minecartCommandBlock").f("minecart_command_block").a((CreativeModeTab) null)); + REGISTRY.a(2256, "record_13", (new ItemRecord("13")).c("record").f("record_13")); + REGISTRY.a(2257, "record_cat", (new ItemRecord("cat")).c("record").f("record_cat")); + REGISTRY.a(2258, "record_blocks", (new ItemRecord("blocks")).c("record").f("record_blocks")); + REGISTRY.a(2259, "record_chirp", (new ItemRecord("chirp")).c("record").f("record_chirp")); + REGISTRY.a(2260, "record_far", (new ItemRecord("far")).c("record").f("record_far")); + REGISTRY.a(2261, "record_mall", (new ItemRecord("mall")).c("record").f("record_mall")); + REGISTRY.a(2262, "record_mellohi", (new ItemRecord("mellohi")).c("record").f("record_mellohi")); + REGISTRY.a(2263, "record_stal", (new ItemRecord("stal")).c("record").f("record_stal")); + REGISTRY.a(2264, "record_strad", (new ItemRecord("strad")).c("record").f("record_strad")); + REGISTRY.a(2265, "record_ward", (new ItemRecord("ward")).c("record").f("record_ward")); + REGISTRY.a(2266, "record_11", (new ItemRecord("11")).c("record").f("record_11")); + REGISTRY.a(2267, "record_wait", (new ItemRecord("wait")).c("record").f("record_wait")); + HashSet hashset = Sets.newHashSet(new Block[] { Blocks.AIR, Blocks.BREWING_STAND, Blocks.BED, Blocks.NETHER_WART, Blocks.CAULDRON, Blocks.FLOWER_POT, Blocks.CROPS, Blocks.SUGAR_CANE_BLOCK, Blocks.CAKE_BLOCK, Blocks.SKULL, Blocks.PISTON_EXTENSION, Blocks.PISTON_MOVING, Blocks.GLOWING_REDSTONE_ORE, Blocks.DIODE_ON, Blocks.PUMPKIN_STEM, Blocks.SIGN_POST, Blocks.REDSTONE_COMPARATOR_ON, Blocks.TRIPWIRE, Blocks.REDSTONE_LAMP_ON, Blocks.MELON_STEM, Blocks.REDSTONE_TORCH_OFF, Blocks.REDSTONE_COMPARATOR_OFF, Blocks.REDSTONE_WIRE, Blocks.WALL_SIGN, Blocks.DIODE_OFF, Blocks.IRON_DOOR_BLOCK, Blocks.WOODEN_DOOR}); + Iterator iterator = Block.REGISTRY.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + Block block = (Block) Block.REGISTRY.get(s); + Object object; + + if (block == Blocks.WOOL) { + object = (new ItemCloth(Blocks.WOOL)).b("cloth"); + } else if (block == Blocks.STAINED_HARDENED_CLAY) { + object = (new ItemCloth(Blocks.STAINED_HARDENED_CLAY)).b("clayHardenedStained"); + } else if (block == Blocks.STAINED_GLASS) { + object = (new ItemCloth(Blocks.STAINED_GLASS)).b("stainedGlass"); + } else if (block == Blocks.STAINED_GLASS_PANE) { + object = (new ItemCloth(Blocks.STAINED_GLASS_PANE)).b("stainedGlassPane"); + } else if (block == Blocks.WOOL_CARPET) { + object = (new ItemCloth(Blocks.WOOL_CARPET)).b("woolCarpet"); + } else if (block == Blocks.DIRT) { + object = (new ItemMultiTexture(Blocks.DIRT, Blocks.DIRT, BlockDirt.a)).b("dirt"); + } else if (block == Blocks.SAND) { + object = (new ItemMultiTexture(Blocks.SAND, Blocks.SAND, BlockSand.a)).b("sand"); + } else if (block == Blocks.LOG) { + object = (new ItemMultiTexture(Blocks.LOG, Blocks.LOG, BlockLog1.M)).b("log"); + } else if (block == Blocks.LOG2) { + object = (new ItemMultiTexture(Blocks.LOG2, Blocks.LOG2, BlockLog2.M)).b("log"); + } else if (block == Blocks.WOOD) { + object = (new ItemMultiTexture(Blocks.WOOD, Blocks.WOOD, BlockWood.a)).b("wood"); + } else if (block == Blocks.MONSTER_EGGS) { + object = (new ItemMultiTexture(Blocks.MONSTER_EGGS, Blocks.MONSTER_EGGS, BlockMonsterEggs.a)).b("monsterStoneEgg"); + } else if (block == Blocks.SMOOTH_BRICK) { + object = (new ItemMultiTexture(Blocks.SMOOTH_BRICK, Blocks.SMOOTH_BRICK, BlockSmoothBrick.a)).b("stonebricksmooth"); + } else if (block == Blocks.SANDSTONE) { + object = (new ItemMultiTexture(Blocks.SANDSTONE, Blocks.SANDSTONE, BlockSandStone.a)).b("sandStone"); + } else if (block == Blocks.QUARTZ_BLOCK) { + object = (new ItemMultiTexture(Blocks.QUARTZ_BLOCK, Blocks.QUARTZ_BLOCK, BlockQuartz.a)).b("quartzBlock"); + } else if (block == Blocks.STEP) { + object = (new ItemStep(Blocks.STEP, Blocks.STEP, Blocks.DOUBLE_STEP, false)).b("stoneSlab"); + } else if (block == Blocks.DOUBLE_STEP) { + object = (new ItemStep(Blocks.DOUBLE_STEP, Blocks.STEP, Blocks.DOUBLE_STEP, true)).b("stoneSlab"); + } else if (block == Blocks.WOOD_STEP) { + object = (new ItemStep(Blocks.WOOD_STEP, Blocks.WOOD_STEP, Blocks.WOOD_DOUBLE_STEP, false)).b("woodSlab"); + } else if (block == Blocks.WOOD_DOUBLE_STEP) { + object = (new ItemStep(Blocks.WOOD_DOUBLE_STEP, Blocks.WOOD_STEP, Blocks.WOOD_DOUBLE_STEP, true)).b("woodSlab"); + } else if (block == Blocks.SAPLING) { + object = (new ItemMultiTexture(Blocks.SAPLING, Blocks.SAPLING, BlockSapling.a)).b("sapling"); + } else if (block == Blocks.LEAVES) { + object = (new ItemLeaves(Blocks.LEAVES)).b("leaves"); + } else if (block == Blocks.LEAVES2) { + object = (new ItemLeaves(Blocks.LEAVES2)).b("leaves"); + } else if (block == Blocks.VINE) { + object = new ItemWithAuxData(Blocks.VINE, false); + } else if (block == Blocks.LONG_GRASS) { + object = (new ItemWithAuxData(Blocks.LONG_GRASS, true)).a(new String[] { "shrub", "grass", "fern"}); + } else if (block == Blocks.YELLOW_FLOWER) { + object = (new ItemMultiTexture(Blocks.YELLOW_FLOWER, Blocks.YELLOW_FLOWER, BlockFlowers.b)).b("flower"); + } else if (block == Blocks.RED_ROSE) { + object = (new ItemMultiTexture(Blocks.RED_ROSE, Blocks.RED_ROSE, BlockFlowers.a)).b("rose"); + } else if (block == Blocks.SNOW) { + object = new ItemSnow(Blocks.SNOW, Blocks.SNOW); + } else if (block == Blocks.WATER_LILY) { + object = new ItemWaterLily(Blocks.WATER_LILY); + } else if (block == Blocks.PISTON) { + object = new ItemPiston(Blocks.PISTON); + } else if (block == Blocks.PISTON_STICKY) { + object = new ItemPiston(Blocks.PISTON_STICKY); + } else if (block == Blocks.COBBLE_WALL) { + object = (new ItemMultiTexture(Blocks.COBBLE_WALL, Blocks.COBBLE_WALL, BlockCobbleWall.a)).b("cobbleWall"); + } else if (block == Blocks.ANVIL) { + object = (new ItemAnvil(Blocks.ANVIL)).b("anvil"); + } else if (block == Blocks.DOUBLE_PLANT) { + object = (new ItemTallPlant(Blocks.DOUBLE_PLANT, Blocks.DOUBLE_PLANT, BlockTallPlant.a)).b("doublePlant"); + // CraftBukkit start - allow certain blocks to retain data + } else if (block == Blocks.MOB_SPAWNER || block == Blocks.BIG_MUSHROOM_1 || block == Blocks.BIG_MUSHROOM_2) { + object = new ItemWithAuxData(block, true); + // CraftBukkit end + } else { + if (hashset.contains(block)) { + continue; + } + + object = new ItemBlock(block); + } + + REGISTRY.a(Block.getId(block), s, object); + } + } + + public Item e(int i) { + this.maxStackSize = i; + return this; + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + return false; + } + + public float getDestroySpeed(ItemStack itemstack, Block block) { + return 1.0F; + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + return itemstack; + } + + public ItemStack b(ItemStack itemstack, World world, EntityHuman entityhuman) { + return itemstack; + } + + public int getMaxStackSize() { + return this.maxStackSize; + } + + public int filterData(int i) { + return 0; + } + + public boolean n() { + return this.j; + } + + protected Item a(boolean flag) { + this.j = flag; + return this; + } + + public int getMaxDurability() { + return this.durability; + } + + protected Item setMaxDurability(int i) { + this.durability = i; + return this; + } + + public boolean usesDurability() { + return this.durability > 0 && !this.j; + } + + public boolean a(ItemStack itemstack, EntityLiving entityliving, EntityLiving entityliving1) { + return false; + } + + public boolean a(ItemStack itemstack, World world, Block block, int i, int j, int k, EntityLiving entityliving) { + return false; + } + + public boolean canDestroySpecialBlock(Block block) { + return false; + } + + public boolean a(ItemStack itemstack, EntityHuman entityhuman, EntityLiving entityliving) { + return false; + } + + public Item q() { + this.i = true; + return this; + } + + public Item c(String s) { + this.name = s; + return this; + } + + public String k(ItemStack itemstack) { + String s = this.a(itemstack); + + return s == null ? "" : LocaleI18n.get(s); + } + + public String getName() { + return "item." + this.name; + } + + public String a(ItemStack itemstack) { + return "item." + this.name; + } + + public Item c(Item item) { + this.craftingResult = item; + return this; + } + + public boolean l(ItemStack itemstack) { + return true; + } + + public boolean s() { + return true; + } + + public Item t() { + return this.craftingResult; + } + + public boolean u() { + return this.craftingResult != null; + } + + public void a(ItemStack itemstack, World world, Entity entity, int i, boolean flag) {} + + public void d(ItemStack itemstack, World world, EntityHuman entityhuman) {} + + public boolean h() { + return false; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.NONE; + } + + public int d_(ItemStack itemstack) { + return 0; + } + + public void a(ItemStack itemstack, World world, EntityHuman entityhuman, int i) {} + + protected Item e(String s) { + this.d = s; + return this; + } + + public String i(ItemStack itemstack) { + return this.d; + } + + public boolean m(ItemStack itemstack) { + return this.i(itemstack) != null; + } + + public String n(ItemStack itemstack) { + return ("" + LocaleI18n.get(this.k(itemstack) + ".name")).trim(); + } + + public EnumItemRarity f(ItemStack itemstack) { + return itemstack.hasEnchantments() ? EnumItemRarity.RARE : EnumItemRarity.COMMON; + } + + public boolean e_(ItemStack itemstack) { + return this.getMaxStackSize() == 1 && this.usesDurability(); + } + + protected MovingObjectPosition a(World world, EntityHuman entityhuman, boolean flag) { + float f = 1.0F; + float f1 = entityhuman.lastPitch + (entityhuman.pitch - entityhuman.lastPitch) * f; + float f2 = entityhuman.lastYaw + (entityhuman.yaw - entityhuman.lastYaw) * f; + double d0 = entityhuman.lastX + (entityhuman.locX - entityhuman.lastX) * (double) f; + double d1 = entityhuman.lastY + (entityhuman.locY - entityhuman.lastY) * (double) f + 1.62D - (double) entityhuman.height; + double d2 = entityhuman.lastZ + (entityhuman.locZ - entityhuman.lastZ) * (double) f; + Vec3D vec3d = Vec3D.a(d0, d1, d2); + float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); + float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); + float f5 = -MathHelper.cos(-f1 * 0.017453292F); + float f6 = MathHelper.sin(-f1 * 0.017453292F); + float f7 = f4 * f5; + float f8 = f3 * f5; + double d3 = 5.0D; + Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); + + return world.rayTrace(vec3d, vec3d1, flag, !flag, false); + } + + public int c() { + return 0; + } + + public Item a(CreativeModeTab creativemodetab) { + this.a = creativemodetab; + return this; + } + + public boolean v() { + return true; + } + + public boolean a(ItemStack itemstack, ItemStack itemstack1) { + return false; + } + + public Multimap k() { + return HashMultimap.create(); + } + + protected Item f(String s) { + this.l = s; + return this; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemBoat.java b/vspigot-server/src/main/java/net/minecraft/server/ItemBoat.java new file mode 100644 index 0000000..e044b03 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemBoat.java @@ -0,0 +1,93 @@ +package net.minecraft.server; + +import java.util.List; + +public class ItemBoat extends Item { + + public ItemBoat() { + this.maxStackSize = 1; + this.a(CreativeModeTab.e); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + float f = 1.0F; + float f1 = entityhuman.lastPitch + (entityhuman.pitch - entityhuman.lastPitch) * f; + float f2 = entityhuman.lastYaw + (entityhuman.yaw - entityhuman.lastYaw) * f; + double d0 = entityhuman.lastX + (entityhuman.locX - entityhuman.lastX) * (double) f; + double d1 = entityhuman.lastY + (entityhuman.locY - entityhuman.lastY) * (double) f + 1.62D - (double) entityhuman.height; + double d2 = entityhuman.lastZ + (entityhuman.locZ - entityhuman.lastZ) * (double) f; + Vec3D vec3d = Vec3D.a(d0, d1, d2); + float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); + float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); + float f5 = -MathHelper.cos(-f1 * 0.017453292F); + float f6 = MathHelper.sin(-f1 * 0.017453292F); + float f7 = f4 * f5; + float f8 = f3 * f5; + double d3 = 5.0D; + Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); + MovingObjectPosition movingobjectposition = world.rayTrace(vec3d, vec3d1, true); + + if (movingobjectposition == null) { + return itemstack; + } else { + Vec3D vec3d2 = entityhuman.j(f); + boolean flag = false; + float f9 = 1.0F; + List list = world.getEntities(entityhuman, entityhuman.boundingBox.a(vec3d2.a * d3, vec3d2.b * d3, vec3d2.c * d3).grow((double) f9, (double) f9, (double) f9)); + + int i; + + for (i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (entity.R()) { + float f10 = entity.af(); + AxisAlignedBB axisalignedbb = entity.boundingBox.grow((double) f10, (double) f10, (double) f10); + + if (axisalignedbb.a(vec3d)) { + flag = true; + } + } + } + + if (flag) { + return itemstack; + } else { + if (movingobjectposition.type == EnumMovingObjectType.BLOCK) { + i = movingobjectposition.b; + int j = movingobjectposition.c; + int k = movingobjectposition.d; + + // CraftBukkit start - Boat placement + org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(entityhuman, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, i, j, k, movingobjectposition.face, itemstack); + + if (event.isCancelled()) { + return itemstack; + } + // CraftBukkit end + + if (world.getType(i, j, k) == Blocks.SNOW) { + --j; + } + + EntityBoat entityboat = new EntityBoat(world, (double) ((float) i + 0.5F), (double) ((float) j + 1.0F), (double) ((float) k + 0.5F)); + + entityboat.yaw = (float) (((MathHelper.floor((double) (entityhuman.yaw * 4.0F / 360.0F) + 0.5D) & 3) - 1) * 90); + if (!world.getCubes(entityboat, entityboat.boundingBox.grow(-0.1D, -0.1D, -0.1D)).isEmpty()) { + return itemstack; + } + + if (!world.isStatic) { + world.addEntity(entityboat); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + } + + return itemstack; + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemBow.java b/vspigot-server/src/main/java/net/minecraft/server/ItemBow.java new file mode 100644 index 0000000..ce4d0c8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemBow.java @@ -0,0 +1,113 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityCombustEvent; // CraftBukkit + +public class ItemBow extends Item { + + public static final String[] a = new String[] { "pulling_0", "pulling_1", "pulling_2"}; + + public ItemBow() { + this.maxStackSize = 1; + this.setMaxDurability(384); + this.a(CreativeModeTab.j); + } + + public void a(ItemStack itemstack, World world, EntityHuman entityhuman, int i) { + boolean flag = entityhuman.abilities.canInstantlyBuild || EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_INFINITE.id, itemstack) > 0; + + if (flag || entityhuman.inventory.b(Items.ARROW)) { + int j = this.d_(itemstack) - i; + float f = (float) j / 20.0F; + + f = (f * f + f * 2.0F) / 3.0F; + if ((double) f < 0.1D) { + return; + } + + if (f > 1.0F) { + f = 1.0F; + } + + EntityArrow entityarrow = new EntityArrow(world, entityhuman, f * 2.0F); + + if (f == 1.0F) { + entityarrow.setCritical(true); + } + + int k = EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_DAMAGE.id, itemstack); + + if (k > 0) { + entityarrow.b(entityarrow.e() + (double) k * 0.5D + 0.5D); + } + + int l = EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_KNOCKBACK.id, itemstack); + + if (l > 0) { + entityarrow.setKnockbackStrength(l); + } + + if (EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_FIRE.id, itemstack) > 0) { + // CraftBukkit start - call EntityCombustEvent + EntityCombustEvent event = new EntityCombustEvent(entityarrow.getBukkitEntity(), 100); + entityarrow.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + entityarrow.setOnFire(event.getDuration()); + } + // CraftBukkit end + } + + // CraftBukkit start + org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(entityhuman, itemstack, entityarrow, f); + if (event.isCancelled()) { + event.getProjectile().remove(); + return; + } + + if (event.getProjectile() == entityarrow.getBukkitEntity()) { + // Velt start + if (!world.addEntity(entityarrow)) { + return; + } + // Velt end + } + // CraftBukkit end + + itemstack.damage(1, entityhuman); + world.makeSound(entityhuman, "random.bow", 1.0F, 1.0F / (g.nextFloat() * 0.4F + 1.2F) + f * 0.5F); + if (flag) { + entityarrow.fromPlayer = 2; + } else { + entityhuman.inventory.a(Items.ARROW); + } + + if (!world.isStatic) { + // world.addEntity(entityarrow); // CraftBukkit - moved up + } + } + } + + public ItemStack b(ItemStack itemstack, World world, EntityHuman entityhuman) { + return itemstack; + } + + public int d_(ItemStack itemstack) { + return 72000; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.BOW; + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild || entityhuman.inventory.b(Items.ARROW)) { + entityhuman.a(itemstack, this.d_(itemstack)); + } + + return itemstack; + } + + public int c() { + return 1; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemBucket.java b/vspigot-server/src/main/java/net/minecraft/server/ItemBucket.java new file mode 100644 index 0000000..68d31b6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemBucket.java @@ -0,0 +1,193 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.player.PlayerBucketFillEvent; +// CraftBukkit end + +import org.github.paperspigot.PaperSpigotConfig; // PaperSpigot + +public class ItemBucket extends Item { + + private Block a; + + public ItemBucket(Block block) { + // PaperSpigot start - Stackable Buckets + if ((block == Blocks.LAVA && PaperSpigotConfig.stackableLavaBuckets) || + (block == Blocks.WATER && PaperSpigotConfig.stackableWaterBuckets)) { + this.maxStackSize = org.bukkit.Material.BUCKET.getMaxStackSize(); + } else { + this.maxStackSize = 1; + } + // PaperSpigot end + this.a = block; + this.a(CreativeModeTab.f); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + boolean flag = this.a == Blocks.AIR; + MovingObjectPosition movingobjectposition = this.a(world, entityhuman, flag); + + if (movingobjectposition == null) { + return itemstack; + } else { + if (movingobjectposition.type == EnumMovingObjectType.BLOCK) { + int i = movingobjectposition.b; + int j = movingobjectposition.c; + int k = movingobjectposition.d; + + if (!world.a(entityhuman, i, j, k)) { + return itemstack; + } + + if (flag) { + if (!entityhuman.a(i, j, k, movingobjectposition.face, itemstack)) { + return itemstack; + } + + Material material = world.getType(i, j, k).getMaterial(); + int l = world.getData(i, j, k); + + if (material == Material.WATER && l == 0) { + // CraftBukkit start + PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, i, j, k, -1, itemstack, Items.WATER_BUCKET); + + if (event.isCancelled()) { + return itemstack; + } + // CraftBukkit end + world.setAir(i, j, k); + return this.a(itemstack, entityhuman, Items.WATER_BUCKET, event.getItemStack()); // CraftBukkit - added Event stack + } + + if (material == Material.LAVA && l == 0) { + // CraftBukkit start + PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, i, j, k, -1, itemstack, Items.LAVA_BUCKET); + + if (event.isCancelled()) { + return itemstack; + } + // CraftBukkit end + world.setAir(i, j, k); + return this.a(itemstack, entityhuman, Items.LAVA_BUCKET, event.getItemStack()); // CraftBukkit - added Event stack + } + } else { + if (this.a == Blocks.AIR) { + // CraftBukkit start + PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent(entityhuman, i, j, k, movingobjectposition.face, itemstack); + + if (event.isCancelled()) { + return itemstack; + } + + return CraftItemStack.asNMSCopy(event.getItemStack()); + } + + int clickedX = i, clickedY = j, clickedZ = k; + // CraftBukkit end + + if (movingobjectposition.face == 0) { + --j; + } + + if (movingobjectposition.face == 1) { + ++j; + } + + if (movingobjectposition.face == 2) { + --k; + } + + if (movingobjectposition.face == 3) { + ++k; + } + + if (movingobjectposition.face == 4) { + --i; + } + + if (movingobjectposition.face == 5) { + ++i; + } + + if (!entityhuman.a(i, j, k, movingobjectposition.face, itemstack)) { + return itemstack; + } + + // CraftBukkit start + PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent(entityhuman, clickedX, clickedY, clickedZ, movingobjectposition.face, itemstack); + + if (event.isCancelled()) { + return itemstack; + } + // CraftBukkit end + + if (this.a(world, i, j, k) && !entityhuman.abilities.canInstantlyBuild) { + // PaperSpigot start - Stackable Buckets + if ((this == Items.LAVA_BUCKET && PaperSpigotConfig.stackableLavaBuckets) || + (this == Items.WATER_BUCKET && PaperSpigotConfig.stackableWaterBuckets)) { + --itemstack.count; + if (itemstack.count <= 0) { + return CraftItemStack.asNMSCopy(event.getItemStack()); + } + if (!entityhuman.inventory.pickup(CraftItemStack.asNMSCopy(event.getItemStack()))) { + entityhuman.drop(CraftItemStack.asNMSCopy(event.getItemStack()), false); + } + return itemstack; + } + // PaperSpigot end + return CraftItemStack.asNMSCopy(event.getItemStack()); // CraftBukkit + } + } + } + + return itemstack; + } + } + + // CraftBukkit - added ob.ItemStack result - TODO: Is this... the right way to handle this? + private ItemStack a(ItemStack itemstack, EntityHuman entityhuman, Item item, org.bukkit.inventory.ItemStack result) { + if (entityhuman.abilities.canInstantlyBuild) { + return itemstack; + } else if (--itemstack.count <= 0) { + return CraftItemStack.asNMSCopy(result); // CraftBukkit + } else { + if (!entityhuman.inventory.pickup(CraftItemStack.asNMSCopy(result))) { // CraftBukkit + entityhuman.drop(CraftItemStack.asNMSCopy(result), false); // CraftBukkit + } + + return itemstack; + } + } + + public boolean a(World world, int i, int j, int k) { + if (this.a == Blocks.AIR) { + return false; + } else { + Material material = world.getType(i, j, k).getMaterial(); + boolean flag = !material.isBuildable(); + + if (!world.isEmpty(i, j, k) && !flag) { + return false; + } else { + if (world.worldProvider.f && this.a == Blocks.WATER) { + world.makeSound((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), "random.fizz", 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); + + for (int l = 0; l < 8; ++l) { + world.addParticle("largesmoke", (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D); + } + } else { + if (!world.isStatic && flag && !material.isLiquid()) { + world.setAir(i, j, k, true); + } + + world.setTypeAndData(i, j, k, this.a, 0, 3); + } + + return true; + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemDoor.java b/vspigot-server/src/main/java/net/minecraft/server/ItemDoor.java new file mode 100644 index 0000000..f457e51 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemDoor.java @@ -0,0 +1,79 @@ +package net.minecraft.server; + +public class ItemDoor extends Item { + + private Material a; + + public ItemDoor(Material material) { + this.a = material; + this.maxStackSize = 1; + this.a(CreativeModeTab.d); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + if (l != 1) { + return false; + } else { + ++j; + Block block; + + if (this.a == Material.WOOD) { + block = Blocks.WOODEN_DOOR; + } else { + block = Blocks.IRON_DOOR_BLOCK; + } + + if (entityhuman.a(i, j, k, l, itemstack) && entityhuman.a(i, j + 1, k, l, itemstack)) { + if (!block.canPlace(world, i, j, k)) { + return false; + } else { + int i1 = MathHelper.floor((double) ((entityhuman.yaw + 180.0F) * 4.0F / 360.0F) - 0.5D) & 3; + + place(world, i, j, k, i1, block); + --itemstack.count; + return true; + } + } else { + return false; + } + } + } + + public static void place(World world, int i, int j, int k, int l, Block block) { + byte b0 = 0; + byte b1 = 0; + + if (l == 0) { + b1 = 1; + } + + if (l == 1) { + b0 = -1; + } + + if (l == 2) { + b1 = -1; + } + + if (l == 3) { + b0 = 1; + } + + int i1 = (world.getType(i - b0, j, k - b1).r() ? 1 : 0) + (world.getType(i - b0, j + 1, k - b1).r() ? 1 : 0); + int j1 = (world.getType(i + b0, j, k + b1).r() ? 1 : 0) + (world.getType(i + b0, j + 1, k + b1).r() ? 1 : 0); + boolean flag = world.getType(i - b0, j, k - b1) == block || world.getType(i - b0, j + 1, k - b1) == block; + boolean flag1 = world.getType(i + b0, j, k + b1) == block || world.getType(i + b0, j + 1, k + b1) == block; + boolean flag2 = false; + + if (flag && !flag1) { + flag2 = true; + } else if (j1 > i1) { + flag2 = true; + } + + world.setTypeAndData(i, j, k, block, l, 3); // Spigot Update - 20141001a + world.setTypeAndData(i, j + 1, k, block, 8 | (flag2 ? 1 : 0), 3); // Spigot Update - 20141001a + //world.applyPhysics(i, j, k, block); // Spigot Update - 20141001a + //world.applyPhysics(i, j + 1, k, block); // Spigot Update - 20141001a + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemDye.java b/vspigot-server/src/main/java/net/minecraft/server/ItemDye.java new file mode 100644 index 0000000..156af8e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemDye.java @@ -0,0 +1,129 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.SheepDyeWoolEvent; // CraftBukkit + +public class ItemDye extends Item { + + public static final String[] a = new String[] { "black", "red", "green", "brown", "blue", "purple", "cyan", "silver", "gray", "pink", "lime", "yellow", "lightBlue", "magenta", "orange", "white"}; + public static final String[] b = new String[] { "black", "red", "green", "brown", "blue", "purple", "cyan", "silver", "gray", "pink", "lime", "yellow", "light_blue", "magenta", "orange", "white"}; + public static final int[] c = new int[] { 1973019, 11743532, 3887386, 5320730, 2437522, 8073150, 2651799, 11250603, 4408131, 14188952, 4312372, 14602026, 6719955, 12801229, 15435844, 15790320}; + + public ItemDye() { + this.a(true); + this.setMaxDurability(0); + this.a(CreativeModeTab.l); + } + + public String a(ItemStack itemstack) { + int i = MathHelper.a(itemstack.getData(), 0, 15); + + return super.getName() + "." + a[i]; + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + if (!entityhuman.a(i, j, k, l, itemstack)) { + return false; + } else { + if (itemstack.getData() == 15) { + if (a(itemstack, world, i, j, k)) { + if (!world.isStatic) { + world.triggerEffect(2005, i, j, k, 0); + } + + return true; + } + } else if (itemstack.getData() == 3) { + Block block = world.getType(i, j, k); + int i1 = world.getData(i, j, k); + + if (block == Blocks.LOG && BlockLogAbstract.c(i1) == 3) { + if (l == 0) { + return false; + } + + if (l == 1) { + return false; + } + + if (l == 2) { + --k; + } + + if (l == 3) { + ++k; + } + + if (l == 4) { + --i; + } + + if (l == 5) { + ++i; + } + + if (world.isEmpty(i, j, k)) { + int j1 = Blocks.COCOA.getPlacedData(world, i, j, k, l, f, f1, f2, 0); + + world.setTypeAndData(i, j, k, Blocks.COCOA, j1, 2); + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + } + + return true; + } + } + + return false; + } + } + + public static boolean a(ItemStack itemstack, World world, int i, int j, int k) { + Block block = world.getType(i, j, k); + + if (block instanceof IBlockFragilePlantElement) { + IBlockFragilePlantElement iblockfragileplantelement = (IBlockFragilePlantElement) block; + + if (iblockfragileplantelement.a(world, i, j, k, world.isStatic)) { + if (!world.isStatic) { + if (iblockfragileplantelement.a(world, world.random, i, j, k)) { + iblockfragileplantelement.b(world, world.random, i, j, k); + } + + --itemstack.count; + } + + return true; + } + } + + return false; + } + + public boolean a(ItemStack itemstack, EntityHuman entityhuman, EntityLiving entityliving) { + if (entityliving instanceof EntitySheep) { + EntitySheep entitysheep = (EntitySheep) entityliving; + int i = BlockCloth.b(itemstack.getData()); + + if (!entitysheep.isSheared() && entitysheep.getColor() != i) { + // CraftBukkit start + byte bColor = (byte) i; + SheepDyeWoolEvent event = new SheepDyeWoolEvent((org.bukkit.entity.Sheep) entitysheep.getBukkitEntity(), org.bukkit.DyeColor.getByData(bColor)); + entitysheep.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + + i = (byte) event.getColor().getWoolData(); + // CraftBukkit end + entitysheep.setColor(i); + --itemstack.count; + } + + return true; + } else { + return false; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemEgg.java b/vspigot-server/src/main/java/net/minecraft/server/ItemEgg.java new file mode 100644 index 0000000..8e3ef11 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemEgg.java @@ -0,0 +1,23 @@ +package net.minecraft.server; + +public class ItemEgg extends Item { + + public ItemEgg() { + this.maxStackSize = 16; + this.a(CreativeModeTab.l); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + // Velt start + if (!world.isStatic && world.addEntity(new EntityEgg(world, entityhuman))) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + world.makeSound(entityhuman, "random.bow", 0.5f, 0.4f / (ItemEgg.g.nextFloat() * 0.4f + 0.8f)); + } + // Velt end + + return itemstack; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemEnchantedBook.java b/vspigot-server/src/main/java/net/minecraft/server/ItemEnchantedBook.java new file mode 100644 index 0000000..0829e4c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemEnchantedBook.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +import java.util.Random; + +public class ItemEnchantedBook extends Item { + + public ItemEnchantedBook() {} + + public boolean e_(ItemStack itemStack) { + return false; + } + + public EnumItemRarity f(ItemStack itemstack) { + if (this.g(itemstack).size() > 0) { + return EnumItemRarity.UNCOMMON; + } + return super.f(itemstack); + } + + public NBTTagList g(ItemStack itemStack) { + if (itemStack.tag == null || !itemStack.tag.hasKeyOfType("StoredEnchantments", 9)) { + return new NBTTagList(); + } + return (NBTTagList)itemStack.tag.get("StoredEnchantments"); + } + + public void a(ItemStack itemStack, EnchantmentInstance enchantmentInstance) { + NBTTagList g = this.g(itemStack); + boolean b = true; + for (int i = 0; i < g.size(); ++i) { + NBTTagCompound value = g.get(i); + if (value.getShort("id") == enchantmentInstance.enchantment.id) { + if (value.getShort("lvl") < enchantmentInstance.level) { + value.setShort("lvl", (short)enchantmentInstance.level); + } + + b = false; + break; + } + } + + if (b) { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + nbtTagCompound.setShort("id", (short)enchantmentInstance.enchantment.id); + nbtTagCompound.setShort("lvl", (short)enchantmentInstance.level); + g.add(nbtTagCompound); + } + + if (!itemStack.hasTag()) { + itemStack.setTag(new NBTTagCompound()); + } + + itemStack.getTag().set("StoredEnchantments", g); + } + + public ItemStack a(EnchantmentInstance enchantmentInstance) { + ItemStack itemStack = new ItemStack(this); + this.a(itemStack, enchantmentInstance); + return itemStack; + } + + public StructurePieceTreasure b(Random random) { + return this.a(random, 1, 1, 1); + } + + public StructurePieceTreasure a(final Random random, final int n, final int n2, final int n3) { + ItemStack itemstack = new ItemStack(Items.BOOK, 1, 0); + + EnchantmentManager.a(random, itemstack, 30); + return new StructurePieceTreasure(itemstack, n, n2, n3); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemEnderEye.java b/vspigot-server/src/main/java/net/minecraft/server/ItemEnderEye.java new file mode 100644 index 0000000..addfda0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemEnderEye.java @@ -0,0 +1,138 @@ +package net.minecraft.server; + +public class ItemEnderEye extends Item { + + public ItemEnderEye() { + this.a(CreativeModeTab.f); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + Block block = world.getType(i, j, k); + int i1 = world.getData(i, j, k); + + if (entityhuman.a(i, j, k, l, itemstack) && block == Blocks.ENDER_PORTAL_FRAME && !BlockEnderPortalFrame.b(i1)) { + if (world.isStatic) { + return true; + } else { + world.setData(i, j, k, i1 + 4, 2); + world.updateAdjacentComparators(i, j, k, Blocks.ENDER_PORTAL_FRAME); + --itemstack.count; + + int j1; + + for (j1 = 0; j1 < 16; ++j1) { + double d0 = (double) ((float) i + (5.0F + g.nextFloat() * 6.0F) / 16.0F); + double d1 = (double) ((float) j + 0.8125F); + double d2 = (double) ((float) k + (5.0F + g.nextFloat() * 6.0F) / 16.0F); + double d3 = 0.0D; + double d4 = 0.0D; + double d5 = 0.0D; + + world.addParticle("smoke", d0, d1, d2, d3, d4, d5); + } + + for (j1 = 0; j1 < 4; j1++) { // MineHQ - Light end portal in any direction + int k1 = 0; + int l1 = 0; + boolean flag = false; + boolean flag1 = true; + int i2 = Direction.g[j1]; + + int j2; + int k2; + int l2; + + for (l2 = -2; l2 <= 2; ++l2) { + j2 = i + Direction.a[i2] * l2; + k2 = k + Direction.b[i2] * l2; + if (world.getType(j2, j, k2) == Blocks.ENDER_PORTAL_FRAME) { + if (!BlockEnderPortalFrame.b(world.getData(j2, j, k2))) { + flag1 = false; + break; + } + + l1 = l2; + if (!flag) { + k1 = l2; + flag = true; + } + } + } + + if (flag1 && l1 == k1 + 2) { + for (l2 = k1; l2 <= l1; ++l2) { + j2 = i + Direction.a[i2] * l2; + k2 = k + Direction.b[i2] * l2; + j2 += Direction.a[j1] * 4; + k2 += Direction.b[j1] * 4; + if (world.getType(j2, j, k2) != Blocks.ENDER_PORTAL_FRAME || !BlockEnderPortalFrame.b(world.getData(j2, j, k2))) { + flag1 = false; + break; + } + } + + int i3; + + for (l2 = k1 - 1; l2 <= l1 + 1; l2 += 4) { + for (j2 = 1; j2 <= 3; ++j2) { + k2 = i + Direction.a[i2] * l2; + i3 = k + Direction.b[i2] * l2; + k2 += Direction.a[j1] * j2; + i3 += Direction.b[j1] * j2; + if (world.getType(k2, j, i3) != Blocks.ENDER_PORTAL_FRAME || !BlockEnderPortalFrame.b(world.getData(k2, j, i3))) { + flag1 = false; + break; + } + } + } + + if (flag1) { + for (l2 = k1; l2 <= l1; ++l2) { + for (j2 = 1; j2 <= 3; ++j2) { + k2 = i + Direction.a[i2] * l2; + i3 = k + Direction.b[i2] * l2; + k2 += Direction.a[j1] * j2; + i3 += Direction.b[j1] * j2; + world.setTypeAndData(k2, j, i3, Blocks.ENDER_PORTAL, 0, 2); + } + } + } + } + } // MineHQ + + return true; + } + } else { + return false; + } + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + MovingObjectPosition movingobjectposition = this.a(world, entityhuman, false); + + if (movingobjectposition != null && movingobjectposition.type == EnumMovingObjectType.BLOCK && world.getType(movingobjectposition.b, movingobjectposition.c, movingobjectposition.d) == Blocks.ENDER_PORTAL_FRAME) { + return itemstack; + } else { + if (!world.isStatic) { + ChunkPosition chunkposition = world.b("Stronghold", (int) entityhuman.locX, (int) entityhuman.locY, (int) entityhuman.locZ); + + if (chunkposition != null) { + EntityEnderSignal entityendersignal = new EntityEnderSignal(world, entityhuman.locX, entityhuman.locY + 1.62D - (double) entityhuman.height, entityhuman.locZ); + + entityendersignal.a((double) chunkposition.x, chunkposition.y, (double) chunkposition.z); + // Velt start + if (world.addEntity(entityendersignal)) { + world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (g.nextFloat() * 0.4F + 0.8F)); + world.a((EntityHuman) null, 1002, (int) entityhuman.locX, (int) entityhuman.locY, (int) entityhuman.locZ, 0); + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + } + // Velt end + } + } + + return itemstack; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemEnderPearl.java b/vspigot-server/src/main/java/net/minecraft/server/ItemEnderPearl.java new file mode 100644 index 0000000..a71f1e8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemEnderPearl.java @@ -0,0 +1,25 @@ +package net.minecraft.server; + +public class ItemEnderPearl extends Item { + + public ItemEnderPearl() { + this.maxStackSize = 16; + this.a(CreativeModeTab.f); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild) { + return itemstack; + } else { + + // Velt start + if (!world.isStatic && world.addEntity(new EntityEnderPearl(world, entityhuman))) { + --itemstack.count; + world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (g.nextFloat() * 0.4F + 0.8F)); + } + // Velt end + + return itemstack; + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemExpBottle.java b/vspigot-server/src/main/java/net/minecraft/server/ItemExpBottle.java new file mode 100644 index 0000000..07abd69 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemExpBottle.java @@ -0,0 +1,23 @@ +package net.minecraft.server; + +public class ItemExpBottle extends Item { + + public ItemExpBottle() { + this.a(CreativeModeTab.f); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + + // Velt start + if (!world.isStatic && world.addEntity(new EntityThrownExpBottle(world, entityhuman))) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (g.nextFloat() * 0.4F + 0.8F)); + } + // Velt end + + return itemstack; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemFireball.java b/vspigot-server/src/main/java/net/minecraft/server/ItemFireball.java new file mode 100644 index 0000000..0a3bf13 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemFireball.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + +public class ItemFireball extends Item { + + public ItemFireball() { + this.a(CreativeModeTab.f); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + if (world.isStatic) { + return true; + } else { + if (l == 0) { + --j; + } + + if (l == 1) { + ++j; + } + + if (l == 2) { + --k; + } + + if (l == 3) { + ++k; + } + + if (l == 4) { + --i; + } + + if (l == 5) { + ++i; + } + + if (!entityhuman.a(i, j, k, l, itemstack)) { + return false; + } else { + if (world.getType(i, j, k).getMaterial() == Material.AIR) { + // CraftBukkit start - fire BlockIgniteEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, i, j, k, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, entityhuman).isCancelled()) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + return false; + } + // CraftBukkit end + + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "fire.ignite", 1.0F, g.nextFloat() * 0.4F + 0.8F); + world.setTypeUpdate(i, j, k, Blocks.FIRE); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + return true; + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemFireworks.java b/vspigot-server/src/main/java/net/minecraft/server/ItemFireworks.java new file mode 100644 index 0000000..ad61d22 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemFireworks.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class ItemFireworks extends Item { + + public ItemFireworks() {} + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + if (!world.isStatic) { + EntityFireworks entityfireworks = new EntityFireworks(world, (double) ((float) i + f), (double) ((float) j + f1), (double) ((float) k + f2), itemstack); + + world.addEntity(entityfireworks); + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemFishingRod.java b/vspigot-server/src/main/java/net/minecraft/server/ItemFishingRod.java new file mode 100644 index 0000000..ec13c93 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemFishingRod.java @@ -0,0 +1,50 @@ +package net.minecraft.server; + +import org.bukkit.event.player.PlayerFishEvent; // CraftBukkit + +public class ItemFishingRod extends Item { + + public ItemFishingRod() { + this.setMaxDurability(64); + this.e(1); + this.a(CreativeModeTab.i); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (entityhuman.hookedFish != null) { + int i = entityhuman.hookedFish.e(); + + itemstack.damage(i, entityhuman); + entityhuman.ba(); + } else { + // CraftBukkit start + EntityFishingHook hook = new EntityFishingHook(world, entityhuman); + PlayerFishEvent playerFishEvent = new PlayerFishEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), null, (org.bukkit.entity.Fish) hook.getBukkitEntity(), PlayerFishEvent.State.FISHING); + world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + entityhuman.hookedFish = null; + return itemstack; + } + // CraftBukkit end + + // Velt start + if (!world.isStatic && world.addEntity(hook)) { + world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (g.nextFloat() * 0.4F + 0.8F)); + } + // Velt end + + entityhuman.ba(); + } + + return itemstack; + } + + public boolean e_(ItemStack itemstack) { + return super.e_(itemstack); + } + + public int c() { + return 1; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemFlintAndSteel.java b/vspigot-server/src/main/java/net/minecraft/server/ItemFlintAndSteel.java new file mode 100644 index 0000000..ec2edea --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemFlintAndSteel.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + +public class ItemFlintAndSteel extends Item { + + public ItemFlintAndSteel() { + this.maxStackSize = 1; + this.setMaxDurability(64); + this.a(CreativeModeTab.i); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + int clickedX = i, clickedY = j, clickedZ = k; // CraftBukkit + if (l == 0) { + --j; + } + + if (l == 1) { + ++j; + } + + if (l == 2) { + --k; + } + + if (l == 3) { + ++k; + } + + if (l == 4) { + --i; + } + + if (l == 5) { + ++i; + } + + if (!entityhuman.a(i, j, k, l, itemstack)) { + return false; + } else { + if (world.getType(i, j, k).getMaterial() == Material.AIR) { + // CraftBukkit start - Store the clicked block + if (CraftEventFactory.callBlockIgniteEvent(world, i, j, k, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, entityhuman).isCancelled()) { + itemstack.damage(1, entityhuman); + return false; + } + + CraftBlockState blockState = CraftBlockState.getBlockState(world, i, j, k); + // CraftBukkit end + + world.makeSound((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D, "fire.ignite", 1.0F, g.nextFloat() * 0.4F + 0.8F); + world.setTypeUpdate(i, j, k, Blocks.FIRE); + + // CraftBukkit start + org.bukkit.event.block.BlockPlaceEvent placeEvent = CraftEventFactory.callBlockPlaceEvent(world, entityhuman, blockState, clickedX, clickedY, clickedZ); + + if (placeEvent.isCancelled() || !placeEvent.canBuild()) { + placeEvent.getBlockPlaced().setTypeIdAndData(0, (byte) 0, false); + return false; + } + // CraftBukkit end + } + + itemstack.damage(1, entityhuman); + return true; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemHanging.java b/vspigot-server/src/main/java/net/minecraft/server/ItemHanging.java new file mode 100644 index 0000000..d88d36a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemHanging.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.event.hanging.HangingPlaceEvent; +import org.bukkit.event.painting.PaintingPlaceEvent; +// CraftBukkit end + +public class ItemHanging extends Item { + + private final Class a; + + public ItemHanging(Class oclass) { + this.a = oclass; + this.a(CreativeModeTab.c); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + if (l == 0) { + return false; + } else if (l == 1) { + return false; + } else { + int i1 = Direction.e[l]; + EntityHanging entityhanging = this.a(world, i, j, k, i1); + + if (!entityhuman.a(i, j, k, l, itemstack)) { + return false; + } else { + if (entityhanging != null && entityhanging.survives()) { + if (!world.isStatic) { + // CraftBukkit start - fire HangingPlaceEvent + Player who = (entityhuman == null) ? null : (Player) entityhuman.getBukkitEntity(); + org.bukkit.block.Block blockClicked = world.getWorld().getBlockAt(i, j, k); + org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(l); + + HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityhanging.getBukkitEntity(), who, blockClicked, blockFace); + world.getServer().getPluginManager().callEvent(event); + + PaintingPlaceEvent paintingEvent = null; + if (entityhanging instanceof EntityPainting) { + // Fire old painting event until it can be removed + paintingEvent = new PaintingPlaceEvent((org.bukkit.entity.Painting) entityhanging.getBukkitEntity(), who, blockClicked, blockFace); + paintingEvent.setCancelled(event.isCancelled()); + world.getServer().getPluginManager().callEvent(paintingEvent); + } + + if (event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { + return false; + } + // CraftBukkit end + + world.addEntity(entityhanging); + } + + --itemstack.count; + } + + return true; + } + } + } + + private EntityHanging a(World world, int i, int j, int k, int l) { + return (EntityHanging) (this.a == EntityPainting.class ? new EntityPainting(world, i, j, k, l) : (this.a == EntityItemFrame.class ? new EntityItemFrame(world, i, j, k, l) : null)); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemLeash.java b/vspigot-server/src/main/java/net/minecraft/server/ItemLeash.java new file mode 100644 index 0000000..8655b9f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemLeash.java @@ -0,0 +1,70 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +import org.bukkit.event.hanging.HangingPlaceEvent; // CraftBukkit + +public class ItemLeash extends Item { + + public ItemLeash() { + this.a(CreativeModeTab.i); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + Block block = world.getType(i, j, k); + + if (block.b() == 11) { + if (world.isStatic) { + return true; + } else { + a(entityhuman, world, i, j, k); + return true; + } + } else { + return false; + } + } + + public static boolean a(EntityHuman entityhuman, World world, int i, int j, int k) { + EntityLeash entityleash = EntityLeash.b(world, i, j, k); + boolean flag = false; + double d0 = 7.0D; + List list = world.a(EntityInsentient.class, AxisAlignedBB.a((double) i - d0, (double) j - d0, (double) k - d0, (double) i + d0, (double) j + d0, (double) k + d0)); + + if (list != null) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityInsentient entityinsentient = (EntityInsentient) iterator.next(); + + if (entityinsentient.bN() && entityinsentient.getLeashHolder() == entityhuman) { + if (entityleash == null) { + entityleash = EntityLeash.a(world, i, j, k); + + // CraftBukkit start - fire HangingPlaceEvent + HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityleash.getBukkitEntity(), entityhuman != null ? (org.bukkit.entity.Player) entityhuman.getBukkitEntity() : null, world.getWorld().getBlockAt(i, j, k), org.bukkit.block.BlockFace.SELF); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + entityleash.die(); + return false; + } + // CraftBukkit end + } + + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(entityinsentient, entityleash, entityhuman).isCancelled()) { + continue; + } + // CraftBukkit end + + entityinsentient.setLeashHolder(entityleash, true); + flag = true; + } + } + } + + return flag; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemMapEmpty.java b/vspigot-server/src/main/java/net/minecraft/server/ItemMapEmpty.java new file mode 100644 index 0000000..8998a3f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemMapEmpty.java @@ -0,0 +1,37 @@ +package net.minecraft.server; + +public class ItemMapEmpty extends ItemWorldMapBase { + + protected ItemMapEmpty() { + this.a(CreativeModeTab.f); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world + ItemStack itemstack1 = new ItemStack(Items.MAP, 1, worldMain.b("map")); // CraftBukkit - use primary world for maps + String s = "map_" + itemstack1.getData(); + WorldMap worldmap = new WorldMap(s); + + worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit - use primary world for maps + worldmap.scale = 0; + int i = 128 * (1 << worldmap.scale); + + worldmap.centerX = (int) (Math.round(entityhuman.locX / (double) i) * (long) i); + worldmap.centerZ = (int) (Math.round(entityhuman.locZ / (double) i) * (long) i); + worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - use bukkit dimension + worldmap.c(); + + org.bukkit.craftbukkit.event.CraftEventFactory.callEvent(new org.bukkit.event.server.MapInitializeEvent(worldmap.mapView)); // CraftBukkit + + --itemstack.count; + if (itemstack.count <= 0) { + return itemstack1; + } else { + if (!entityhuman.inventory.pickup(itemstack1.cloneItemStack())) { + entityhuman.drop(itemstack1, false); + } + + return itemstack; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemMilkBucket.java b/vspigot-server/src/main/java/net/minecraft/server/ItemMilkBucket.java new file mode 100644 index 0000000..862768e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemMilkBucket.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +import org.github.paperspigot.PaperSpigotConfig; // PaperSpigot + +public class ItemMilkBucket extends Item { + + public ItemMilkBucket() { + // PaperSpigot start - Stackable Buckets + if (PaperSpigotConfig.stackableMilkBuckets) { + this.e(org.bukkit.Material.BUCKET.getMaxStackSize()); + } else { + this.e(1); + } + // PaperSpigot end + this.a(CreativeModeTab.f); + } + + public ItemStack b(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + if (!world.isStatic) { + entityhuman.removeAllEffects(); + } + + // PaperSpigot start - Stackable Buckets + if (PaperSpigotConfig.stackableMilkBuckets) { + if (itemstack.count <= 0) { + return new ItemStack(Items.BUCKET); + } else if (!entityhuman.inventory.pickup(new ItemStack(Items.BUCKET))) { + entityhuman.drop(new ItemStack(Items.BUCKET), false); + } + } + // PaperSpigot end + + return itemstack.count <= 0 ? new ItemStack(Items.BUCKET) : itemstack; + } + + public int d_(ItemStack itemstack) { + return 32; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.DRINK; + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + entityhuman.a(itemstack, this.d_(itemstack)); + return itemstack; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemMinecart.java b/vspigot-server/src/main/java/net/minecraft/server/ItemMinecart.java new file mode 100644 index 0000000..02a1fd8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemMinecart.java @@ -0,0 +1,40 @@ +package net.minecraft.server; + +public class ItemMinecart extends Item { + + private static final IDispenseBehavior b = new DispenseBehaviorMinecart(); + public int a; + + public ItemMinecart(int i) { + this.maxStackSize = 1; + this.a = i; + this.a(CreativeModeTab.e); + BlockDispenser.a.a(this, b); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + if (BlockMinecartTrackAbstract.a(world.getType(i, j, k))) { + if (!world.isStatic) { + // CraftBukkit start - Minecarts + org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(entityhuman, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, i, j, k, l, itemstack); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), this.a); + + if (itemstack.hasName()) { + entityminecartabstract.a(itemstack.getName()); + } + + world.addEntity(entityminecartabstract); + } + + --itemstack.count; + return true; + } else { + return false; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemMonsterEgg.java b/vspigot-server/src/main/java/net/minecraft/server/ItemMonsterEgg.java new file mode 100644 index 0000000..639ddf5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemMonsterEgg.java @@ -0,0 +1,124 @@ +package net.minecraft.server; + +public class ItemMonsterEgg extends Item { + + public ItemMonsterEgg() { + this.a(true); + this.a(CreativeModeTab.f); + } + + public String n(ItemStack itemstack) { + String s = ("" + LocaleI18n.get(this.getName() + ".name")).trim(); + String s1 = EntityTypes.b(itemstack.getData()); + + if (s1 != null) { + s = s + " " + LocaleI18n.get("entity." + s1 + ".name"); + } + + return s; + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + // CraftBukkit - check ItemStack data + if (world.isStatic || itemstack.getData() == 48 || itemstack.getData() == 49 || itemstack.getData() == 63 || itemstack.getData() == 64) { + return true; + } else { + Block block = world.getType(i, j, k); + + i += Facing.b[l]; + j += Facing.c[l]; + k += Facing.d[l]; + double d0 = 0.0D; + + if (l == 1 && block.b() == 11) { + d0 = 0.5D; + } + + Entity entity = a(world, itemstack.getData(), (double) i + 0.5D, (double) j + d0, (double) k + 0.5D); + + if (entity != null) { + if (entity instanceof EntityLiving && itemstack.hasName()) { + ((EntityInsentient) entity).setCustomName(itemstack.getName()); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + } + + return true; + } + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (world.isStatic) { + return itemstack; + } else { + MovingObjectPosition movingobjectposition = this.a(world, entityhuman, true); + + if (movingobjectposition == null) { + return itemstack; + } else { + if (movingobjectposition.type == EnumMovingObjectType.BLOCK) { + int i = movingobjectposition.b; + int j = movingobjectposition.c; + int k = movingobjectposition.d; + + if (!world.a(entityhuman, i, j, k)) { + return itemstack; + } + + if (!entityhuman.a(i, j, k, movingobjectposition.face, itemstack)) { + return itemstack; + } + + if (world.getType(i, j, k) instanceof BlockFluids) { + Entity entity = a(world, itemstack.getData(), (double) i, (double) j, (double) k); + + if (entity != null) { + if (entity instanceof EntityLiving && itemstack.hasName()) { + ((EntityInsentient) entity).setCustomName(itemstack.getName()); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + } + } + } + + return itemstack; + } + } + } + + public static Entity a(World world, int i, double d0, double d1, double d2) { + // CraftBukkit start - delegate to spawnCreature + return spawnCreature(world, i, d0, d1, d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); + } + + public static Entity spawnCreature(World world, int i, double d0, double d1, double d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { + // CraftBukkit end + if (!EntityTypes.eggInfo.containsKey(Integer.valueOf(i))) { + return null; + } else { + Entity entity = null; + + for (int j = 0; j < 1; ++j) { + entity = EntityTypes.a(i, world); + if (entity != null && entity instanceof EntityLiving) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + + entity.setPositionRotation(d0, d1, d2, MathHelper.g(world.random.nextFloat() * 360.0F), 0.0F); + entityinsentient.aO = entityinsentient.yaw; + entityinsentient.aM = entityinsentient.yaw; + entityinsentient.prepare((GroupDataEntity) null); + world.addEntity(entity, spawnReason); // CraftBukkit + entityinsentient.r(); + } + } + + return entity; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemPotion.java b/vspigot-server/src/main/java/net/minecraft/server/ItemPotion.java new file mode 100644 index 0000000..2777f84 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemPotion.java @@ -0,0 +1,148 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class ItemPotion extends Item { + + private HashMap a = new HashMap(); + private static final Map b = new LinkedHashMap(); + + public ItemPotion() { + this.e(1); + this.a(true); + this.setMaxDurability(0); + this.a(CreativeModeTab.k); + } + + public List g(ItemStack itemstack) { + if (itemstack.hasTag() && itemstack.getTag().hasKeyOfType("CustomPotionEffects", 9)) { + ArrayList arraylist = new ArrayList(); + NBTTagList nbttaglist = itemstack.getTag().getList("CustomPotionEffects", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.get(i); + MobEffect mobeffect = MobEffect.b(nbttagcompound); + + if (mobeffect != null) { + arraylist.add(mobeffect); + } + } + + return arraylist; + } else { + List list = (List) this.a.get(Integer.valueOf(itemstack.getData())); + + if (list == null) { + list = PotionBrewer.getEffects(itemstack.getData(), false); + this.a.put(Integer.valueOf(itemstack.getData()), list); + } + + return list; + } + } + + public List c(int i) { + List list = (List) this.a.get(Integer.valueOf(i)); + + if (list == null) { + list = PotionBrewer.getEffects(i, false); + this.a.put(Integer.valueOf(i), list); + } + + return list; + } + + public ItemStack b(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + if (!world.isStatic) { + List list = this.g(itemstack); + + if (list != null) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityhuman.addEffect(new MobEffect(mobeffect)); + } + } + } + + if (!entityhuman.abilities.canInstantlyBuild) { + if (itemstack.count <= 0) { + return new ItemStack(Items.GLASS_BOTTLE); + } + + entityhuman.inventory.pickup(new ItemStack(Items.GLASS_BOTTLE)); + } + + return itemstack; + } + + public int d_(ItemStack itemstack) { + return 32; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.DRINK; + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (g(itemstack.getData())) { + // Velt start + if (!world.isStatic && world.addEntity(new EntityPotion(world, entityhuman, itemstack))) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (g.nextFloat() * 0.4F + 0.8F)); + } + // Velt end + + return itemstack; + } else { + entityhuman.a(itemstack, this.d_(itemstack)); + return itemstack; + } + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + return false; + } + + public static boolean g(int i) { + return (i & 16384) != 0; + } + + public String n(ItemStack itemstack) { + if (itemstack.getData() == 0) { + return LocaleI18n.get("item.emptyPotion.name").trim(); + } else { + String s = ""; + + if (g(itemstack.getData())) { + s = LocaleI18n.get("potion.prefix.grenade").trim() + " "; + } + + List list = Items.POTION.g(itemstack); + String s1; + + if (list != null && !list.isEmpty()) { + s1 = ((MobEffect) list.get(0)).f(); + s1 = s1 + ".postfix"; + return s + LocaleI18n.get(s1).trim(); + } else { + s1 = PotionBrewer.c(itemstack.getData()); + return LocaleI18n.get(s1).trim() + " " + super.n(itemstack); + } + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemSkull.java b/vspigot-server/src/main/java/net/minecraft/server/ItemSkull.java new file mode 100644 index 0000000..3deacc6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemSkull.java @@ -0,0 +1,117 @@ +package net.minecraft.server; + +import java.util.UUID; + +import net.minecraft.util.com.mojang.authlib.GameProfile; + +public class ItemSkull extends Item { + + private static final String[] b = new String[] { "skeleton", "wither", "zombie", "char", "creeper"}; + public static final String[] a = new String[] { "skeleton", "wither", "zombie", "steve", "creeper"}; + + public ItemSkull() { + this.a(CreativeModeTab.c); + this.setMaxDurability(0); + this.a(true); + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + if (l == 0) { + return false; + } else if (!world.getType(i, j, k).getMaterial().isBuildable()) { + return false; + } else { + if (l == 1) { + ++j; + } + + if (l == 2) { + --k; + } + + if (l == 3) { + ++k; + } + + if (l == 4) { + --i; + } + + if (l == 5) { + ++i; + } + + if (!world.isStatic) { + // Spigot Start + if ( !Blocks.SKULL.canPlace( world, i, j, k ) ) + { + return false; + } + // Spigot End + world.setTypeAndData(i, j, k, Blocks.SKULL, l, 2); + int i1 = 0; + + if (l == 1) { + i1 = MathHelper.floor((double) (entityhuman.yaw * 16.0F / 360.0F) + 0.5D) & 15; + } + + TileEntity tileentity = world.getTileEntity(i, j, k); + + if (tileentity != null && tileentity instanceof TileEntitySkull) { + if (itemstack.getData() == 3) { + GameProfile gameprofile = null; + + if (itemstack.hasTag()) { + NBTTagCompound nbttagcompound = itemstack.getTag(); + + if (nbttagcompound.hasKeyOfType("SkullOwner", 10)) { + gameprofile = GameProfileSerializer.deserialize(nbttagcompound.getCompound("SkullOwner")); + } else if (nbttagcompound.hasKeyOfType("SkullOwner", 8) && nbttagcompound.getString("SkullOwner").length() > 0) { + gameprofile = new GameProfile((UUID) null, nbttagcompound.getString("SkullOwner")); + } + } + + ((TileEntitySkull) tileentity).setGameProfile(gameprofile); + } else { + ((TileEntitySkull) tileentity).setSkullType(itemstack.getData()); + } + + ((TileEntitySkull) tileentity).setRotation(i1); + ((BlockSkull) Blocks.SKULL).a(world, i, j, k, (TileEntitySkull) tileentity); + } + + --itemstack.count; + } + + return true; + } + } + + public int filterData(int i) { + return i; + } + + public String a(ItemStack itemstack) { + int i = itemstack.getData(); + + if (i < 0 || i >= b.length) { + i = 0; + } + + return super.getName() + "." + b[i]; + } + + public String n(ItemStack itemstack) { + if (itemstack.getData() == 3 && itemstack.hasTag()) { + if (itemstack.getTag().hasKeyOfType("SkullOwner", 10)) { + return LocaleI18n.get("item.skull.player.name", new Object[] { GameProfileSerializer.deserialize(itemstack.getTag().getCompound("SkullOwner")).getName()}); + } + + if (itemstack.getTag().hasKeyOfType("SkullOwner", 8)) { + return LocaleI18n.get("item.skull.player.name", new Object[] { itemstack.getTag().getString("SkullOwner")}); + } + } + + return super.n(itemstack); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemSnowball.java b/vspigot-server/src/main/java/net/minecraft/server/ItemSnowball.java new file mode 100644 index 0000000..4ad6df2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemSnowball.java @@ -0,0 +1,23 @@ +package net.minecraft.server; + +public class ItemSnowball extends Item { + + public ItemSnowball() { + this.maxStackSize = 16; + this.a(CreativeModeTab.f); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + // Velt start + if (!world.isStatic && world.addEntity(new EntitySnowball(world, entityhuman))) { + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + + world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (g.nextFloat() * 0.4F + 0.8F)); + } + // Velt end + + return itemstack; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemStack.java b/vspigot-server/src/main/java/net/minecraft/server/ItemStack.java new file mode 100644 index 0000000..dba9112 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemStack.java @@ -0,0 +1,684 @@ +package net.minecraft.server; + +import java.text.DecimalFormat; +import java.util.Random; + +import net.minecraft.util.com.google.common.collect.HashMultimap; +import net.minecraft.util.com.google.common.collect.Multimap; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.TreeType; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Player; +import org.bukkit.event.world.StructureGrowEvent; +// CraftBukkit end + +import net.valorhcf.ThreadingManager; // Poweruser + +public final class ItemStack { + + public static final DecimalFormat a = new DecimalFormat("#.###"); + public int count; + public int c; + private Item item; + public NBTTagCompound tag; + private int damage; + private EntityItemFrame g; + + public ItemStack(Block block) { + this(block, 1); + } + + public ItemStack(Block block, int i) { + this(block, i, 0); + } + + public ItemStack(Block block, int i, int j) { + this(Item.getItemOf(block), i, j); + } + + public ItemStack(Item item) { + this(item, 1); + } + + public ItemStack(Item item, int i) { + this(item, i, 0); + } + + public ItemStack(Item item, int i, int j) { + this.item = item; + this.count = i; + // CraftBukkit start - Pass to setData to do filtering + this.setData(j); + //this.damage = j; + //if (this.damage < 0) { + // this.damage = 0; + //} + // CraftBukkit end + } + + public static ItemStack createStack(NBTTagCompound nbttagcompound) { + ItemStack itemstack = new ItemStack(); + + itemstack.c(nbttagcompound); + if (itemstack.count < 0) itemstack.count = 0; // EMC + return itemstack.getItem() != null ? itemstack : null; + } + + private ItemStack() {} + + public ItemStack a(int i) { + ItemStack itemstack = new ItemStack(this.item, i, this.damage); + + if (this.tag != null) { + itemstack.tag = (NBTTagCompound) this.tag.clone(); + } + + this.count -= i; + return itemstack; + } + + public Item getItem() { + return this.item; + } + + public boolean placeItem(EntityHuman entityhuman, World world, int i, int j, int k, int l, float f, float f1, float f2) { + // CraftBukkit start - handle all block place event logic here + int data = this.getData(); + int count = this.count; + + if (!(this.getItem() instanceof ItemBucket)) { // if not bucket + world.captureBlockStates = true; + // special case bonemeal + if (this.getItem() instanceof ItemDye && this.getData() == 15) { + Block block = world.getType(i, j, k); + if (block == Blocks.SAPLING || block instanceof BlockMushroom) { + world.captureTreeGeneration = true; + } + } + } + boolean flag = this.getItem().interactWith(this, entityhuman, world, i, j, k, l, f, f1, f2); + int newData = this.getData(); + int newCount = this.count; + this.count = count; + this.setData(data); + world.captureBlockStates = false; + if (flag && world.captureTreeGeneration && world.capturedBlockStates.size() > 0) { + world.captureTreeGeneration = false; + Location location = new Location(world.getWorld(), i, j, k); + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; + List blocks = (List) world.capturedBlockStates.clone(); + world.capturedBlockStates.clear(); + StructureGrowEvent event = null; + if (treeType != null) { + event = new StructureGrowEvent(location, treeType, false, (Player) entityhuman.getBukkitEntity(), blocks); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + } + if (event == null || !event.isCancelled()) { + // Change the stack to its new contents if it hasn't been tampered with. + if (this.count == count && this.getData() == data) { + this.setData(newData); + this.count = newCount; + } + for (BlockState blockstate : blocks) { + blockstate.update(true); + } + } + + return flag; + } + world.captureTreeGeneration = false; + + if (flag) { + org.bukkit.event.block.BlockPlaceEvent placeEvent = null; + List blocks = (List) world.capturedBlockStates.clone(); + world.capturedBlockStates.clear(); + if (blocks.size() > 1) { + placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(world, entityhuman, blocks, i, j, k); + } else if (blocks.size() == 1) { + placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, blocks.get(0), i, j, k); + } + + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { + flag = false; // cancel placement + // revert back all captured blocks + for (BlockState blockstate : blocks) { + blockstate.update(true, false); + } + } else { + // Change the stack to its new contents if it hasn't been tampered with. + if (this.count == count && this.getData() == data) { + this.setData(newData); + this.count = newCount; + } + for (BlockState blockstate : blocks) { + int x = blockstate.getX(); + int y = blockstate.getY(); + int z = blockstate.getZ(); + int updateFlag = ((CraftBlockState) blockstate).getFlag(); + org.bukkit.Material mat = blockstate.getType(); + Block oldBlock = CraftMagicNumbers.getBlock(mat); + Block block = world.getType(x, y, z); + + if (block != null && !(block instanceof BlockContainer)) { // Containers get placed automatically + block.onPlace(world, x, y, z); + } + + world.notifyAndUpdatePhysics(x, y, z, null, oldBlock, block, updateFlag); // send null chunk as chunk.k() returns false by this point + } + entityhuman.a(StatisticList.USE_ITEM_COUNT[Item.getId(this.item)], 1); + } + } + world.capturedBlockStates.clear(); + // CraftBukkit end + + return flag; + } + + public float a(Block block) { + return this.getItem().getDestroySpeed(this, block); + } + + public ItemStack a(World world, EntityHuman entityhuman) { + return this.getItem().a(this, world, entityhuman); + } + + public ItemStack b(World world, EntityHuman entityhuman) { + return this.getItem().b(this, world, entityhuman); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("id", (short) Item.getId(this.item)); + nbttagcompound.setByte("Count", (byte) this.count); + nbttagcompound.setShort("Damage", (short) this.damage); + if (this.tag != null) { + nbttagcompound.set("tag", this.tag.clone()); // CraftBukkit - make defensive copy, data is going to another thread + } + + return nbttagcompound; + } + + public void c(NBTTagCompound nbttagcompound) { + this.item = Item.getById(nbttagcompound.getShort("id")); + this.count = nbttagcompound.getByte("Count"); + /* CraftBukkit start - Route through setData for filtering + this.damage = nbttagcompound.getShort("Damage"); + if (this.damage < 0) { + this.damage = 0; + } + */ + this.setData(nbttagcompound.getShort("Damage")); + // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("tag", 10)) { + // CraftBukkit - make defensive copy as this data may be coming from the save thread + this.tag = (NBTTagCompound) nbttagcompound.getCompound("tag").clone(); + validateSkullSkin(); // Spigot + } + } + + // Spigot start - make sure the tag is given the full gameprofile if it's a skull (async lookup) + public void validateSkullSkin() + { + if ( this.item == Items.SKULL && this.getData() == 3 ) + { + String owner; + if ( this.tag.hasKeyOfType( "SkullOwner", 8 ) ) + { + owner = this.tag.getString( "SkullOwner" ); + } else if ( this.tag.hasKeyOfType( "SkullOwner", 10 ) ) + { + net.minecraft.util.com.mojang.authlib.GameProfile profile = GameProfileSerializer.deserialize( this.tag.getCompound( "SkullOwner" ) ); + if ( profile == null || !profile.getProperties().isEmpty() ) + { + return; + } else + { + owner = profile.getName(); + } + } else + { + return; + } + + final String finalOwner = owner; + ThreadingManager.queueHeadConversion(new Runnable() // Poweruser + { + @Override + public void run() + { + + final net.minecraft.util.com.mojang.authlib.GameProfile profile = TileEntitySkull.skinCache.getUnchecked( finalOwner.toLowerCase() ); + if ( profile != null ) + { + MinecraftServer.getServer().processQueue.add( new Runnable() + { + @Override + public void run() + { + NBTTagCompound nbtProfile = new NBTTagCompound(); + GameProfileSerializer.serialize( nbtProfile, profile ); + ItemStack.this.tag.set( "SkullOwner", nbtProfile ); + } + } ); + } + } + } ); + } + } + // Spigot end + + public int getMaxStackSize() { + return this.getItem().getMaxStackSize(); + } + + public boolean isStackable() { + return this.getMaxStackSize() > 1 && (!this.g() || !this.i()); + } + + public boolean g() { + // Spigot Start + if ( this.item.getMaxDurability() <= 0 ) + { + return false; + } + return ( !hasTag() ) || ( !getTag().getBoolean( "Unbreakable" ) ); + // Spigot End + } + + public boolean usesData() { + return this.item.n(); + } + + public boolean i() { + return this.g() && this.damage > 0; + } + + public int j() { + return this.damage; + } + + public int getData() { + return this.damage; + } + + public void setData(int i) { + // CraftBukkit start - Filter out data for items that shouldn't have it + // The crafting system uses this value for a special purpose so we have to allow it + if (i == 32767) { + this.damage = i; + return; + } + + // Is this a block? + if (CraftMagicNumbers.getBlock(CraftMagicNumbers.getId(this.getItem())) != Blocks.AIR) { + // If vanilla doesn't use data on it don't allow any + if (!(this.usesData() || this.getItem().usesDurability())) { + i = 0; + } + } + + // Filter invalid plant data + if (CraftMagicNumbers.getBlock(CraftMagicNumbers.getId(this.getItem())) == Blocks.DOUBLE_PLANT && (i > 5 || i < 0)) { + i = 0; + } + // CraftBukkit end + + this.damage = i; + if (this.damage < -1) { // CraftBukkit - don't filter -1, we use it + this.damage = 0; + } + } + + public int l() { + return this.item.getMaxDurability(); + } + + // Spigot start + public boolean isDamaged(int i, Random random) { + return isDamaged(i, random, null); + } + + public boolean isDamaged(int i, Random random, EntityLiving entityliving) { + // Spigot end + if (!this.g()) { + return false; + } else { + if (i > 0) { + int j = EnchantmentManager.getEnchantmentLevel(Enchantment.DURABILITY.id, this); + int k = 0; + + for (int l = 0; j > 0 && l < i; ++l) { + if (EnchantmentDurability.a(this, j, random)) { + ++k; + } + } + + i -= k; + // Spigot start + if (entityliving instanceof EntityPlayer) { + org.bukkit.craftbukkit.inventory.CraftItemStack item = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this); + org.bukkit.event.player.PlayerItemDamageEvent event = new org.bukkit.event.player.PlayerItemDamageEvent((org.bukkit.entity.Player) entityliving.getBukkitEntity(), item, i); + org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) return false; + i = event.getDamage(); + } + // Spigot end + if (i <= 0 ) { + return false; + } + } + + this.damage += i; + return this.damage > this.l(); + } + } + + public void damage(int i, EntityLiving entityliving) { + if (!(entityliving instanceof EntityHuman) || !((EntityHuman) entityliving).abilities.canInstantlyBuild) { + if (this.g()) { + if (this.isDamaged(i, entityliving.aI(), entityliving)) { // Spigot + entityliving.a(this); + --this.count; + if (entityliving instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entityliving; + + entityhuman.a(StatisticList.BREAK_ITEM_COUNT[Item.getId(this.item)], 1); + if (this.count == 0 && this.getItem() instanceof ItemBow) { + entityhuman.bG(); + } + } + + if (this.count < 0) { + this.count = 0; + } + + // CraftBukkit start - Check for item breaking + if (this.count == 0 && entityliving instanceof EntityHuman) { + org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((EntityHuman) entityliving, this); + } + // CraftBukkit end + + this.damage = 0; + } + } + } + } + + public void a(EntityLiving entityliving, EntityHuman entityhuman) { + boolean flag = this.item.a(this, entityliving, (EntityLiving) entityhuman); + + if (flag) { + entityhuman.a(StatisticList.USE_ITEM_COUNT[Item.getId(this.item)], 1); + } + } + + public void a(World world, Block block, int i, int j, int k, EntityHuman entityhuman) { + boolean flag = this.item.a(this, world, block, i, j, k, entityhuman); + + if (flag) { + entityhuman.a(StatisticList.USE_ITEM_COUNT[Item.getId(this.item)], 1); + } + } + + public boolean b(Block block) { + return this.item.canDestroySpecialBlock(block); + } + + public boolean a(EntityHuman entityhuman, EntityLiving entityliving) { + return this.item.a(this, entityhuman, entityliving); + } + + public ItemStack cloneItemStack() { + ItemStack itemstack = new ItemStack(this.item, this.count, this.damage); + + if (this.tag != null) { + itemstack.tag = (NBTTagCompound) this.tag.clone(); + } + + return itemstack; + } + + // Spigot start + public static boolean fastMatches(ItemStack itemstack, ItemStack itemstack1) { + if (itemstack == null && itemstack1 == null) { + return true; + } + + if (itemstack != null && itemstack1 != null) { + return itemstack.item == itemstack1.item && itemstack.count == itemstack1.count && itemstack.damage == itemstack1.damage; + } + + return false; + } + // Spigot End + + public static boolean equals(ItemStack itemstack, ItemStack itemstack1) { + return itemstack == null && itemstack1 == null ? true : (itemstack != null && itemstack1 != null ? (itemstack.tag == null && itemstack1.tag != null ? false : itemstack.tag == null || itemstack.tag.equals(itemstack1.tag)) : false); + } + + public static boolean matches(ItemStack itemstack, ItemStack itemstack1) { + return itemstack == null && itemstack1 == null ? true : (itemstack != null && itemstack1 != null ? itemstack.d(itemstack1) : false); + } + + private boolean d(ItemStack itemstack) { + return this.count != itemstack.count ? false : (this.item != itemstack.item ? false : (this.damage != itemstack.damage ? false : (this.tag == null && itemstack.tag != null ? false : this.tag == null || this.tag.equals(itemstack.tag)))); + } + + public boolean doMaterialsMatch(ItemStack itemstack) { + return this.item == itemstack.item && this.damage == itemstack.damage; + } + + public String a() { + return this.item.a(this); + } + + public static ItemStack b(ItemStack itemstack) { + return itemstack == null ? null : itemstack.cloneItemStack(); + } + + public String toString() { + return this.count + "x" + this.item.getName() + "@" + this.damage; + } + + public void a(World world, Entity entity, int i, boolean flag) { + if (this.c > 0) { + --this.c; + } + + this.item.a(this, world, entity, i, flag); + } + + public void a(World world, EntityHuman entityhuman, int i) { + entityhuman.a(StatisticList.CRAFT_BLOCK_COUNT[Item.getId(this.item)], i); + this.item.d(this, world, entityhuman); + } + + public int n() { + return this.getItem().d_(this); + } + + public EnumAnimation o() { + return this.getItem().d(this); + } + + public void b(World world, EntityHuman entityhuman, int i) { + this.getItem().a(this, world, entityhuman, i); + } + + public boolean hasTag() { + return this.tag != null; + } + + public NBTTagCompound getTag() { + return this.tag; + } + + public NBTTagList getEnchantments() { + return this.tag == null ? null : this.tag.getList("ench", 10); + } + + public void setTag(NBTTagCompound nbttagcompound) { + this.tag = nbttagcompound; + validateSkullSkin(); // Spigot + } + + public String getName() { + String s = this.getItem().n(this); + + if (this.tag != null && this.tag.hasKeyOfType("display", 10)) { + NBTTagCompound nbttagcompound = this.tag.getCompound("display"); + + if (nbttagcompound.hasKeyOfType("Name", 8)) { + s = nbttagcompound.getString("Name"); + } + } + + return s; + } + + public ItemStack c(String s) { + if (this.tag == null) { + this.tag = new NBTTagCompound(); + } + + if (!this.tag.hasKeyOfType("display", 10)) { + this.tag.set("display", new NBTTagCompound()); + } + + this.tag.getCompound("display").setString("Name", s); + return this; + } + + public void t() { + if (this.tag != null) { + if (this.tag.hasKeyOfType("display", 10)) { + NBTTagCompound nbttagcompound = this.tag.getCompound("display"); + + nbttagcompound.remove("Name"); + if (nbttagcompound.isEmpty()) { + this.tag.remove("display"); + if (this.tag.isEmpty()) { + this.setTag((NBTTagCompound) null); + } + } + } + } + } + + public boolean hasName() { + return this.tag == null ? false : (!this.tag.hasKeyOfType("display", 10) ? false : this.tag.getCompound("display").hasKeyOfType("Name", 8)); + } + + public EnumItemRarity w() { + return this.getItem().f(this); + } + + public boolean x() { + return !this.getItem().e_(this) ? false : !this.hasEnchantments(); + } + + public void addEnchantment(Enchantment enchantment, int i) { + if (this.tag == null) { + this.setTag(new NBTTagCompound()); + } + + if (!this.tag.hasKeyOfType("ench", 9)) { + this.tag.set("ench", new NBTTagList()); + } + + NBTTagList nbttaglist = this.tag.getList("ench", 10); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setShort("id", (short) enchantment.id); + nbttagcompound.setShort("lvl", (short) ((byte) i)); + nbttaglist.add(nbttagcompound); + } + + public boolean hasEnchantments() { + return this.tag != null && this.tag.hasKeyOfType("ench", 9); + } + + public void a(String s, NBTBase nbtbase) { + if (this.tag == null) { + this.setTag(new NBTTagCompound()); + } + + this.tag.set(s, nbtbase); + } + + public boolean z() { + return this.getItem().v(); + } + + public boolean A() { + return this.g != null; + } + + public void a(EntityItemFrame entityitemframe) { + this.g = entityitemframe; + } + + public EntityItemFrame B() { + return this.g; + } + + public int getRepairCost() { + return this.hasTag() && this.tag.hasKeyOfType("RepairCost", 3) ? this.tag.getInt("RepairCost") : 0; + } + + public void setRepairCost(int i) { + if (!this.hasTag()) { + this.tag = new NBTTagCompound(); + } + + this.tag.setInt("RepairCost", i); + } + + public Multimap D() { + Object object; + + if (this.hasTag() && this.tag.hasKeyOfType("AttributeModifiers", 9)) { + object = HashMultimap.create(); + NBTTagList nbttaglist = this.tag.getList("AttributeModifiers", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.get(i); + AttributeModifier attributemodifier = GenericAttributes.a(nbttagcompound); + + if (attributemodifier.a().getLeastSignificantBits() != 0L && attributemodifier.a().getMostSignificantBits() != 0L) { + ((Multimap) object).put(nbttagcompound.getString("AttributeName"), attributemodifier); + } + } + } else { + object = this.getItem().k(); + } + + return (Multimap) object; + } + + public void setItem(Item item) { + this.item = item; + this.setData(this.getData()); // CraftBukkit - Set data again to ensure it is filtered properly + } + + public IChatBaseComponent E() { + IChatBaseComponent ichatbasecomponent = (new ChatComponentText("[")).a(this.getName()).a("]"); + + if (this.item != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + this.save(nbttagcompound); + ichatbasecomponent.getChatModifier().a(new ChatHoverable(EnumHoverAction.SHOW_ITEM, new ChatComponentText(nbttagcompound.toString()))); + ichatbasecomponent.getChatModifier().setColor(this.w().e); + } + + return ichatbasecomponent; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemWaterLily.java b/vspigot-server/src/main/java/net/minecraft/server/ItemWaterLily.java new file mode 100644 index 0000000..246090d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemWaterLily.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +public class ItemWaterLily extends ItemWithAuxData { + + public ItemWaterLily(Block block) { + super(block, false); + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { + MovingObjectPosition movingobjectposition = this.a(world, entityhuman, true); + + if (movingobjectposition == null) { + return itemstack; + } else { + if (movingobjectposition.type == EnumMovingObjectType.BLOCK) { + int i = movingobjectposition.b; + int j = movingobjectposition.c; + int k = movingobjectposition.d; + + if (!world.a(entityhuman, i, j, k)) { + return itemstack; + } + + if (!entityhuman.a(i, j, k, movingobjectposition.face, itemstack)) { + return itemstack; + } + + if (world.getType(i, j, k).getMaterial() == Material.WATER && world.getData(i, j, k) == 0 && world.isEmpty(i, j + 1, k)) { + // CraftBukkit start - special case for handling block placement with water lilies + org.bukkit.block.BlockState blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(world, i, j + 1, k); + world.setTypeUpdate(i, j + 1, k, Blocks.WATER_LILY); + org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, blockstate, i, j, k); + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { + blockstate.update(true, false); + return itemstack; + } + // CraftBukkit end + + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } + } + } + + return itemstack; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ItemWorldMap.java b/vspigot-server/src/main/java/net/minecraft/server/ItemWorldMap.java new file mode 100644 index 0000000..881fd4b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ItemWorldMap.java @@ -0,0 +1,231 @@ +package net.minecraft.server; + +import net.minecraft.util.com.google.common.collect.HashMultiset; +import net.minecraft.util.com.google.common.collect.Iterables; +import net.minecraft.util.com.google.common.collect.Multisets; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.event.server.MapInitializeEvent; +// CraftBukkit end + +public class ItemWorldMap extends ItemWorldMapBase { + + protected ItemWorldMap() { + this.a(true); + } + + public WorldMap getSavedMap(ItemStack itemstack, World world) { + World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world + String s = "map_" + itemstack.getData(); + WorldMap worldmap = (WorldMap) worldMain.a(WorldMap.class, s); // CraftBukkit - use primary world for maps + + if (worldmap == null && !world.isStatic) { + itemstack.setData(worldMain.b("map")); // CraftBukkit - use primary world for maps + s = "map_" + itemstack.getData(); + worldmap = new WorldMap(s); + worldmap.scale = 3; + int i = 128 * (1 << worldmap.scale); + + worldmap.centerX = Math.round((float) world.getWorldData().c() / (float) i) * i; + worldmap.centerZ = Math.round((float) (world.getWorldData().e() / i)) * i; + worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - fixes Bukkit multiworld maps + worldmap.c(); + worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit - use primary world for maps + + // CraftBukkit start + MapInitializeEvent event = new MapInitializeEvent(worldmap.mapView); + Bukkit.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + } + + return worldmap; + } + + public void a(World world, Entity entity, WorldMap worldmap) { + // CraftBukkit - world.worldProvider -> ((WorldServer) world) + if (((WorldServer) world).dimension == worldmap.map && entity instanceof EntityHuman) { + int i = 1 << worldmap.scale; + int j = worldmap.centerX; + int k = worldmap.centerZ; + int l = MathHelper.floor(entity.locX - (double) j) / i + 64; + int i1 = MathHelper.floor(entity.locZ - (double) k) / i + 64; + int j1 = 128 / i; + + if (world.worldProvider.g) { + j1 /= 2; + } + + WorldMapHumanTracker worldmaphumantracker = worldmap.a((EntityHuman) entity); + + ++worldmaphumantracker.d; + + for (int k1 = l - j1 + 1; k1 < l + j1; ++k1) { + if ((k1 & 15) == (worldmaphumantracker.d & 15)) { + int l1 = 255; + int i2 = 0; + double d0 = 0.0D; + + for (int j2 = i1 - j1 - 1; j2 < i1 + j1; ++j2) { + if (k1 >= 0 && j2 >= -1 && k1 < 128 && j2 < 128) { + int k2 = k1 - l; + int l2 = j2 - i1; + boolean flag = k2 * k2 + l2 * l2 > (j1 - 2) * (j1 - 2); + int i3 = (j / i + k1 - 64) * i; + int j3 = (k / i + j2 - 64) * i; + HashMultiset hashmultiset = HashMultiset.create(); + Chunk chunk = world.getChunkAtWorldCoords(i3, j3); + + if (!chunk.isEmpty()) { + int k3 = i3 & 15; + int l3 = j3 & 15; + int i4 = 0; + double d1 = 0.0D; + int j4; + + if (world.worldProvider.g) { + j4 = i3 + j3 * 231871; + j4 = j4 * j4 * 31287121 + j4 * 11; + if ((j4 >> 20 & 1) == 0) { + hashmultiset.add(Blocks.DIRT.f(0), 10); + } else { + hashmultiset.add(Blocks.STONE.f(0), 100); + } + + d1 = 100.0D; + } else { + for (j4 = 0; j4 < i; ++j4) { + for (int k4 = 0; k4 < i; ++k4) { + int l4 = chunk.b(j4 + k3, k4 + l3) + 1; + Block block = Blocks.AIR; + int i5 = 0; + + if (l4 > 1) { + do { + --l4; + block = chunk.getType(j4 + k3, l4, k4 + l3); + i5 = chunk.getData(j4 + k3, l4, k4 + l3); + } while (block.f(i5) == MaterialMapColor.b && l4 > 0); + + if (l4 > 0 && block.getMaterial().isLiquid()) { + int j5 = l4 - 1; + + Block block1; + + do { + block1 = chunk.getType(j4 + k3, j5--, k4 + l3); + ++i4; + } while (j5 > 0 && block1.getMaterial().isLiquid()); + } + } + + d1 += (double) l4 / (double) (i * i); + hashmultiset.add(block.f(i5)); + } + } + } + + i4 /= i * i; + double d2 = (d1 - d0) * 4.0D / (double) (i + 4) + ((double) (k1 + j2 & 1) - 0.5D) * 0.4D; + byte b0 = 1; + + if (d2 > 0.6D) { + b0 = 2; + } + + if (d2 < -0.6D) { + b0 = 0; + } + + MaterialMapColor materialmapcolor = (MaterialMapColor) Iterables.getFirst(Multisets.copyHighestCountFirst(hashmultiset), MaterialMapColor.b); + + if (materialmapcolor == MaterialMapColor.n) { + d2 = (double) i4 * 0.1D + (double) (k1 + j2 & 1) * 0.2D; + b0 = 1; + if (d2 < 0.5D) { + b0 = 2; + } + + if (d2 > 0.9D) { + b0 = 0; + } + } + + d0 = d1; + if (j2 >= 0 && k2 * k2 + l2 * l2 < j1 * j1 && (!flag || (k1 + j2 & 1) != 0)) { + byte b1 = worldmap.colors[k1 + j2 * 128]; + byte b2 = (byte) (materialmapcolor.M * 4 + b0); + + if (b1 != b2) { + if (l1 > j2) { + l1 = j2; + } + + if (i2 < j2) { + i2 = j2; + } + + worldmap.colors[k1 + j2 * 128] = b2; + } + } + } + } + } + + if (l1 <= i2) { + worldmap.flagDirty(k1, l1, i2); + } + } + } + } + } + + public void a(ItemStack itemstack, World world, Entity entity, int i, boolean flag) { + if (!world.isStatic) { + WorldMap worldmap = this.getSavedMap(itemstack, world); + + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + worldmap.a(entityhuman, itemstack); + } + + if (flag) { + this.a(world, entity, worldmap); + } + } + } + + public Packet c(ItemStack itemstack, World world, EntityHuman entityhuman) { + byte[] abyte = this.getSavedMap(itemstack, world).getUpdatePacket(itemstack, world, entityhuman); + + return abyte == null ? null : new PacketPlayOutMap(itemstack.getData(), abyte, this.getSavedMap(itemstack, world).scale); // Spigot - protocol patch + } + + public void d(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (itemstack.hasTag() && itemstack.getTag().getBoolean("map_is_scaling")) { + WorldMap worldmap = Items.MAP.getSavedMap(itemstack, world); + + world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps + + itemstack.setData(world.b("map")); + WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData()); + + worldmap1.scale = (byte) (worldmap.scale + 1); + if (worldmap1.scale > 4) { + worldmap1.scale = 4; + } + + worldmap1.centerX = worldmap.centerX; + worldmap1.centerZ = worldmap.centerZ; + worldmap1.map = worldmap.map; + worldmap1.c(); + world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1); + + // CraftBukkit start + MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView); + Bukkit.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/JsonList.java b/vspigot-server/src/main/java/net/minecraft/server/JsonList.java new file mode 100644 index 0000000..dce0d1d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/JsonList.java @@ -0,0 +1,179 @@ +package net.minecraft.server; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.google.common.collect.Lists; +import net.minecraft.util.com.google.common.collect.Maps; +import net.minecraft.util.com.google.common.io.Files; +import net.minecraft.util.com.google.gson.Gson; +import net.minecraft.util.com.google.gson.GsonBuilder; +import net.minecraft.util.com.google.gson.JsonObject; +import net.minecraft.util.org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.spigotmc.SpigotConfig; + +public class JsonList { + + protected static final Logger a = LogManager.getLogger(); + protected final Gson b; + private final File c; + private final Map d = Maps.newHashMap(); + private boolean e = true; + private static final ParameterizedType f = new JsonListType(); + + public JsonList(File file1) { + this.c = file1; + GsonBuilder gsonbuilder = (new GsonBuilder()).setPrettyPrinting(); + + gsonbuilder.registerTypeHierarchyAdapter(JsonListEntry.class, new JsonListEntrySerializer(this, (JsonListType) null)); + this.b = gsonbuilder.create(); + } + + public boolean isEnabled() { + return this.e; + } + + public void a(boolean flag) { + this.e = flag; + } + + public File c() { + return this.c; + } + + public void add(JsonListEntry jsonlistentry) { + this.d.put(this.a(jsonlistentry.getKey()), jsonlistentry); + + try { + this.save(); + } catch (IOException ioexception) { + a.warn("Could not save the list after adding a user.", ioexception); + } + } + + public JsonListEntry get(Object object) { + this.h(); + return (JsonListEntry) this.d.get(this.a(object)); + } + + public void remove(Object object) { + this.d.remove(this.a(object)); + + try { + this.save(); + } catch (IOException ioexception) { + a.warn("Could not save the list after removing a user.", ioexception); + } + } + + public String[] getEntries() { + return (String[]) this.d.keySet().toArray(new String[this.d.size()]); + } + + // CraftBukkit start + public Collection getValues() { + return this.d.values(); + } + // CraftBukkit end + + public boolean isEmpty() { + return this.d.size() < 1; + } + + protected String a(Object object) { + return object.toString(); + } + + protected boolean d(Object object) { + return this.d.containsKey(this.a(object)); + } + + private void h() { + ArrayList arraylist = Lists.newArrayList(); + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { + JsonListEntry jsonlistentry = (JsonListEntry) iterator.next(); + + if (jsonlistentry.hasExpired()) { + arraylist.add(jsonlistentry.getKey()); + } + } + + iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + Object object = iterator.next(); + + this.d.remove(object); + } + } + + protected JsonListEntry a(JsonObject jsonobject) { + return new JsonListEntry(null, jsonobject); + } + + protected Map e() { + return this.d; + } + + public void save() throws IOException { // CraftBukkit - Added throws + if (SpigotConfig.disableSaving) return; // MineHQ + Collection collection = this.d.values(); + String s = this.b.toJson(collection); + BufferedWriter bufferedwriter = null; + + try { + bufferedwriter = Files.newWriter(this.c, Charsets.UTF_8); + bufferedwriter.write(s); + } finally { + IOUtils.closeQuietly(bufferedwriter); + } + } + + public void load() throws IOException { // CraftBukkit - Added throws + Collection collection = null; + BufferedReader bufferedreader = null; + + try { + bufferedreader = Files.newReader(this.c, Charsets.UTF_8); + collection = (Collection) this.b.fromJson(bufferedreader, f); + // Spigot Start + } catch ( java.io.FileNotFoundException ex ) + { + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.INFO, "Unable to find file {0}, creating it.", this.c ); + } catch ( net.minecraft.util.com.google.gson.JsonSyntaxException ex ) + { + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.WARNING, "Unable to read file {0}, backing it up to {0}.backup and creating new copy.", this.c ); + File backup = new File( this.c + ".backup" ); + this.c.renameTo( backup ); + this.c.delete(); + // Spigot End + } finally { + IOUtils.closeQuietly(bufferedreader); + } + + if (collection != null) { + this.d.clear(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + JsonListEntry jsonlistentry = (JsonListEntry) iterator.next(); + + if (jsonlistentry.getKey() != null) { + this.d.put(this.a(jsonlistentry.getKey()), jsonlistentry); + } + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/JsonListEntry.java b/vspigot-server/src/main/java/net/minecraft/server/JsonListEntry.java new file mode 100644 index 0000000..1cd5104 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/JsonListEntry.java @@ -0,0 +1,26 @@ +package net.minecraft.server; + +import net.minecraft.util.com.google.gson.JsonObject; + +public class JsonListEntry { + + private final Object a; + + public JsonListEntry(Object object) { + this.a = object; + } + + protected JsonListEntry(Object object, JsonObject jsonobject) { + this.a = object; + } + + public Object getKey() { // CraftBukkit -> package private -> public + return this.a; + } + + boolean hasExpired() { + return false; + } + + protected void a(JsonObject jsonobject) {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/JsonListEntrySerializer.java b/vspigot-server/src/main/java/net/minecraft/server/JsonListEntrySerializer.java new file mode 100644 index 0000000..f4eee66 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/JsonListEntrySerializer.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +// CraftBukkit - Imported for package private use in JsonList + +import java.lang.reflect.Type; + +import net.minecraft.util.com.google.gson.JsonDeserializationContext; +import net.minecraft.util.com.google.gson.JsonDeserializer; +import net.minecraft.util.com.google.gson.JsonElement; +import net.minecraft.util.com.google.gson.JsonObject; +import net.minecraft.util.com.google.gson.JsonSerializationContext; +import net.minecraft.util.com.google.gson.JsonSerializer; + +class JsonListEntrySerializer implements JsonDeserializer, JsonSerializer { + + final JsonList a; + + private JsonListEntrySerializer(JsonList jsonlist) { + this.a = jsonlist; + } + + public JsonElement a(JsonListEntry jsonlistentry, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + jsonlistentry.a(jsonobject); + return jsonobject; + } + + public JsonListEntry a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) { + if (jsonelement.isJsonObject()) { + JsonObject jsonobject = jsonelement.getAsJsonObject(); + JsonListEntry jsonlistentry = this.a.a(jsonobject); + + return jsonlistentry; + } else { + return null; + } + } + + public JsonElement serialize(Object object, Type type, JsonSerializationContext jsonserializationcontext) { + return this.a((JsonListEntry) object, type, jsonserializationcontext); + } + + public Object deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) { + return this.a(jsonelement, type, jsondeserializationcontext); + } + + JsonListEntrySerializer(JsonList jsonlist, JsonListType jsonlisttype) { + this(jsonlist); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/LayerIsland.java b/vspigot-server/src/main/java/net/minecraft/server/LayerIsland.java new file mode 100644 index 0000000..ae7702d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/LayerIsland.java @@ -0,0 +1,39 @@ +package net.minecraft.server; + +import java.util.Arrays; + +public class LayerIsland extends GenLayer { + + // MineHQ start - add world + private final World world; + + public LayerIsland(long i, World world) { + super(i); + this.world = world; + // MineHQ end + } + + public int[] a(int i, int j, int k, int l) { + int[] aint = IntCache.a(k * l); + + // MineHQ start - oceans option + if (!world.generatorConfig.oceans) { + Arrays.fill(aint, 1); + return aint; + } + // MineHQ end + + for (int i1 = 0; i1 < l; ++i1) { + for (int j1 = 0; j1 < k; ++j1) { + this.a((long) (i + j1), (long) (j + i1)); + aint[j1 + i1 * k] = this.a(10) == 0 ? 1 : 0; + } + } + + if (i > -k && i <= 0 && j > -l && j <= 0) { + aint[-i + -j * k] = 1; + } + + return aint; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/LoginListener.java b/vspigot-server/src/main/java/net/minecraft/server/LoginListener.java new file mode 100644 index 0000000..6cab30b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/LoginListener.java @@ -0,0 +1,223 @@ +package net.minecraft.server; + +import java.util.Arrays; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import javax.crypto.SecretKey; + +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.properties.Property; +import net.minecraft.util.io.netty.util.concurrent.Future; +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; +import net.minecraft.util.org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// Pweruser start +import net.valorhcf.ThreadingManager; +// Poweruser end + +public class LoginListener implements PacketLoginInListener { + + private static final AtomicInteger b = new AtomicInteger(0); + private static final Logger c = LogManager.getLogger(); + private static final Random random = new Random(); + private final byte[] e = new byte[4]; + private final MinecraftServer server; + public final NetworkManager networkManager; + private EnumProtocolState g; + private int h; + private GameProfile i; + private String j; + private SecretKey loginKey; + public String hostname = ""; // CraftBukkit - add field + + public LoginListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { + this.g = EnumProtocolState.HELLO; + this.j = ""; + this.server = minecraftserver; + this.networkManager = networkmanager; + random.nextBytes(this.e); + } + + // Poweruser start + private IllegalStateException authenticationException; + + protected void caughtAuthenticationException(Exception e) { + this.authenticationException = new IllegalStateException(e.getMessage(), e); + } + + protected boolean compareRandomConnectionKey(byte[] array) { + return Arrays.equals(this.e, array); + } + + protected void setLoginKey(SecretKey loginKey) { + this.loginKey = loginKey; + } + + public void a() { + if(this.authenticationException != null) { + IllegalStateException exception = this.authenticationException; + this.authenticationException = null; + throw exception; + } + // Poweruser end + + if (this.g == EnumProtocolState.READY_TO_ACCEPT) { + this.c(); + } + + if (this.h++ == 600) { + this.disconnect("Took too long to log in"); + } + } + + public void disconnect(String s) { + try { + c.info("Disconnecting " + this.i.getName() + ": " + s); + ChatComponentText chatcomponenttext = new ChatComponentText(s); + + this.networkManager.handle(new PacketLoginOutDisconnect(chatcomponenttext), NetworkManager.emptyListenerArray); // Poweruser + this.networkManager.close(chatcomponenttext); + } catch (Exception exception) { + c.error("Error whilst disconnecting player", exception); + } + } + + // Spigot start + public void initUUID() + { + UUID uuid; + if ( networkManager.spoofedUUID != null ) + { + uuid = networkManager.spoofedUUID; + } else + { + uuid = UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + this.i.getName() ).getBytes( Charsets.UTF_8 ) ); + } + + this.i = new GameProfile( uuid, this.i.getName() ); + + if (networkManager.spoofedProfile != null) + { + for ( Property property : networkManager.spoofedProfile ) + { + this.i.getProperties().put( property.getName(), property ); + } + } + } + // Spigot end + + public void c() { + // Spigot start - Moved to initUUID + /* + if (!this.i.isComplete()) { + this.i = this.a(this.i); + } + */ + // Spigot end + + // CraftBukkit start - fire PlayerLoginEvent + EntityPlayer s = this.server.getPlayerList().attemptLogin(this, this.i, this.hostname); + + if (s == null) { + // this.disconnect(s); + // CraftBukkit end + } else { + this.g = EnumProtocolState.e; + // Spigot start + if ( networkManager.getVersion() >= 27 ) + { + this.networkManager.handle( new org.spigotmc.ProtocolInjector.PacketLoginCompression( 256 ), new GenericFutureListener() + { + @Override + public void operationComplete(Future future) throws Exception + { + networkManager.enableCompression(); + } + } ); + } + // Spigot end + this.networkManager.handle(new PacketLoginOutSuccess(this.i), NetworkManager.emptyListenerArray); // Poweruser + this.server.getPlayerList().a(this.networkManager, this.server.getPlayerList().processLogin(this.i, s)); // CraftBukkit - add player reference + } + } + + public void a(IChatBaseComponent ichatbasecomponent) { + c.info((this.i != null ? this.i.getName() : this.networkManager.getSocketAddress()) + " lost connection: " + ichatbasecomponent.c()); + } + + public String getName() { + return this.i != null ? "[" + this.i.getName() + ", " + this.i.getId() + "]" + " (" + this.networkManager.getSocketAddress().toString() + ")" : String.valueOf(this.networkManager.getSocketAddress()); + } + + public void a(EnumProtocol enumprotocol, EnumProtocol enumprotocol1) { + Validate.validState(this.g == EnumProtocolState.e || this.g == EnumProtocolState.HELLO, "Unexpected change in protocol", new Object[0]); + Validate.validState(enumprotocol1 == EnumProtocol.PLAY || enumprotocol1 == EnumProtocol.LOGIN, "Unexpected protocol " + enumprotocol1, new Object[0]); + } + + public void a(PacketLoginInStart packetlogininstart) { + Validate.validState(this.g == EnumProtocolState.HELLO, "Unexpected hello packet", new Object[0]); + this.i = packetlogininstart.c(); + if (this.server.getOnlineMode() && !this.networkManager.c()) { + this.g = EnumProtocolState.KEY; + this.networkManager.handle(new PacketLoginOutEncryptionBegin(this.j, this.server.K().getPublic(), this.e), NetworkManager.emptyListenerArray); // Poweruser + } else { + ThreadingManager.execute(new ThreadPlayerLookupUUID(this)); // Poweruser + } + } + + public void a(PacketLoginInEncryptionBegin packetlogininencryptionbegin) { + Validate.validState(this.g == EnumProtocolState.KEY, "Unexpected key packet", new Object[0]); + + /* + PrivateKey privatekey = this.server.K().getPrivate(); + + if (!Arrays.equals(this.e, packetlogininencryptionbegin.b(privatekey))) { + throw new IllegalStateException("Invalid nonce!"); + } else { + this.loginKey = packetlogininencryptionbegin.a(privatekey); + this.g = EnumProtocolState.AUTHENTICATING; + this.networkManager.a(this.loginKey); + ThreadingManager.execute(new ThreadPlayerLookupUUID(this)); // Poweruser + } + */ + ThreadingManager.execute(new ThreadPlayerLookupUUID(this, packetlogininencryptionbegin)); // Poweruser + } + + protected GameProfile a(GameProfile gameprofile) { + UUID uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + gameprofile.getName()).getBytes(Charsets.UTF_8)); + + return new GameProfile(uuid, gameprofile.getName()); + } + + static GameProfile a(LoginListener loginlistener) { + return loginlistener.i; + } + + static String b(LoginListener loginlistener) { + return loginlistener.j; + } + + static MinecraftServer c(LoginListener loginlistener) { + return loginlistener.server; + } + + static SecretKey d(LoginListener loginlistener) { + return loginlistener.loginKey; + } + + static GameProfile a(LoginListener loginlistener, GameProfile gameprofile) { + return loginlistener.i = gameprofile; + } + + static Logger e() { + return c; + } + + static EnumProtocolState a(LoginListener loginlistener, EnumProtocolState enumprotocolstate) { + return loginlistener.g = enumprotocolstate; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/MerchantRecipe.java b/vspigot-server/src/main/java/net/minecraft/server/MerchantRecipe.java new file mode 100644 index 0000000..aa6bbf6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/MerchantRecipe.java @@ -0,0 +1,101 @@ +package net.minecraft.server; + +public class MerchantRecipe { + + private ItemStack buyingItem1; + private ItemStack buyingItem2; + private ItemStack sellingItem; + public int uses; // Spigot - protocol patch + public int maxUses; // Spigot - protocol patch + + public MerchantRecipe(NBTTagCompound nbttagcompound) { + this.a(nbttagcompound); + } + + public MerchantRecipe(ItemStack itemstack, ItemStack itemstack1, ItemStack itemstack2) { + this.buyingItem1 = itemstack; + this.buyingItem2 = itemstack1; + this.sellingItem = itemstack2; + this.maxUses = 7; + } + + public MerchantRecipe(ItemStack itemstack, ItemStack itemstack1) { + this(itemstack, (ItemStack) null, itemstack1); + } + + public MerchantRecipe(ItemStack itemstack, Item item) { + this(itemstack, new ItemStack(item)); + } + + public ItemStack getBuyItem1() { + return this.buyingItem1; + } + + public ItemStack getBuyItem2() { + return this.buyingItem2; + } + + public boolean hasSecondItem() { + return this.buyingItem2 != null; + } + + public ItemStack getBuyItem3() { + return this.sellingItem; + } + + public boolean a(MerchantRecipe merchantrecipe) { + return this.buyingItem1.getItem() == merchantrecipe.buyingItem1.getItem() && this.sellingItem.getItem() == merchantrecipe.sellingItem.getItem() ? this.buyingItem2 == null && merchantrecipe.buyingItem2 == null || this.buyingItem2 != null && merchantrecipe.buyingItem2 != null && this.buyingItem2.getItem() == merchantrecipe.buyingItem2.getItem() : false; + } + + public boolean b(MerchantRecipe merchantrecipe) { + return this.a(merchantrecipe) && (this.buyingItem1.count < merchantrecipe.buyingItem1.count || this.buyingItem2 != null && this.buyingItem2.count < merchantrecipe.buyingItem2.count); + } + + public void f() { + ++this.uses; + } + + public void a(int i) { + this.maxUses += i; + } + + public boolean g() { + return this.uses >= this.maxUses; + } + + public void a(NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("buy"); + + this.buyingItem1 = ItemStack.createStack(nbttagcompound1); + NBTTagCompound nbttagcompound2 = nbttagcompound.getCompound("sell"); + + this.sellingItem = ItemStack.createStack(nbttagcompound2); + if (nbttagcompound.hasKeyOfType("buyB", 10)) { + this.buyingItem2 = ItemStack.createStack(nbttagcompound.getCompound("buyB")); + } + + if (nbttagcompound.hasKeyOfType("uses", 99)) { + this.uses = nbttagcompound.getInt("uses"); + } + + if (nbttagcompound.hasKeyOfType("maxUses", 99)) { + this.maxUses = nbttagcompound.getInt("maxUses"); + } else { + this.maxUses = 7; + } + } + + public NBTTagCompound i() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.set("buy", this.buyingItem1.save(new NBTTagCompound())); + nbttagcompound.set("sell", this.sellingItem.save(new NBTTagCompound())); + if (this.buyingItem2 != null) { + nbttagcompound.set("buyB", this.buyingItem2.save(new NBTTagCompound())); + } + + nbttagcompound.setInt("uses", this.uses); + nbttagcompound.setInt("maxUses", this.maxUses); + return nbttagcompound; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/MerchantRecipeList.java b/vspigot-server/src/main/java/net/minecraft/server/MerchantRecipeList.java new file mode 100644 index 0000000..6ddbf7a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/MerchantRecipeList.java @@ -0,0 +1,96 @@ +package net.minecraft.server; + +import java.util.ArrayList; + +public class MerchantRecipeList extends ArrayList { + + public MerchantRecipeList() {} + + public MerchantRecipeList(NBTTagCompound nbttagcompound) { + this.a(nbttagcompound); + } + + public MerchantRecipe a(ItemStack itemstack, ItemStack itemstack1, int i) { + if (i > 0 && i < this.size()) { + MerchantRecipe merchantrecipe = (MerchantRecipe) this.get(i); + + return itemstack.getItem() == merchantrecipe.getBuyItem1().getItem() && (itemstack1 == null && !merchantrecipe.hasSecondItem() || merchantrecipe.hasSecondItem() && itemstack1 != null && merchantrecipe.getBuyItem2().getItem() == itemstack1.getItem()) && itemstack.count >= merchantrecipe.getBuyItem1().count && (!merchantrecipe.hasSecondItem() || itemstack1.count >= merchantrecipe.getBuyItem2().count) ? merchantrecipe : null; + } else { + for (int j = 0; j < this.size(); ++j) { + MerchantRecipe merchantrecipe1 = (MerchantRecipe) this.get(j); + + if (itemstack.getItem() == merchantrecipe1.getBuyItem1().getItem() && itemstack.count >= merchantrecipe1.getBuyItem1().count && (!merchantrecipe1.hasSecondItem() && itemstack1 == null || merchantrecipe1.hasSecondItem() && itemstack1 != null && merchantrecipe1.getBuyItem2().getItem() == itemstack1.getItem() && itemstack1.count >= merchantrecipe1.getBuyItem2().count)) { + return merchantrecipe1; + } + } + + return null; + } + } + + public void a(MerchantRecipe merchantrecipe) { + for (int i = 0; i < this.size(); ++i) { + MerchantRecipe merchantrecipe1 = (MerchantRecipe) this.get(i); + + if (merchantrecipe.a(merchantrecipe1)) { + if (merchantrecipe.b(merchantrecipe1)) { + this.set(i, merchantrecipe); + } + + return; + } + } + + this.add(merchantrecipe); + } + + public void a(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeByte((byte) (this.size() & 255)); + + for (int i = 0; i < this.size(); ++i) { + MerchantRecipe merchantrecipe = (MerchantRecipe) this.get(i); + + packetdataserializer.a(merchantrecipe.getBuyItem1()); + packetdataserializer.a(merchantrecipe.getBuyItem3()); + ItemStack itemstack = merchantrecipe.getBuyItem2(); + + packetdataserializer.writeBoolean(itemstack != null); + if (itemstack != null) { + packetdataserializer.a(itemstack); + } + + packetdataserializer.writeBoolean(merchantrecipe.g()); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 28 ) + { + packetdataserializer.writeInt( merchantrecipe.uses ); + packetdataserializer.writeInt( merchantrecipe.maxUses ); + } + // Spigot end + } + } + + public void a(NBTTagCompound nbttagcompound) { + NBTTagList nbttaglist = nbttagcompound.getList("Recipes", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + + this.add(new MerchantRecipe(nbttagcompound1)); + } + } + + public NBTTagCompound a() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.size(); ++i) { + MerchantRecipe merchantrecipe = (MerchantRecipe) this.get(i); + + nbttaglist.add(merchantrecipe.i()); + } + + nbttagcompound.set("Recipes", nbttaglist); + return nbttagcompound; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/MethodProfiler.java b/vspigot-server/src/main/java/net/minecraft/server/MethodProfiler.java new file mode 100644 index 0000000..7285e09 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/MethodProfiler.java @@ -0,0 +1,24 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start - Strip down to empty methods, performance cost +public class MethodProfiler { + public boolean a = false; + + public final void a() { } + public final void a(String s) { } + public final void b() { } + public final List b(String s) { return null; } + public final void c(String s) { } + public final String c() { return null; } +} +// CraftBukkit end diff --git a/vspigot-server/src/main/java/net/minecraft/server/MinecraftServer.java b/vspigot-server/src/main/java/net/minecraft/server/MinecraftServer.java new file mode 100644 index 0000000..d7d5851 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +1,1477 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.event.server.ServerTickEndEvent; +import com.destroystokyo.paper.event.server.ServerTickStartEvent; +import jline.console.ConsoleReader; +import joptsimple.OptionSet; +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.GameProfileRepository; +import net.minecraft.util.com.mojang.authlib.minecraft.MinecraftSessionService; +import net.minecraft.util.com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.buffer.ByteBufOutputStream; +import net.minecraft.util.io.netty.buffer.Unpooled; +import net.minecraft.util.io.netty.handler.codec.base64.Base64; +import net.minecraft.util.org.apache.commons.lang3.Validate; +import net.valorhcf.ThreadingManager; +import net.valorhcf.autosave.AutoSave; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.World.Environment; +import org.bukkit.craftbukkit.SpigotTimings; +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.event.server.RemoteServerCommandEvent; +import org.bukkit.event.world.WorldSaveEvent; +import org.spigotmc.SpigotConfig; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.Proxy; +import java.security.KeyPair; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.*; +import java.util.concurrent.Callable; +// Poweruser end + +public abstract class MinecraftServer implements ICommandListener, Runnable, IMojangStatistics { + + private static final File a = new File("config/misc", "usercache.json"); // MineHQ - Dedicated config directory + private static MinecraftServer j; + public Convertable convertable; // CraftBukkit - private final -> public + private final MojangStatisticsGenerator l = new MojangStatisticsGenerator("server", this, ar()); + public File universe; // CraftBukkit - private final -> public + private final List n = new ArrayList(); + private final ICommandHandler o; + public final MethodProfiler methodProfiler = new MethodProfiler(); + private ServerConnection p; // Spigot + private final ServerPing q = new ServerPing(); + private final Random r = new Random(); + private String serverIp; + private int t = -1; + public WorldServer[] worldServer; + private PlayerList u; + private boolean isRunning = true; + private boolean isStopped; + private int ticks; + protected final Proxy d; + public String e; + public int f; + private boolean onlineMode; + private boolean spawnAnimals; + private boolean spawnNPCs; + private boolean pvpMode; + private boolean allowFlight; + private String motd; + private int E; + private int F = 0; + public final long[] g = new long[100]; + public long[][] h; + private KeyPair G; + private String H; + private String I; + private boolean demoMode; + private boolean L; + private boolean M; + private String N = ""; + private boolean O; + private long P; + private String Q; + private boolean R; + private boolean S; + private final YggdrasilAuthenticationService T; + private final MinecraftSessionService U; + private long V = 0L; + private final GameProfileRepository W; + private final UserCache X; + + // CraftBukkit start - add fields + public List worlds = new ArrayList(); + public org.bukkit.craftbukkit.CraftServer server; + public OptionSet options; + public org.bukkit.command.ConsoleCommandSender console; + public org.bukkit.command.RemoteConsoleCommandSender remoteConsole; + public ConsoleReader reader; + public static int currentTick = 0; + public final Thread primaryThread; + public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); + public int autosavePeriod; + // CraftBukkit end + // Spigot start + private static final int TPS = 20; + private static final int TICK_TIME = 1000000000 / TPS; + private static final int SAMPLE_INTERVAL = 100; + public final double[] recentTps = new double[3]; + // Spigot end + protected boolean abnormalTermination; // SportBukkit + + // Kohi start + public int entities; + public int activeEntities; + // Kohi end + + // Poweruser start + private ThreadingManager threadingManager; + private AutoSave autoSaveManager; + // Poweruser end + + public float lastTickTime = 0F; // MineHQ + + public MinecraftServer(OptionSet options, Proxy proxy) { // CraftBukkit - signature file -> OptionSet + net.minecraft.util.io.netty.util.ResourceLeakDetector.setEnabled(false); // Spigot - disable + this.X = new UserCache(this, a); + j = this; + this.d = proxy; + this.threadingManager = new ThreadingManager(); // Poweruser + this.autoSaveManager = new AutoSave(); // Poweruser + // this.universe = file1; // CraftBukkit + // this.p = new ServerConnection(this); // Spigot + this.o = new CommandDispatcher(); + // this.convertable = new WorldLoaderServer(file1); // CraftBukkit - moved to DedicatedServer.init + this.T = new YggdrasilAuthenticationService(proxy, UUID.randomUUID().toString()); + this.U = this.T.createMinecraftSessionService(); + this.W = this.T.createProfileRepository(); + // CraftBukkit start + this.options = options; + // Try to see if we're actually running in a terminal, disable jline if not + if (System.console() == null) { + System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); + org.bukkit.craftbukkit.Main.useJline = false; + } + + try { + this.reader = new ConsoleReader(System.in, System.out); + this.reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators + } catch (Throwable e) { + try { + // Try again with jline disabled for Windows users without C++ 2008 Redistributable + System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); + System.setProperty("user.language", "en"); + org.bukkit.craftbukkit.Main.useJline = false; + this.reader = new ConsoleReader(System.in, System.out); + this.reader.setExpandEvents(false); + } catch (IOException ex) { + getLogger().warn((String) null, ex); + } + } + Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this)); + + primaryThread = new ThreadServerApplication(this, "Server thread"); // Moved from main + } + + public abstract PropertyManager getPropertyManager(); + // CraftBukkit end + + protected abstract boolean init() throws java.net.UnknownHostException; // CraftBukkit - throws UnknownHostException + + protected void a(String s) { + if (this.getConvertable().isConvertable(s)) { + getLogger().info("Converting map!"); + this.b("menu.convertingLevel"); + this.getConvertable().convert(s, new ConvertProgressUpdater(this)); + } + } + + protected synchronized void b(String s) { + this.Q = s; + } + + protected void a(String s, String s1, long i, WorldType worldtype, String s2) { + this.a(s); + this.b("menu.loadingLevel"); + this.worldServer = new WorldServer[3]; + // this.h = new long[this.worldServer.length][100]; // CraftBukkit - Removed ticktime arrays + // IDataManager idatamanager = this.convertable.a(s, true); + // WorldData worlddata = idatamanager.getWorldData(); + /* CraftBukkit start - Removed worldsettings + WorldSettings worldsettings; + + if (worlddata == null) { + worldsettings = new WorldSettings(i, this.getGamemode(), this.getGenerateStructures(), this.isHardcore(), worldtype); + worldsettings.a(s2); + } else { + worldsettings = new WorldSettings(worlddata); + } + + if (this.L) { + worldsettings.a(); + } + // */ + int worldCount = 3; + + for (int j = 0; j < worldCount; ++j) { + WorldServer world; + int dimension = 0; + + if (j == 1) { + if (this.getAllowNether()) { + dimension = -1; + } else { + continue; + } + } + + if (j == 2) { + if (this.server.getAllowEnd()) { + dimension = 1; + } else { + continue; + } + } + + String worldType = Environment.getEnvironment(dimension).toString().toLowerCase(); + String name = (dimension == 0) ? s : s + "_" + worldType; + + org.bukkit.generator.ChunkGenerator gen = this.server.getGenerator(name); + WorldSettings worldsettings = new WorldSettings(i, this.getGamemode(), this.getGenerateStructures(), this.isHardcore(), worldtype); + worldsettings.a(s2); + + if (j == 0) { + IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), s1, true); + if (this.R()) { + world = new DemoWorldServer(this, idatamanager, s1, dimension, this.methodProfiler); + } else { + // world =, b0 to dimension, added Environment and gen + world = new WorldServer(this, idatamanager, s1, dimension, worldsettings, this.methodProfiler, Environment.getEnvironment(dimension), gen); + } + this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard()); + } else { + String dim = "DIM" + dimension; + + File newWorld = new File(new File(name), dim); + File oldWorld = new File(new File(s), dim); + + if ((!newWorld.isDirectory()) && (oldWorld.isDirectory())) { + MinecraftServer.i.info("---- Migration of old " + worldType + " folder required ----"); + MinecraftServer.i.info("Unfortunately due to the way that Minecraft implemented multiworld support in 1.6, Bukkit requires that you move your " + worldType + " folder to a new location in order to operate correctly."); + MinecraftServer.i.info("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Bukkit in the future."); + MinecraftServer.i.info("Attempting to move " + oldWorld + " to " + newWorld + "..."); + + if (newWorld.exists()) { + MinecraftServer.i.warn("A file or folder already exists at " + newWorld + "!"); + MinecraftServer.i.info("---- Migration of old " + worldType + " folder failed ----"); + } else if (newWorld.getParentFile().mkdirs()) { + if (oldWorld.renameTo(newWorld)) { + MinecraftServer.i.info("Success! To restore " + worldType + " in the future, simply move " + newWorld + " to " + oldWorld); + // Migrate world data too. + try { + com.google.common.io.Files.copy(new File(new File(s), "level.dat"), new File(new File(name), "level.dat")); + } catch (IOException exception) { + MinecraftServer.i.warn("Unable to migrate world data."); + } + MinecraftServer.i.info("---- Migration of old " + worldType + " folder complete ----"); + } else { + MinecraftServer.i.warn("Could not move folder " + oldWorld + " to " + newWorld + "!"); + MinecraftServer.i.info("---- Migration of old " + worldType + " folder failed ----"); + } + } else { + MinecraftServer.i.warn("Could not create path for " + newWorld + "!"); + MinecraftServer.i.info("---- Migration of old " + worldType + " folder failed ----"); + } + } + + IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), name, true); + // world =, b0 to dimension, s1 to name, added Environment and gen + world = new SecondaryWorldServer(this, idatamanager, name, dimension, worldsettings, this.worlds.get(0), this.methodProfiler, Environment.getEnvironment(dimension), gen); + } + + if (gen != null) { + world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld())); + } + + this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld())); + + world.addIWorldAccess(new WorldManager(this, world)); + if (!this.N()) { + world.getWorldData().setGameType(this.getGamemode()); + } + + this.worlds.add(world); + this.u.setPlayerFileData(this.worlds.toArray(new WorldServer[this.worlds.size()])); + // CraftBukkit end + } + + this.a(this.getDifficulty()); + this.g(); + } + + protected void g() { + boolean flag = true; + boolean flag1 = true; + boolean flag2 = true; + boolean flag3 = true; + int i = 0; + + this.b("menu.generatingTerrain"); + byte b0 = 0; + + // CraftBukkit start - fire WorldLoadEvent and handle whether or not to keep the spawn in memory + for (int m = 0; m < this.worlds.size(); ++m) { + WorldServer worldserver = this.worlds.get(m); + getLogger().info("Preparing start region for level " + m + " (Seed: " + worldserver.getSeed() + ")"); + if (!worldserver.getWorld().getKeepSpawnInMemory()) { + continue; + } + + ChunkCoordinates chunkcoordinates = worldserver.getSpawn(); + long j = ar(); + i = 0; + + for (int k = -192; k <= 192 && this.isRunning(); k += 16) { + for (int l = -192; l <= 192 && this.isRunning(); l += 16) { + long i1 = ar(); + + if (i1 - j > 1000L) { + this.a_("Preparing spawn area", i * 100 / 625); + j = i1; + } + + ++i; + worldserver.chunkProviderServer.getChunkAt(chunkcoordinates.x + k >> 4, chunkcoordinates.z + l >> 4); + } + } + } + + for (WorldServer world : this.worlds) { + this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(world.getWorld())); + } + // CraftBukkit end + this.n(); + } + + public abstract boolean getGenerateStructures(); + + public abstract EnumGamemode getGamemode(); + + public abstract EnumDifficulty getDifficulty(); + + public abstract boolean isHardcore(); + + public abstract int l(); + + public abstract boolean m(); + + protected void a_(String s, int i) { + this.e = s; + this.f = i; + // CraftBukkit - Use FQN to work around decompiler issue + MinecraftServer.i.info(s + ": " + i + "%"); + } + + protected void n() { + this.e = null; + this.f = 0; + + this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); // CraftBukkit + } + + protected void saveChunks(boolean flag) throws ExceptionWorldConflict { // CraftBukkit - added throws + if (SpigotConfig.disableSaving) return; // MineHQ + if (!this.M) { + // CraftBukkit start - fire WorldSaveEvent + // WorldServer[] aworldserver = this.worldServer; + int i = this.worlds.size(); + + for (int j = 0; j < i; ++j) { + WorldServer worldserver = this.worlds.get(j); + + if (worldserver != null) { + if (!flag) { + MinecraftServer.i.info("Saving chunks for level \'" + worldserver.getWorldData().getName() + "\'/" + worldserver.worldProvider.getName()); + } + + worldserver.save(true, (IProgressUpdate) null); + worldserver.saveLevel(); + + WorldSaveEvent event = new WorldSaveEvent(worldserver.getWorld()); + this.server.getPluginManager().callEvent(event); + // CraftBukkit end + } + } + } + } + + public void stop() throws ExceptionWorldConflict { // CraftBukkit - added throws + if (!this.M) { + getLogger().info("Stopping server"); + // CraftBukkit start + if (this.server != null) { + this.server.disablePlugins(); + } + // CraftBukkit end + + if (this.ai() != null) { + this.ai().b(); + } + + if (this.u != null) { + getLogger().info("Saving players"); + this.u.savePlayers(); + this.u.u(); + } + + if (this.worldServer != null) { + getLogger().info("Saving worlds"); + this.saveChunks(false); + + /* CraftBukkit start - Handled in saveChunks + for (int i = 0; i < this.worldServer.length; ++i) { + WorldServer worldserver = this.worldServer[i]; + + worldserver.saveLevel(); + } + // CraftBukkit end */ + } + + if (this.l.d()) { + this.l.e(); + } + // Spigot start + if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { + getLogger().info("Saving usercache.json"); + this.X.c(); + } + //Spigot end + + this.threadingManager.shutdown(); // Poweruser + } + } + + public String getServerIp() { + return this.serverIp; + } + + public void c(String s) { + this.serverIp = s; + } + + public boolean isRunning() { + return this.isRunning; + } + + public void safeShutdown() { + this.isRunning = false; + } + + // Spigot Start + private static double calcTps(double avg, double exp, double tps) { + return (avg * exp) + (tps * (1 - exp)); + } + // Spigot End + + public void run() { + try { + if (this.init()) { + long i = ar(); + long j = 0L; + + this.q.setMOTD(new ChatComponentText(this.motd)); + this.q.setServerInfo(new ServerPingServerData("1.7.10", 5)); + this.a(this.q); + + // Spigot start + Arrays.fill(recentTps, 20); + long lastTick = System.nanoTime(), catchupTime = 0, curTime, wait, tickSection = lastTick; + while (this.isRunning) { + curTime = System.nanoTime(); + wait = TICK_TIME - (curTime - lastTick) - catchupTime; + if (wait > 0) { + Thread.sleep(wait / 1000000); + catchupTime = 0; + continue; + } else { + catchupTime = Math.min(1000000000, Math.abs(wait)); + } + + if (MinecraftServer.currentTick++ % SAMPLE_INTERVAL == 0) { + double currentTps = 1E9 / (curTime - tickSection) * SAMPLE_INTERVAL; + recentTps[0] = calcTps(recentTps[0], 0.92, currentTps); // 1/exp(5sec/1min) + recentTps[1] = calcTps(recentTps[1], 0.9835, currentTps); // 1/exp(5sec/5min) + recentTps[2] = calcTps(recentTps[2], 0.9945, currentTps); // 1/exp(5sec/15min) + tickSection = curTime; + } + lastTick = curTime; + + Bukkit.getServer().getPluginManager().callEvent(new ServerTickStartEvent(MinecraftServer.currentTick)); + + this.u(); + + long endTime = System.nanoTime(); + double duration = (endTime - lastTick) / 1000000D; + long remaining = (TICK_TIME - (endTime - lastTick)) - catchupTime; + + Bukkit.getServer().getPluginManager().callEvent(new ServerTickEndEvent(MinecraftServer.currentTick, duration, remaining)); + + this.O = true; + } + // Spigot end + } else { + //this.a((CrashReport) null); // CraftBukkit - if init fails, stop the server + this.abnormalTermination = true; // SportBukkit + } + } catch (Throwable throwable) { + this.abnormalTermination = true; // SportBukkit + getLogger().error("Encountered an unexpected exception", throwable); + // Spigot Start + if (throwable.getCause() != null) { + getLogger().error("\tCause of unexpected exception was", throwable.getCause()); + } + // Spigot End + CrashReport crashreport = null; + + if (throwable instanceof ReportedException) { + crashreport = this.b(((ReportedException) throwable).a()); + } else { + crashreport = this.b(new CrashReport("Exception in server tick loop", throwable)); + } + + File file1 = new File(new File(this.s(), "crash-reports"), "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt"); + + if (crashreport.a(file1)) { + getLogger().error("This crash report has been saved to: " + file1.getAbsolutePath()); + } else { + getLogger().error("We were unable to save this crash report to disk."); + } + + this.a(crashreport); + } finally { + try { + org.spigotmc.WatchdogThread.doStop(); + this.stop(); + this.isStopped = true; + } catch (Throwable throwable1) { + getLogger().error("Exception stopping the server", throwable1); + } finally { + // CraftBukkit start - Restore terminal to original settings + try { + this.reader.getTerminal().restore(); + } catch (Exception e) { + } + // CraftBukkit end + this.t(); + } + } + } + + private void a(ServerPing serverping) { + File file1 = this.d("server-icon.png"); + + if (file1.isFile()) { + ByteBuf bytebuf = Unpooled.buffer(); + + try { + BufferedImage bufferedimage = ImageIO.read(file1); + + Validate.validState(bufferedimage.getWidth() == 64, "Must be 64 pixels wide", new Object[0]); + Validate.validState(bufferedimage.getHeight() == 64, "Must be 64 pixels high", new Object[0]); + ImageIO.write(bufferedimage, "PNG", new ByteBufOutputStream(bytebuf)); + ByteBuf bytebuf1 = Base64.encode(bytebuf); + + serverping.setFavicon("data:image/png;base64," + bytebuf1.toString(Charsets.UTF_8)); + } catch (Exception exception) { + getLogger().error("Couldn\'t load server icon", exception); + } finally { + bytebuf.release(); + } + } + } + + protected File s() { + return new File("."); + } + + protected void a(CrashReport crashreport) { + } + + protected void t() { + } + + protected void u() throws ExceptionWorldConflict { // CraftBukkit - added throws + SpigotTimings.serverTickTimer.startTiming(); // Spigot + long i = System.nanoTime(); + + ThreadingManager.getTickCounter().increaseTickCounter(); // Poweruser + ThreadingManager.startTickTimerTask(); // Poweruser + + ++this.ticks; + if (this.R) { + this.R = false; + this.methodProfiler.a = true; + this.methodProfiler.a(); + } + + this.methodProfiler.a("root"); + this.v(); + if (i - this.V >= 5000000000L) { + this.V = i; + this.q.setPlayerSample(new ServerPingPlayerSample(this.D(), this.C())); + GameProfile[] agameprofile = new GameProfile[Math.min(this.C(), 12)]; + int j = MathHelper.nextInt(this.r, 0, this.C() - agameprofile.length); + + for (int k = 0; k < agameprofile.length; ++k) { + agameprofile[k] = ((EntityPlayer) this.u.players.get(j + k)).getProfile(); + } + + Collections.shuffle(Arrays.asList(agameprofile)); + this.q.b().a(agameprofile); + } + + // Poweruser start + if (this.autoSaveManager.isActive()) { + SpigotTimings.worldSaveTimer.startTiming(); // Spigot + server.playerCommandState = true; + this.autoSaveManager.execute(); + server.playerCommandState = false; + SpigotTimings.worldSaveTimer.stopTiming(); // Spigot + } else if ((this.autosavePeriod > 0) && ((this.ticks % this.autosavePeriod) == 0)) { // CraftBukkit + this.autoSaveManager.reset(); + for (WorldServer worldserver : worlds) { + this.autoSaveManager.queueWorld(worldserver); + } + this.autoSaveManager.start(); + } + // Poweruser end + /* + SpigotTimings.worldSaveTimer.startTiming(); // Spigot + this.methodProfiler.a("save"); + this.u.savePlayers(); + // Spigot Start + // We replace this with saving each individual world as this.saveChunks(...) is broken, + // and causes the main thread to sleep for random amounts of time depending on chunk activity + // Also pass flag to only save modified chunks -- PaperSpigot + server.playerCommandState = true; + for (World world : worlds) { + world.getWorld().save(true); + } + server.playerCommandState = false; + // this.saveChunks(true); + // Spigot End + this.methodProfiler.b(); + SpigotTimings.worldSaveTimer.stopTiming(); // Spigot + } + */ + + this.methodProfiler.a("tallying"); + this.g[this.ticks % 100] = System.nanoTime() - i; + this.methodProfiler.b(); + this.methodProfiler.a("snooper"); + if (getSnooperEnabled() && !this.l.d() && this.ticks > 100) { // Spigot + this.l.a(); + } + + if (getSnooperEnabled() && this.ticks % 6000 == 0) { // Spigot + this.l.b(); + } + + this.methodProfiler.b(); + this.methodProfiler.b(); + org.spigotmc.WatchdogThread.tick(); // Spigot + SpigotTimings.serverTickTimer.stopTiming(); // Spigot + this.lastTickTime = (System.nanoTime() - i) / 1000000F; + ThreadingManager.cancelTimerTask(this.lastTickTime); // Poweruser + net.valorhcf.timings.ExtendedCustomTimingsHandler.tick(); // Poweruser + } + + public static List PRE_ENTITY_TRACKER_RUNNABLE_LIST = new ArrayList<>(); + public static List POST_ENTITY_TRACKER_RUNNABLE_LIST = new ArrayList<>(); + + public void v() { + // MineHQ start - tick connections earlier + // We tick the playerList here too so updated + // pings are sent out as soon as possible. + SpigotTimings.connectionTimer.startTiming(); // Spigot + this.ai().c(); + SpigotTimings.connectionTimer.stopTiming(); // Spigot + SpigotTimings.playerListTimer.startTiming(); // Spigot + this.u.tick(); + SpigotTimings.playerListTimer.stopTiming(); // Spigot + // MineHQ end + this.methodProfiler.a("levels"); + + SpigotTimings.schedulerTimer.startTiming(); // Spigot + // CraftBukkit start + this.server.getScheduler().mainThreadHeartbeat(this.ticks); + SpigotTimings.schedulerTimer.stopTiming(); // Spigot + + // Run tasks that are waiting on processing + SpigotTimings.processQueueTimer.startTiming(); // Spigot + while (!processQueue.isEmpty()) { + processQueue.remove().run(); + } + SpigotTimings.processQueueTimer.stopTiming(); // Spigot + + SpigotTimings.chunkIOTickTimer.startTiming(); // Spigot + org.bukkit.craftbukkit.chunkio.ChunkIOExecutor.tick(); + SpigotTimings.chunkIOTickTimer.stopTiming(); // Spigot + + SpigotTimings.timeUpdateTimer.startTiming(); // Spigot + // Send time updates to everyone, it will get the right time from the world the player is in. + if (this.ticks % 20 == 0) { + for (int i = 0; i < this.getPlayerList().players.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) this.getPlayerList().players.get(i); + entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(entityplayer.world.getTime(), entityplayer.getPlayerTime(), entityplayer.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time + } + } + SpigotTimings.timeUpdateTimer.stopTiming(); // Spigot + + int i; + + entities = 0; + activeEntities = 0; + for (i = 0; i < this.worlds.size(); ++i) { + long j = System.nanoTime(); + + // if (i == 0 || this.getAllowNether()) { + WorldServer worldserver = this.worlds.get(i); + if (!worldserver.checkTicking()) continue; // MineHQ + + this.methodProfiler.a(worldserver.getWorldData().getName()); + this.methodProfiler.a("pools"); + this.methodProfiler.b(); + /* Drop global time updates + if (this.ticks % 20 == 0) { + this.methodProfiler.a("timeSync"); + this.t.a(new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")), worldserver.worldProvider.dimension); + this.methodProfiler.b(); + } + // CraftBukkit end */ + + this.methodProfiler.a("tick"); + + CrashReport crashreport; + + try { + worldserver.timings.doTick.startTiming(); // Spigot + worldserver.doTick(); + worldserver.timings.doTick.stopTiming(); // Spigot + } catch (Throwable throwable) { + // Spigot Start + try { + crashreport = CrashReport.a(throwable, "Exception ticking world"); + } catch (Throwable t) { + throw new RuntimeException("Error generating crash report", t); + } + // Spigot End + worldserver.a(crashreport); + throw new ReportedException(crashreport); + } + + try { + worldserver.timings.tickEntities.startTiming(); // Spigot + worldserver.tickEntities(); + worldserver.timings.tickEntities.stopTiming(); // Spigot + } catch (Throwable throwable1) { + // Spigot Start + try { + crashreport = CrashReport.a(throwable1, "Exception ticking world entities"); + } catch (Throwable t) { + throw new RuntimeException("Error generating crash report", t); + } + // Spigot End + worldserver.a(crashreport); + throw new ReportedException(crashreport); + } + + this.methodProfiler.b(); + this.methodProfiler.a("tracker"); + worldserver.timings.tracker.startTiming(); // Spigot + + // Rowin was here - EntityTracker + PRE_ENTITY_TRACKER_RUNNABLE_LIST.forEach(Runnable::run); + worldserver.getTracker().updatePlayers(); + POST_ENTITY_TRACKER_RUNNABLE_LIST.forEach(Runnable::run); + + worldserver.timings.tracker.stopTiming(); // Spigot + this.methodProfiler.b(); + this.methodProfiler.b(); + worldserver.explosionDensityCache.clear(); // PaperSpigot - Optimize explosions + // } // CraftBukkit + + // this.h[i][this.ticks % 100] = System.nanoTime() - j; // CraftBukkit + } + + this.methodProfiler.c("connection"); + // MineHQ start - move up + /* + SpigotTimings.connectionTimer.startTiming(); // Spigot + this.ai().c(); + SpigotTimings.connectionTimer.stopTiming(); // Spigot + this.methodProfiler.c("players"); + SpigotTimings.playerListTimer.startTiming(); // Spigot + this.u.tick(); + SpigotTimings.playerListTimer.stopTiming(); // Spigot + */ + // MineHQ end + this.methodProfiler.c("tickables"); + + SpigotTimings.tickablesTimer.startTiming(); // Spigot + for (i = 0; i < this.n.size(); ++i) { + ((IUpdatePlayerListBox) this.n.get(i)).a(); + } + SpigotTimings.tickablesTimer.stopTiming(); // Spigot + + this.methodProfiler.b(); + } + + public boolean getAllowNether() { + return true; + } + + public void a(IUpdatePlayerListBox iupdateplayerlistbox) { + this.n.add(iupdateplayerlistbox); + } + + public static void main(final OptionSet options) { // CraftBukkit - replaces main(String[] astring) + DispenserRegistry.b(); + org.spigotmc.ProtocolInjector.inject(); + + try { + /* CraftBukkit start - Replace everything + boolean flag = true; + String s = null; + String s1 = "."; + String s2 = null; + boolean flag1 = false; + boolean flag2 = false; + int i = -1; + + for (int j = 0; j < astring.length; ++j) { + String s3 = astring[j]; + String s4 = j == astring.length - 1 ? null : astring[j + 1]; + boolean flag3 = false; + + if (!s3.equals("nogui") && !s3.equals("--nogui")) { + if (s3.equals("--port") && s4 != null) { + flag3 = true; + + try { + i = Integer.parseInt(s4); + } catch (NumberFormatException numberformatexception) { + ; + } + } else if (s3.equals("--singleplayer") && s4 != null) { + flag3 = true; + s = s4; + } else if (s3.equals("--universe") && s4 != null) { + flag3 = true; + s1 = s4; + } else if (s3.equals("--world") && s4 != null) { + flag3 = true; + s2 = s4; + } else if (s3.equals("--demo")) { + flag1 = true; + } else if (s3.equals("--bonusChest")) { + flag2 = true; + } + } else { + flag = false; + } + + if (flag3) { + ++j; + } + } + + DedicatedServer dedicatedserver = new DedicatedServer(new File(s1)); + + if (s != null) { + dedicatedserver.j(s); + } + + if (s2 != null) { + dedicatedserver.k(s2); + } + + if (i >= 0) { + dedicatedserver.setPort(i); + } + + if (flag1) { + dedicatedserver.b(true); + } + + if (flag2) { + dedicatedserver.c(true); + } + + if (flag) { + dedicatedserver.aD(); + } + // */ + + DedicatedServer dedicatedserver = new DedicatedServer(options); + + if (options.has("port")) { + int port = (Integer) options.valueOf("port"); + if (port > 0) { + dedicatedserver.setPort(port); + } + } + + if (options.has("universe")) { + dedicatedserver.universe = (File) options.valueOf("universe"); + } + + if (options.has("world")) { + dedicatedserver.k((String) options.valueOf("world")); + } + + dedicatedserver.primaryThread.start(); + // Runtime.getRuntime().addShutdownHook(new ThreadShutdown("Server Shutdown Thread", dedicatedserver)); + // CraftBukkit end + } catch (Exception exception) { + getLogger().fatal("Failed to start the minecraft server", exception); + System.exit(1); // Sportbukkit + } + } + + public void x() { + // (new ThreadServerApplication(this, "Server thread")).start(); // CraftBukkit - prevent abuse + } + + public File d(String s) { + return new File(this.s(), s); + } + + public void info(String s) { + getLogger().info(s); + } + + public void warning(String s) { + getLogger().warn(s); + } + + public WorldServer getWorldServer(int i) { + // CraftBukkit start + for (WorldServer world : this.worlds) { + if (world.dimension == i) { + return world; + } + } + + return this.worlds.get(0); + // CraftBukkit end + } + + public String y() { + return this.serverIp; + } + + public int z() { + return this.t; + } + + public String A() { + return this.motd; + } + + public String getVersion() { + return "1.7.10"; + } + + public int C() { + return this.u.getPlayerCount(); + } + + public int D() { + return this.u.getMaxPlayers(); + } + + public String[] getPlayers() { + return this.u.f(); + } + + public GameProfile[] F() { + return this.u.g(); + } + + public String getPlugins() { + // CraftBukkit start - Whole method + StringBuilder result = new StringBuilder(); + org.bukkit.plugin.Plugin[] plugins = server.getPluginManager().getPlugins(); + + result.append(server.getName()); + result.append(" on Bukkit "); + result.append(server.getBukkitVersion()); + + if (plugins.length > 0 && this.server.getQueryPlugins()) { + result.append(": "); + + for (int i = 0; i < plugins.length; i++) { + if (i > 0) { + result.append("; "); + } + + result.append(plugins[i].getDescription().getName()); + result.append(" "); + result.append(plugins[i].getDescription().getVersion().replaceAll(";", ",")); + } + } + + return result.toString(); + // CraftBukkit end + } + + // CraftBukkit start - fire RemoteServerCommandEvent + public String g(final String s) { // final parameter + Waitable waitable = new Waitable() { + @Override + protected String evaluate() { + RemoteControlCommandListener.instance.e(); + // Event changes start + RemoteServerCommandEvent event = new RemoteServerCommandEvent(MinecraftServer.this.remoteConsole, s); + MinecraftServer.this.server.getPluginManager().callEvent(event); + // Event changes end + ServerCommand servercommand = new ServerCommand(event.getCommand(), RemoteControlCommandListener.instance); + MinecraftServer.this.server.dispatchServerCommand(MinecraftServer.this.remoteConsole, servercommand); // CraftBukkit + // this.o.a(RemoteControlCommandListener.instance, s); + return RemoteControlCommandListener.instance.f(); + } + }; + processQueue.add(waitable); + try { + return waitable.get(); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Exception processing rcon command " + s, e.getCause()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Maintain interrupted state + throw new RuntimeException("Interrupted processing rcon command " + s, e); + } + // CraftBukkit end + } + + public boolean isDebugging() { + return this.getPropertyManager().getBoolean("debug", false); // CraftBukkit - don't hardcode + } + + public void h(String s) { + getLogger().error(s); + } + + public void i(String s) { + if (this.isDebugging()) { + getLogger().info(s); + } + } + + public String getServerModName() { + return "MineHQ Cloud v0.2.1 Alpha"; // MineHQ - ayy lmao + } + + public CrashReport b(CrashReport crashreport) { + crashreport.g().a("Profiler Position", (Callable) (new CrashReportProfilerPosition(this))); + if (this.worlds != null && this.worlds.size() > 0 && this.worlds.get(0) != null) { // CraftBukkit + crashreport.g().a("Vec3 Pool Size", (Callable) (new CrashReportVec3DPoolSize(this))); + } + + if (this.u != null) { + crashreport.g().a("Player Count", (Callable) (new CrashReportPlayerCount(this))); + } + + return crashreport; + } + + public List a(ICommandListener icommandlistener, String s) { + // CraftBukkit start - Allow tab-completion of Bukkit commands + /* + ArrayList arraylist = new ArrayList(); + + if (s.startsWith("/")) { + s = s.substring(1); + boolean flag = !s.contains(" "); + List list = this.o.b(icommandlistener, s); + + if (list != null) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + String s1 = (String) iterator.next(); + + if (flag) { + arraylist.add("/" + s1); + } else { + arraylist.add(s1); + } + } + } + + return arraylist; + } else { + String[] astring = s.split(" ", -1); + String s2 = astring[astring.length - 1]; + String[] astring1 = this.u.f(); + int i = astring1.length; + + for (int j = 0; j < i; ++j) { + String s3 = astring1[j]; + + if (CommandAbstract.a(s2, s3)) { + arraylist.add(s3); + } + } + + return arraylist; + } + */ + return this.server.tabComplete(icommandlistener, s); + // CraftBukkit end + } + + public static MinecraftServer getServer() { + return j; + } + + public String getName() { + return "Server"; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.console.sendMessage(ichatbasecomponent.c()); // CraftBukkit - we want coloured and pretty messages too! + } + + public boolean a(int i, String s) { + return true; + } + + public ICommandHandler getCommandHandler() { + return this.o; + } + + public KeyPair K() { + return this.G; + } + + public int L() { + return this.t; + } + + public void setPort(int i) { + this.t = i; + } + + public String M() { + return this.H; + } + + public void j(String s) { + this.H = s; + } + + public boolean N() { + return this.H != null; + } + + public String O() { + return this.I; + } + + public void k(String s) { + this.I = s; + } + + public void a(KeyPair keypair) { + this.G = keypair; + } + + public void a(EnumDifficulty enumdifficulty) { + // CraftBukkit start - Use worlds list for iteration + for (int j = 0; j < this.worlds.size(); ++j) { + WorldServer worldserver = this.worlds.get(j); + // CraftBukkit end + + if (worldserver != null) { + if (worldserver.getWorldData().isHardcore()) { + worldserver.difficulty = EnumDifficulty.HARD; + worldserver.setSpawnFlags(true, true); + } else if (this.N()) { + worldserver.difficulty = enumdifficulty; + worldserver.setSpawnFlags(worldserver.difficulty != EnumDifficulty.PEACEFUL, true); + } else { + worldserver.difficulty = enumdifficulty; + worldserver.setSpawnFlags(this.getSpawnMonsters(), this.spawnAnimals); + } + } + } + } + + protected boolean getSpawnMonsters() { + return true; + } + + public boolean R() { + return this.demoMode; + } + + public void b(boolean flag) { + this.demoMode = flag; + } + + public void c(boolean flag) { + this.L = flag; + } + + public Convertable getConvertable() { + return this.convertable; + } + + public void U() { + this.M = true; + this.getConvertable().d(); + + // CraftBukkit start + for (int i = 0; i < this.worlds.size(); ++i) { + WorldServer worldserver = this.worlds.get(i); + // CraftBukkit end + + if (worldserver != null) { + worldserver.saveLevel(); + } + } + + this.getConvertable().e(this.worlds.get(0).getDataManager().g()); // CraftBukkit + this.safeShutdown(); + } + + public String getResourcePack() { + return this.N; + } + + public void setTexturePack(String s) { + this.N = s; + } + + public void a(MojangStatisticsGenerator mojangstatisticsgenerator) { + mojangstatisticsgenerator.a("whitelist_enabled", Boolean.valueOf(false)); + mojangstatisticsgenerator.a("whitelist_count", Integer.valueOf(0)); + mojangstatisticsgenerator.a("players_current", Integer.valueOf(this.C())); + mojangstatisticsgenerator.a("players_max", Integer.valueOf(this.D())); + mojangstatisticsgenerator.a("players_seen", Integer.valueOf(this.u.getSeenPlayers().length)); + mojangstatisticsgenerator.a("uses_auth", Boolean.valueOf(this.onlineMode)); + mojangstatisticsgenerator.a("gui_state", this.ak() ? "enabled" : "disabled"); + mojangstatisticsgenerator.a("run_time", Long.valueOf((ar() - mojangstatisticsgenerator.g()) / 60L * 1000L)); + mojangstatisticsgenerator.a("avg_tick_ms", Integer.valueOf((int) (MathHelper.a(this.g) * 1.0E-6D))); + int i = 0; + + // CraftBukkit start - use worlds list for iteration + for (int j = 0; j < this.worlds.size(); ++j) { + WorldServer worldserver = this.worlds.get(j); + if (worldServer != null) { + // CraftBukkit end + WorldData worlddata = worldserver.getWorldData(); + + mojangstatisticsgenerator.a("world[" + i + "][dimension]", Integer.valueOf(worldserver.worldProvider.dimension)); + mojangstatisticsgenerator.a("world[" + i + "][mode]", worlddata.getGameType()); + mojangstatisticsgenerator.a("world[" + i + "][difficulty]", worldserver.difficulty); + mojangstatisticsgenerator.a("world[" + i + "][hardcore]", Boolean.valueOf(worlddata.isHardcore())); + mojangstatisticsgenerator.a("world[" + i + "][generator_name]", worlddata.getType().name()); + mojangstatisticsgenerator.a("world[" + i + "][generator_version]", Integer.valueOf(worlddata.getType().getVersion())); + mojangstatisticsgenerator.a("world[" + i + "][height]", Integer.valueOf(this.E)); + mojangstatisticsgenerator.a("world[" + i + "][chunks_loaded]", Integer.valueOf(worldserver.L().getLoadedChunks())); + ++i; + } + } + + mojangstatisticsgenerator.a("worlds", Integer.valueOf(i)); + } + + public void b(MojangStatisticsGenerator mojangstatisticsgenerator) { + mojangstatisticsgenerator.b("singleplayer", Boolean.valueOf(this.N())); + mojangstatisticsgenerator.b("server_brand", this.getServerModName()); + mojangstatisticsgenerator.b("gui_supported", GraphicsEnvironment.isHeadless() ? "headless" : "supported"); + mojangstatisticsgenerator.b("dedicated", Boolean.valueOf(this.X())); + } + + public boolean getSnooperEnabled() { + return true; + } + + public abstract boolean X(); + + public boolean getOnlineMode() { + return this.server.getOnlineMode(); // CraftBukkit + } + + public void setOnlineMode(boolean flag) { + this.onlineMode = flag; + } + + public boolean getSpawnAnimals() { + return this.spawnAnimals; + } + + public void setSpawnAnimals(boolean flag) { + this.spawnAnimals = flag; + } + + public boolean getSpawnNPCs() { + return this.spawnNPCs; + } + + public void setSpawnNPCs(boolean flag) { + this.spawnNPCs = flag; + } + + public boolean getPvP() { + return this.pvpMode; + } + + public void setPvP(boolean flag) { + this.pvpMode = flag; + } + + public boolean getAllowFlight() { + return this.allowFlight; + } + + public void setAllowFlight(boolean flag) { + this.allowFlight = flag; + } + + public abstract boolean getEnableCommandBlock(); + + public String getMotd() { + return this.motd; + } + + public void setMotd(String s) { + this.motd = s; + } + + public int getMaxBuildHeight() { + return this.E; + } + + public void c(int i) { + this.E = i; + } + + public boolean isStopped() { + return this.isStopped; + } + + public PlayerList getPlayerList() { + return this.u; + } + + public void a(PlayerList playerlist) { + this.u = playerlist; + } + + public void a(EnumGamemode enumgamemode) { + // CraftBukkit start - use worlds list for iteration + for (int i = 0; i < this.worlds.size(); ++i) { + getServer().worlds.get(i).getWorldData().setGameType(enumgamemode); + // CraftBukkit end + } + } + + // Spigot Start + public ServerConnection getServerConnection() { + return this.p; + } + + // Spigot End + public ServerConnection ai() { + return (this.p) == null ? this.p = new ServerConnection(this) : this.p; // Spigot + } + + public boolean ak() { + return false; + } + + public abstract String a(EnumGamemode enumgamemode, boolean flag); + + public int al() { + return this.ticks; + } + + public void am() { + this.R = true; + } + + public ChunkCoordinates getChunkCoordinates() { + return new ChunkCoordinates(0, 0, 0); + } + + public World getWorld() { + return this.worlds.get(0); // CraftBukkit + } + + public int getSpawnProtection() { + return 16; + } + + public boolean a(World world, int i, int j, int k, EntityHuman entityhuman) { + return false; + } + + public void setForceGamemode(boolean flag) { + this.S = flag; + } + + public boolean getForceGamemode() { + return this.S; + } + + public Proxy aq() { + return this.d; + } + + public static long ar() { + return System.currentTimeMillis(); + } + + public int getIdleTimeout() { + return this.F; + } + + public void setIdleTimeout(int i) { + this.F = i; + } + + public IChatBaseComponent getScoreboardDisplayName() { + return new ChatComponentText(this.getName()); + } + + public boolean at() { + return true; + } + + public MinecraftSessionService av() { + return this.U; + } + + public GameProfileRepository getGameProfileRepository() { + return this.W; + } + + public UserCache getUserCache() { + return this.X; + } + + public ServerPing ay() { + return this.q; + } + + public void az() { + this.V = 0L; + } + + private static Logger i; + + public static Logger getLogger() { + if (i == null) { + i = LogManager.getLogger(); + } + return i; + } + + public static PlayerList a(MinecraftServer minecraftserver) { + return minecraftserver.u; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/MobEffectAttackDamage.java b/vspigot-server/src/main/java/net/minecraft/server/MobEffectAttackDamage.java new file mode 100644 index 0000000..b1ac919 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/MobEffectAttackDamage.java @@ -0,0 +1,14 @@ +package net.minecraft.server; + +import org.github.paperspigot.PaperSpigotConfig; + +public class MobEffectAttackDamage extends MobEffectList { + + protected MobEffectAttackDamage(int i, boolean flag, int j) { + super(i, flag, j); + } + + public double a(int i, AttributeModifier attributemodifier) { + return this.id == MobEffectList.WEAKNESS.id ? (double) (PaperSpigotConfig.weaknessEffectModifier * (float) (i + 1)) : PaperSpigotConfig.strengthEffectModifier * (double) (i + 1); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/MobEffectList.java b/vspigot-server/src/main/java/net/minecraft/server/MobEffectList.java new file mode 100644 index 0000000..369bea2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/MobEffectList.java @@ -0,0 +1,274 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import java.util.Map.Entry; + +import net.minecraft.util.com.google.common.collect.Maps; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.potion.CraftPotionEffectType; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; +// CraftBukkit end + +// Kohi start +import java.io.File; +import org.bukkit.configuration.file.YamlConfiguration; +// Kohi end + +public class MobEffectList { + + private static final File CONFIG_FILE = new File("config/server", "effects.yml"); // MineHQ - Dedicated config directory + protected static YamlConfiguration config = YamlConfiguration.loadConfiguration(CONFIG_FILE); + + public static final MobEffectList[] byId = new MobEffectList[32]; + public static final MobEffectList b = null; + public static final MobEffectList FASTER_MOVEMENT = (new MobEffectList(1, false, 8171462)).b("potion.moveSpeed").b(0, 0).a(GenericAttributes.d, "91AEAA56-376B-4498-935B-2F7F68070635", 0.20000000298023224D, 2); + public static final MobEffectList SLOWER_MOVEMENT = (new MobEffectList(2, true, 5926017)).b("potion.moveSlowdown").b(1, 0).a(GenericAttributes.d, "7107DE5E-7CE8-4030-940E-514C1F160890", -0.15000000596046448D, 2); + public static final MobEffectList FASTER_DIG = (new MobEffectList(3, false, 14270531)).b("potion.digSpeed").b(2, 0).a(1.5D); + public static final MobEffectList SLOWER_DIG = (new MobEffectList(4, true, 4866583)).b("potion.digSlowDown").b(3, 0); + public static final MobEffectList INCREASE_DAMAGE = (new MobEffectAttackDamage(5, false, 9643043)).b("potion.damageBoost").b(4, 0).a(GenericAttributes.e, "648D7064-6A60-4F59-8ABE-C2C23A6DD7A9", 3.0D, 2).setAmount(1.3F); + public static final MobEffectList HEAL = (new InstantMobEffect(6, false, 16262179)).b("potion.heal").setAmount(4); + public static final MobEffectList HARM = (new InstantMobEffect(7, true, 4393481)).b("potion.harm").setAmount(6); + public static final MobEffectList JUMP = (new MobEffectList(8, false, 7889559)).b("potion.jump").b(2, 1); + + + public static final MobEffectList CONFUSION = (new MobEffectList(9, true, 5578058)).b("potion.confusion").b(3, 1).a(0.25D); + public static final MobEffectList REGENERATION = (new MobEffectList(10, false, 13458603)).b("potion.regeneration").b(7, 0).a(0.25D).setTickRate(50); + public static final MobEffectList RESISTANCE = (new MobEffectList(11, false, 10044730)).b("potion.resistance").b(6, 1); + public static final MobEffectList FIRE_RESISTANCE = (new MobEffectList(12, false, 14981690)).b("potion.fireResistance").b(7, 1); + public static final MobEffectList WATER_BREATHING = (new MobEffectList(13, false, 3035801)).b("potion.waterBreathing").b(0, 2); + public static final MobEffectList INVISIBILITY = (new MobEffectList(14, false, 8356754)).b("potion.invisibility").b(0, 1); + public static final MobEffectList BLINDNESS = (new MobEffectList(15, true, 2039587)).b("potion.blindness").b(5, 1).a(0.25D); + public static final MobEffectList NIGHT_VISION = (new MobEffectList(16, false, 2039713)).b("potion.nightVision").b(4, 1); + public static final MobEffectList HUNGER = (new MobEffectList(17, true, 5797459)).b("potion.hunger").b(1, 1); + public static final MobEffectList WEAKNESS = (new MobEffectAttackDamage(18, true, 4738376)).b("potion.weakness").b(5, 0).a(GenericAttributes.e, "22653B89-116E-49DC-9B6B-9971489B5BE5", 2.0D, 0).setAmount(-0.5F); + public static final MobEffectList POISON = (new MobEffectList(19, true, 5149489)).b("potion.poison").b(6, 0).a(0.25D).setTickRate(25); + public static final MobEffectList WITHER = (new MobEffectList(20, true, 3484199)).b("potion.wither").b(1, 2).a(0.25D).setTickRate(40); + public static final MobEffectList HEALTH_BOOST = (new MobEffectHealthBoost(21, false, 16284963)).b("potion.healthBoost").b(2, 2).a(GenericAttributes.maxHealth, "5D6F0BA2-1186-46AC-B896-C61C5CEE99CC", 4.0D, 0); + public static final MobEffectList ABSORPTION = (new MobEffectAbsorption(22, false, 2445989)).b("potion.absorption").b(2, 2); + public static final MobEffectList SATURATION = (new InstantMobEffect(23, false, 16262179)).b("potion.saturation"); + public static final MobEffectList z = null; + public static final MobEffectList A = null; + public static final MobEffectList B = null; + public static final MobEffectList C = null; + public static final MobEffectList D = null; + public static final MobEffectList E = null; + public static final MobEffectList F = null; + public static final MobEffectList G = null; + public final int id; + private final Map I = Maps.newHashMap(); + private final boolean J; + private final int K; + private String L = ""; + private int M = -1; + private double N; + private boolean O; + private String configName; // Kohi + protected Float amount; // Kohi: amount this potion does, example: HP healed + private Integer operation; // Kohi: attribute modifier operation + private Integer tickRate; // Kohi: how often the potion does its tick + + protected MobEffectList(int i, boolean flag, int j) { + this.id = i; + byId[i] = this; + this.J = flag; + if (flag) { + this.N = 0.5D; + } else { + this.N = 1.0D; + } + + this.K = j; + + // CraftBukkit start + CraftPotionEffectType craftEffect = new CraftPotionEffectType(this); + org.bukkit.potion.PotionEffectType.registerPotionEffectType(craftEffect); + // CraftBukkit end + + configName = craftEffect.getName().toLowerCase().replace('_', '-'); + + if (config.isDouble(configName + ".amount") || config.isInt(configName + ".amount")) { + amount = (float) config.getDouble(configName + ".amount"); + System.out.println(configName + ".amount = " + amount); + } + + if (config.isInt(configName + ".operation")) { + operation = config.getInt(configName + ".operation"); + System.out.println(configName + ".operation = " + operation); + } + + if (config.isInt(configName + ".tick-rate")) { + tickRate = config.getInt(configName + ".tick-rate"); + System.out.println(configName + ".tick-rate = " + tickRate); + } + } + + private MobEffectList setAmount(float amount) { + if (this.amount == null) { + this.amount = amount; + } + return this; + } + + private MobEffectList setTickRate(int tickRate) { + if (this.tickRate == null) { + this.tickRate = tickRate; + } + return this; + } + + protected MobEffectList b(int i, int j) { + this.M = i + j * 8; + return this; + } + + public int getId() { + return this.id; + } + + public void tick(EntityLiving entityliving, int i) { + if (this.id == REGENERATION.id) { + if (entityliving.getHealth() < entityliving.getMaxHealth()) { + entityliving.heal(1.0F, RegainReason.MAGIC_REGEN); // CraftBukkit + } + } else if (this.id == POISON.id) { + if (entityliving.getHealth() > 1.0F) { + entityliving.damageEntity(CraftEventFactory.POISON, 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON + } + } else if (this.id == WITHER.id) { + entityliving.damageEntity(DamageSource.WITHER, 1.0F); + } else if (this.id == HUNGER.id && entityliving instanceof EntityHuman) { + ((EntityHuman) entityliving).applyExhaustion(0.025F * (float) (i + 1)); + } else if (this.id == SATURATION.id && entityliving instanceof EntityHuman) { + if (!entityliving.world.isStatic) { + // CraftBukkit start + EntityHuman entityhuman = (EntityHuman) entityliving; + int oldFoodLevel = entityhuman.getFoodData().foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = CraftEventFactory.callFoodLevelChangeEvent(entityhuman, i + 1 + oldFoodLevel); + + if (!event.isCancelled()) { + entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F); + } + + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel)); + // CraftBukkit end + } + } else if ((this.id != HEAL.id || entityliving.aR()) && (this.id != HARM.id || !entityliving.aR())) { + if (this.id == HARM.id && !entityliving.aR() || this.id == HEAL.id && entityliving.aR()) { + entityliving.damageEntity(DamageSource.MAGIC, (float) (amount.intValue() << i)); + } + } else { + entityliving.heal((float) Math.max(amount.intValue() << i, 0), RegainReason.MAGIC); // CraftBukkit + } + } + + public void applyInstantEffect(EntityLiving entityliving, EntityLiving entityliving1, int i, double d0) { + // CraftBukkit start - Delegate; we need EntityPotion + applyInstantEffect(entityliving, entityliving1, i, d0, null); + } + + public void applyInstantEffect(EntityLiving entityliving, EntityLiving entityliving1, int i, double d0, EntityPotion potion) { + // CraftBukkit end + int j; + + if ((this.id != HEAL.id || entityliving1.aR()) && (this.id != HARM.id || !entityliving1.aR())) { + if (this.id == HARM.id && !entityliving1.aR() || this.id == HEAL.id && entityliving1.aR()) { + j = (int) (d0 * (double) (amount.intValue() << i) + 0.5D); + if (entityliving == null) { + entityliving1.damageEntity(DamageSource.MAGIC, (float) j); + } else { + // CraftBukkit - The "damager" needs to be the potion + entityliving1.damageEntity(DamageSource.b(potion != null ? potion : entityliving1, entityliving), (float) j); + } + } + } else { + j = (int) (d0 * (double) (amount.intValue() << i) + 0.5D); + entityliving1.heal((float) j, RegainReason.MAGIC); // CraftBukkit + } + } + + public boolean isInstant() { + return false; + } + + public boolean a(int i, int j) { + int k; + + if (this.id == REGENERATION.id || this.id == POISON.id || this.id == WITHER.id) { + k = tickRate >> j; + return k > 0 ? i % k == 0 : true; + } else { + return this.id == HUNGER.id; + } + } + + public MobEffectList b(String s) { + this.L = s; + return this; + } + + public String a() { + return this.L; + } + + protected MobEffectList a(double d0) { + this.N = d0; + return this; + } + + public double getDurationModifier() { + return this.N; + } + + public boolean i() { + return this.O; + } + + public int j() { + return this.K; + } + + public MobEffectList a(IAttribute iattribute, String s, double d0, int i) { + AttributeModifier attributemodifier = new AttributeModifier(UUID.fromString(s), this.a(), d0, i); + + this.I.put(iattribute, attributemodifier); + return this; + } + + public void a(EntityLiving entityliving, AttributeMapBase attributemapbase, int i) { + Iterator iterator = this.I.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + AttributeInstance attributeinstance = attributemapbase.a((IAttribute) entry.getKey()); + + if (attributeinstance != null) { + attributeinstance.b((AttributeModifier) entry.getValue()); + } + } + } + + public void b(EntityLiving entityliving, AttributeMapBase attributemapbase, int i) { + Iterator iterator = this.I.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + AttributeInstance attributeinstance = attributemapbase.a((IAttribute) entry.getKey()); + + if (attributeinstance != null) { + AttributeModifier attributemodifier = (AttributeModifier) entry.getValue(); + + attributeinstance.b(attributemodifier); + attributeinstance.a(new AttributeModifier(attributemodifier.a(), this.a() + " " + i, this.a(i, attributemodifier), operation == null ? attributemodifier.c() : operation)); + } + } + } + + public double a(int i, AttributeModifier attributemodifier) { + return attributemodifier.d() * (double) (i + 1); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/MobSpawnerAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/MobSpawnerAbstract.java new file mode 100644 index 0000000..03e755e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/MobSpawnerAbstract.java @@ -0,0 +1,300 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.SpawnerSpawnEvent; +// CraftBukkit end + +public abstract class MobSpawnerAbstract { + + public int spawnDelay = 20; + private String mobName = "Pig"; + private List mobs; + private TileEntityMobSpawnerData spawnData; + public double c; + public double d; + private int minSpawnDelay = 200; + private int maxSpawnDelay = 800; + private int spawnCount = 4; + private Entity j; + private int maxNearbyEntities = 6; + private int requiredPlayerRange = 16; + private int spawnRange = 4; + + public MobSpawnerAbstract() {} + + public String getMobName() { + if (this.i() == null) { + if (this.mobName.equals("Minecart")) { + this.mobName = "MinecartRideable"; + } + + return this.mobName; + } else { + return this.i().c; + } + } + + public void setMobName(String s) { + this.mobName = s; + } + + public boolean f() { + return this.a().findNearbyPlayerWhoAffectsSpawning((double) this.b() + 0.5D, (double) this.c() + 0.5D, (double) this.d() + 0.5D, (double) this.requiredPlayerRange) != null; // PaperSpigot + } + + public void g() { + if (this.spawnDelay == -1) { + this.j(); + } + + // Kohi - check spawn delay before player distance + if (this.spawnDelay > 0) { + --this.spawnDelay; + return; + } + + if (!this.f()) { + this.j(); + return; + } + + for (int i = 0; i < this.spawnCount; ++i) { + Entity entity = EntityTypes.createEntityByName(this.getMobName(), this.a()); + + if (entity == null) { + return; + } + + int j = this.a().a(entity.getClass(), AxisAlignedBB.a((double) this.b(), (double) this.c(), (double) this.d(), (double) (this.b() + 1), (double) (this.c() + 1), (double) (this.d() + 1)).grow((double) (this.spawnRange * 2), 4.0D, (double) (this.spawnRange * 2))).size(); + + if (j >= this.maxNearbyEntities) { + this.j(); + return; + } + + double d0 = (double) this.b() + (this.a().random.nextDouble() - this.a().random.nextDouble()) * (double) this.spawnRange; + double d3 = (double) (this.c() + this.a().random.nextInt(3) - 1); + double d4 = (double) this.d() + (this.a().random.nextDouble() - this.a().random.nextDouble()) * (double) this.spawnRange; + EntityInsentient entityinsentient = entity instanceof EntityInsentient ? (EntityInsentient) entity : null; + + entity.setPositionRotation(d0, d3, d4, this.a().random.nextFloat() * 360.0F, 0.0F); + + if (entityinsentient == null || entityinsentient.canSpawn()) { + this.a(entity); + this.a().triggerEffect(2004, this.b(), this.c(), this.d(), 0); + + if (entityinsentient != null) { + entityinsentient.s(); + } + } + } + + this.j(); + } + + public Entity a(Entity entity) { + if (this.i() != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + entity.d(nbttagcompound); + Iterator iterator = this.i().b.c().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + NBTBase nbtbase = this.i().b.get(s); + + nbttagcompound.set(s, nbtbase.clone()); + } + + entity.f(nbttagcompound); + if (entity.world != null) { + // CraftBukkit start - call SpawnerSpawnEvent, abort if cancelled + SpawnerSpawnEvent event = CraftEventFactory.callSpawnerSpawnEvent(entity, this.b(), this.c(), this.d()); + if (!event.isCancelled()) { + entity.world.addEntity(entity, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + // Spigot Start + if ( entity.world.spigotConfig.nerfSpawnerMobs ) + { + entity.fromMobSpawner = true; + } + // Spigot End + } + // CraftBukkit end + } + + NBTTagCompound nbttagcompound1; + + for (Entity entity1 = entity; nbttagcompound.hasKeyOfType("Riding", 10); nbttagcompound = nbttagcompound1) { + nbttagcompound1 = nbttagcompound.getCompound("Riding"); + Entity entity2 = EntityTypes.createEntityByName(nbttagcompound1.getString("id"), entity.world); + + if (entity2 != null) { + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + + entity2.d(nbttagcompound2); + Iterator iterator1 = nbttagcompound1.c().iterator(); + + while (iterator1.hasNext()) { + String s1 = (String) iterator1.next(); + NBTBase nbtbase1 = nbttagcompound1.get(s1); + + nbttagcompound2.set(s1, nbtbase1.clone()); + } + + entity2.f(nbttagcompound2); + entity2.setPositionRotation(entity1.locX, entity1.locY, entity1.locZ, entity1.yaw, entity1.pitch); + // CraftBukkit start - call SpawnerSpawnEvent, skip if cancelled + SpawnerSpawnEvent event = CraftEventFactory.callSpawnerSpawnEvent(entity2, this.b(), this.c(), this.d()); + if (event.isCancelled()) { + continue; + } + if (entity.world != null) { + entity.world.addEntity(entity2, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + } + + entity1.mount(entity2); + } + + entity1 = entity2; + } + } else if (entity instanceof EntityLiving && entity.world != null) { + ((EntityInsentient) entity).prepare((GroupDataEntity) null); + // Spigot start - call SpawnerSpawnEvent, abort if cancelled + SpawnerSpawnEvent event = CraftEventFactory.callSpawnerSpawnEvent(entity, this.b(), this.c(), this.d()); + if (!event.isCancelled()) { + this.a().addEntity(entity, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + // Spigot Start + if ( entity.world.spigotConfig.nerfSpawnerMobs ) + { + entity.fromMobSpawner = true; + } + // Spigot End + } + // Spigot end + } + + return entity; + } + + private void j() { + if (this.maxSpawnDelay <= this.minSpawnDelay) { + this.spawnDelay = this.minSpawnDelay; + } else { + int i = this.maxSpawnDelay - this.minSpawnDelay; + + this.spawnDelay = this.minSpawnDelay + this.a().random.nextInt(i); + } + + if (this.mobs != null && this.mobs.size() > 0) { + this.a((TileEntityMobSpawnerData) WeightedRandom.a(this.a().random, (Collection) this.mobs)); + } + + this.a(1); + } + + public void a(NBTTagCompound nbttagcompound) { + this.mobName = nbttagcompound.getString("EntityId"); + this.spawnDelay = nbttagcompound.getShort("Delay"); + if (nbttagcompound.hasKeyOfType("SpawnPotentials", 9)) { + this.mobs = new ArrayList(); + NBTTagList nbttaglist = nbttagcompound.getList("SpawnPotentials", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + this.mobs.add(new TileEntityMobSpawnerData(this, nbttaglist.get(i))); + } + } else { + this.mobs = null; + } + + if (nbttagcompound.hasKeyOfType("SpawnData", 10)) { + this.a(new TileEntityMobSpawnerData(this, nbttagcompound.getCompound("SpawnData"), this.mobName)); + } else { + this.a((TileEntityMobSpawnerData) null); + } + + if (nbttagcompound.hasKeyOfType("MinSpawnDelay", 99)) { + this.minSpawnDelay = nbttagcompound.getShort("MinSpawnDelay"); + this.maxSpawnDelay = nbttagcompound.getShort("MaxSpawnDelay"); + this.spawnCount = nbttagcompound.getShort("SpawnCount"); + } + + if (nbttagcompound.hasKeyOfType("MaxNearbyEntities", 99)) { + this.maxNearbyEntities = nbttagcompound.getShort("MaxNearbyEntities"); + this.requiredPlayerRange = nbttagcompound.getShort("RequiredPlayerRange"); + } + + if (nbttagcompound.hasKeyOfType("SpawnRange", 99)) { + this.spawnRange = nbttagcompound.getShort("SpawnRange"); + } + + if (this.a() != null && this.a().isStatic) { + this.j = null; + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setString("EntityId", this.getMobName()); + nbttagcompound.setShort("Delay", (short) this.spawnDelay); + nbttagcompound.setShort("MinSpawnDelay", (short) this.minSpawnDelay); + nbttagcompound.setShort("MaxSpawnDelay", (short) this.maxSpawnDelay); + nbttagcompound.setShort("SpawnCount", (short) this.spawnCount); + nbttagcompound.setShort("MaxNearbyEntities", (short) this.maxNearbyEntities); + nbttagcompound.setShort("RequiredPlayerRange", (short) this.requiredPlayerRange); + nbttagcompound.setShort("SpawnRange", (short) this.spawnRange); + if (this.i() != null) { + nbttagcompound.set("SpawnData", this.i().b.clone()); + } + + if (this.i() != null || this.mobs != null && this.mobs.size() > 0) { + NBTTagList nbttaglist = new NBTTagList(); + + if (this.mobs != null && this.mobs.size() > 0) { + Iterator iterator = this.mobs.iterator(); + + while (iterator.hasNext()) { + TileEntityMobSpawnerData tileentitymobspawnerdata = (TileEntityMobSpawnerData) iterator.next(); + + nbttaglist.add(tileentitymobspawnerdata.a()); + } + } else { + nbttaglist.add(this.i().a()); + } + + nbttagcompound.set("SpawnPotentials", nbttaglist); + } + } + + public boolean b(int i) { + if (i == 1 && this.a().isStatic) { + this.spawnDelay = this.minSpawnDelay; + return true; + } else { + return false; + } + } + + public TileEntityMobSpawnerData i() { + return this.spawnData; + } + + public void a(TileEntityMobSpawnerData tileentitymobspawnerdata) { + this.spawnData = tileentitymobspawnerdata; + } + + public abstract void a(int i); + + public abstract World a(); + + public abstract int b(); + + public abstract int c(); + + public abstract int d(); +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NBTBase.java b/vspigot-server/src/main/java/net/minecraft/server/NBTBase.java new file mode 100644 index 0000000..af13af5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NBTBase.java @@ -0,0 +1,83 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +public abstract class NBTBase { + + public static final String[] a = new String[] { "END", "BYTE", "SHORT", "INT", "LONG", "FLOAT", "DOUBLE", "BYTE[]", "STRING", "LIST", "COMPOUND", "INT[]"}; + + abstract void write(DataOutput dataoutput) throws IOException; + + abstract void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException; + + public abstract String toString(); + + public abstract byte getTypeId(); + + protected NBTBase() {} + + protected static NBTBase createTag(byte b0) { + switch (b0) { + case 0: + return new NBTTagEnd(); + + case 1: + return new NBTTagByte(); + + case 2: + return new NBTTagShort(); + + case 3: + return new NBTTagInt(); + + case 4: + return new NBTTagLong(); + + case 5: + return new NBTTagFloat(); + + case 6: + return new NBTTagDouble(); + + case 7: + return new NBTTagByteArray(); + + case 8: + return new NBTTagString(); + + case 9: + return new NBTTagList(); + + case 10: + return new NBTTagCompound(); + + case 11: + return new NBTTagIntArray(); + + default: + return null; + } + } + + public abstract NBTBase clone(); + + public boolean equals(Object object) { + if (!(object instanceof NBTBase)) { + return false; + } else { + NBTBase nbtbase = (NBTBase) object; + + return this.getTypeId() == nbtbase.getTypeId(); + } + } + + public int hashCode() { + return this.getTypeId(); + } + + protected String a_() { + return this.toString(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java b/vspigot-server/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java new file mode 100644 index 0000000..594f21b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java @@ -0,0 +1,138 @@ +package net.minecraft.server; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class NBTCompressedStreamTools { + + public static NBTTagCompound a(InputStream inputstream) { + try { + DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(inputstream))); + + NBTTagCompound nbttagcompound; + + try { + nbttagcompound = a((DataInput) datainputstream, NBTReadLimiter.a); + } finally { + datainputstream.close(); + } + + return nbttagcompound; + } catch (IOException ex) { org.spigotmc.SneakyThrow.sneaky( ex ); } return null; + } + + public static void a(NBTTagCompound nbttagcompound, OutputStream outputstream) { + try { + DataOutputStream dataoutputstream = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(outputstream))); + + try { + a(nbttagcompound, (DataOutput) dataoutputstream); + } finally { + dataoutputstream.close(); + } + } catch (IOException ex) { org.spigotmc.SneakyThrow.sneaky( ex ); } + } + + public static NBTTagCompound a(byte[] abyte, NBTReadLimiter nbtreadlimiter) { + try { + DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new org.spigotmc.LimitStream(new GZIPInputStream(new ByteArrayInputStream(abyte)), nbtreadlimiter))); // Spigot + + NBTTagCompound nbttagcompound; + + try { + nbttagcompound = a((DataInput) datainputstream, nbtreadlimiter); + } finally { + datainputstream.close(); + } + + return nbttagcompound; + } catch (IOException ex) { org.spigotmc.SneakyThrow.sneaky( ex ); } return null; + } + + public static byte[] a(NBTTagCompound nbttagcompound) { + try { + ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(); + DataOutputStream dataoutputstream = new DataOutputStream(new GZIPOutputStream(bytearrayoutputstream)); + + try { + a(nbttagcompound, (DataOutput) dataoutputstream); + } finally { + dataoutputstream.close(); + } + + return bytearrayoutputstream.toByteArray(); + } catch (IOException ex) { org.spigotmc.SneakyThrow.sneaky( ex ); } return null; + } + + public static NBTTagCompound a(DataInputStream datainputstream) { + return a((DataInput) datainputstream, NBTReadLimiter.a); + } + + public static NBTTagCompound a(DataInput datainput, NBTReadLimiter nbtreadlimiter) { + try { + // PaperSpigot start - backport security fix + if ( datainput instanceof net.minecraft.util.io.netty.buffer.ByteBufInputStream ) + { + datainput = new DataInputStream( new org.spigotmc.LimitStream( (InputStream) datainput, nbtreadlimiter ) ); + } + // PaperSpigot end + NBTBase nbtbase = a(datainput, 0, nbtreadlimiter); + + if (nbtbase instanceof NBTTagCompound) { + return (NBTTagCompound) nbtbase; + } else { + throw new IOException("Root tag must be a named compound tag"); + } + } catch (IOException ex) { org.spigotmc.SneakyThrow.sneaky( ex ); } return null; + } + + public static void a(NBTTagCompound nbttagcompound, DataOutput dataoutput) { + a((NBTBase) nbttagcompound, dataoutput); + } + + private static void a(NBTBase nbtbase, DataOutput dataoutput) { + try { + dataoutput.writeByte(nbtbase.getTypeId()); + if (nbtbase.getTypeId() != 0) { + dataoutput.writeUTF(""); + nbtbase.write(dataoutput); + } + } catch (IOException ex) { org.spigotmc.SneakyThrow.sneaky( ex ); } + } + + private static NBTBase a(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) { + try { + byte b0 = datainput.readByte(); + + if (b0 == 0) { + return new NBTTagEnd(); + } else { + datainput.readUTF(); + NBTBase nbtbase = NBTBase.createTag(b0); + + try { + nbtbase.load(datainput, i, nbtreadlimiter); + return nbtbase; + } catch (IOException ioexception) { + CrashReport crashreport = CrashReport.a(ioexception, "Loading NBT data"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("NBT Tag"); + + crashreportsystemdetails.a("Tag name", "[UNNAMED TAG]"); + crashreportsystemdetails.a("Tag type", Byte.valueOf(b0)); + throw new ReportedException(crashreport); + } + } + } catch (IOException ex) { org.spigotmc.SneakyThrow.sneaky( ex ); } return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NBTTagByteArray.java b/vspigot-server/src/main/java/net/minecraft/server/NBTTagByteArray.java new file mode 100644 index 0000000..78a1b9a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NBTTagByteArray.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Arrays; + +public class NBTTagByteArray extends NBTBase { + + private byte[] data; + + NBTTagByteArray() {} + + public NBTTagByteArray(byte[] abyte) { + this.data = abyte; + } + + void write(DataOutput dataoutput) throws IOException { + dataoutput.writeInt(this.data.length); + dataoutput.write(this.data); + } + + void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + int j = datainput.readInt(); + com.google.common.base.Preconditions.checkArgument( j < 1 << 24); + + nbtreadlimiter.a((long) (8 * j)); + this.data = new byte[j]; + datainput.readFully(this.data); + } + + public byte getTypeId() { + return (byte) 7; + } + + public String toString() { + return "[" + this.data.length + " bytes]"; + } + + public NBTBase clone() { + byte[] abyte = new byte[this.data.length]; + + System.arraycopy(this.data, 0, abyte, 0, this.data.length); + return new NBTTagByteArray(abyte); + } + + public boolean equals(Object object) { + return super.equals(object) ? Arrays.equals(this.data, ((NBTTagByteArray) object).data) : false; + } + + public int hashCode() { + return super.hashCode() ^ Arrays.hashCode(this.data); + } + + public byte[] c() { + return this.data; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NBTTagIntArray.java b/vspigot-server/src/main/java/net/minecraft/server/NBTTagIntArray.java new file mode 100644 index 0000000..099e16a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NBTTagIntArray.java @@ -0,0 +1,74 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Arrays; + +public class NBTTagIntArray extends NBTBase { + + private int[] data; + + NBTTagIntArray() {} + + public NBTTagIntArray(int[] aint) { + this.data = aint; + } + + void write(DataOutput dataoutput) throws IOException { + dataoutput.writeInt(this.data.length); + + for (int i = 0; i < this.data.length; ++i) { + dataoutput.writeInt(this.data[i]); + } + } + + void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + int j = datainput.readInt(); + com.google.common.base.Preconditions.checkArgument( j < 1 << 24); + + nbtreadlimiter.a((long) (32 * j)); + this.data = new int[j]; + + for (int k = 0; k < j; ++k) { + this.data[k] = datainput.readInt(); + } + } + + public byte getTypeId() { + return (byte) 11; + } + + public String toString() { + String s = "["; + int[] aint = this.data; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint[j]; + + s = s + k + ","; + } + + return s + "]"; + } + + public NBTBase clone() { + int[] aint = new int[this.data.length]; + + System.arraycopy(this.data, 0, aint, 0, this.data.length); + return new NBTTagIntArray(aint); + } + + public boolean equals(Object object) { + return super.equals(object) ? Arrays.equals(this.data, ((NBTTagIntArray) object).data) : false; + } + + public int hashCode() { + return super.hashCode() ^ Arrays.hashCode(this.data); + } + + public int[] c() { + return this.data; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NBTTagList.java b/vspigot-server/src/main/java/net/minecraft/server/NBTTagList.java new file mode 100644 index 0000000..9b19ebb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NBTTagList.java @@ -0,0 +1,169 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class NBTTagList extends NBTBase { + + private List list = new ArrayList(); + private byte type = 0; + + public NBTTagList() {} + + void write(DataOutput dataoutput) throws IOException { + if (!this.list.isEmpty()) { + this.type = ((NBTBase) this.list.get(0)).getTypeId(); + } else { + this.type = 0; + } + + dataoutput.writeByte(this.type); + dataoutput.writeInt(this.list.size()); + + for (int i = 0; i < this.list.size(); ++i) { + ((NBTBase) this.list.get(i)).write(dataoutput); + } + } + + void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + if (i > 512) { + throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512"); + } else { + nbtreadlimiter.a(8L); + this.type = datainput.readByte(); + int j = datainput.readInt(); + + this.list = new ArrayList(); + + for (int k = 0; k < j; ++k) { + NBTBase nbtbase = NBTBase.createTag(this.type); + nbtreadlimiter.a(8); // PaperSpigot - backport security fix + + nbtbase.load(datainput, i + 1, nbtreadlimiter); + this.list.add(nbtbase); + } + } + } + + public byte getTypeId() { + return (byte) 9; + } + + public String toString() { + String s = "["; + int i = 0; + + for (Iterator iterator = this.list.iterator(); iterator.hasNext(); ++i) { + NBTBase nbtbase = (NBTBase) iterator.next(); + + s = s + "" + i + ':' + nbtbase + ','; + } + + return s + "]"; + } + + public void add(NBTBase nbtbase) { + if (this.type == 0) { + this.type = nbtbase.getTypeId(); + } else if (this.type != nbtbase.getTypeId()) { + System.err.println("WARNING: Adding mismatching tag types to tag list"); + return; + } + + this.list.add(nbtbase); + } + + public NBTTagCompound get(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + return nbtbase.getTypeId() == 10 ? (NBTTagCompound) nbtbase : new NBTTagCompound(); + } else { + return new NBTTagCompound(); + } + } + + public int[] c(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + return nbtbase.getTypeId() == 11 ? ((NBTTagIntArray) nbtbase).c() : new int[0]; + } else { + return new int[0]; + } + } + + public double d(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + return nbtbase.getTypeId() == 6 ? ((NBTTagDouble) nbtbase).g() : 0.0D; + } else { + return 0.0D; + } + } + + public float e(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + return nbtbase.getTypeId() == 5 ? ((NBTTagFloat) nbtbase).h() : 0.0F; + } else { + return 0.0F; + } + } + + public String getString(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + return nbtbase.getTypeId() == 8 ? nbtbase.a_() : nbtbase.toString(); + } else { + return ""; + } + } + + public int size() { + return this.list.size(); + } + + public NBTBase clone() { + NBTTagList nbttaglist = new NBTTagList(); + + nbttaglist.type = this.type; + Iterator iterator = this.list.iterator(); + + while (iterator.hasNext()) { + NBTBase nbtbase = (NBTBase) iterator.next(); + NBTBase nbtbase1 = nbtbase.clone(); + + nbttaglist.list.add(nbtbase1); + } + + return nbttaglist; + } + + public boolean equals(Object object) { + if (super.equals(object)) { + NBTTagList nbttaglist = (NBTTagList) object; + + if (this.type == nbttaglist.type) { + return this.list.equals(nbttaglist.list); + } + } + + return false; + } + + public int hashCode() { + return super.hashCode() ^ this.list.hashCode(); + } + + public int d() { + return this.type; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NBTTagString.java b/vspigot-server/src/main/java/net/minecraft/server/NBTTagString.java new file mode 100644 index 0000000..9b06979 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NBTTagString.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +public class NBTTagString extends NBTBase { + + private String data; + + public NBTTagString() { + this.data = ""; + } + + public NBTTagString(String s) { + this.data = s; + if (s == null) { + throw new IllegalArgumentException("Empty string not allowed"); + } + } + + void write(DataOutput dataoutput) throws IOException { + dataoutput.writeUTF(this.data); + } + + void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + this.data = datainput.readUTF(); + nbtreadlimiter.a((long) (16 * this.data.length())); + } + + public byte getTypeId() { + return (byte) 8; + } + + public String toString() { + return "\"" + this.data + "\""; + } + + public NBTBase clone() { + return new NBTTagString(this.data); + } + + public boolean equals(Object object) { + if (!super.equals(object)) { + return false; + } else { + NBTTagString nbttagstring = (NBTTagString) object; + + return this.data == null && nbttagstring.data == null || this.data != null && this.data.equals(nbttagstring.data); + } + } + + public int hashCode() { + return super.hashCode() ^ this.data.hashCode(); + } + + public String a_() { + return this.data; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NameReferencingFileConverter.java b/vspigot-server/src/main/java/net/minecraft/server/NameReferencingFileConverter.java new file mode 100644 index 0000000..56fb152 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NameReferencingFileConverter.java @@ -0,0 +1,402 @@ +package net.minecraft.server; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.google.common.collect.Iterators; +import net.minecraft.util.com.google.common.collect.Lists; +import net.minecraft.util.com.google.common.collect.Maps; +import net.minecraft.util.com.google.common.io.Files; +import net.minecraft.util.com.mojang.authlib.Agent; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.ProfileLookupCallback; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit - Imported for package private static methods + +public class NameReferencingFileConverter { + + private static final Logger e = LogManager.getLogger(); + public static final File a = new File("banned-ips.txt"); + public static final File b = new File("banned-players.txt"); + public static final File c = new File("ops.txt"); + public static final File d = new File("white-list.txt"); + + static List a(File file1, Map map) throws IOException { // CraftBukkit - Added throws + List list = Files.readLines(file1, Charsets.UTF_8); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + s = s.trim(); + if (!s.startsWith("#") && s.length() >= 1) { + String[] astring = s.split("\\|"); + + map.put(astring[0].toLowerCase(Locale.ROOT), astring); + } + } + + return list; + } + + private static void a(MinecraftServer minecraftserver, Collection collection, ProfileLookupCallback profilelookupcallback) { + String[] astring = (String[]) Iterators.toArray(Iterators.filter(collection.iterator(), new PredicateEmptyList()), String.class); + + if (minecraftserver.getOnlineMode() || org.spigotmc.SpigotConfig.bungee) { // Spigot: bungee = online mode, for now. + minecraftserver.getGameProfileRepository().findProfilesByNames(astring, Agent.MINECRAFT, profilelookupcallback); + } else { + String[] astring1 = astring; + int i = astring.length; + + for (int j = 0; j < i; ++j) { + String s = astring1[j]; + UUID uuid = EntityHuman.a(new GameProfile((UUID) null, s)); + GameProfile gameprofile = new GameProfile(uuid, s); + + profilelookupcallback.onProfileLookupSucceeded(gameprofile); + } + } + } + + public static boolean a(MinecraftServer minecraftserver) { + GameProfileBanList gameprofilebanlist = new GameProfileBanList(PlayerList.a); + + if (b.exists() && b.isFile()) { + if (gameprofilebanlist.c().exists()) { + try { + gameprofilebanlist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace + } catch (IOException filenotfoundexception) { + e.warn("Could not load existing file " + gameprofilebanlist.c().getName() + ", " + filenotfoundexception.getMessage()); + } + // CraftBukkit end + } + + try { + HashMap hashmap = Maps.newHashMap(); + + a(b, (Map) hashmap); + GameProfileBanListEntryConverter gameprofilebanlistentryconverter = new GameProfileBanListEntryConverter(minecraftserver, hashmap, gameprofilebanlist); + + a(minecraftserver, hashmap.keySet(), gameprofilebanlistentryconverter); + gameprofilebanlist.save(); + c(b); + return true; + } catch (IOException ioexception) { + e.warn("Could not read old user banlist to convert it!", ioexception); + return false; + } catch (FileConversionException fileconversionexception) { + e.error("Conversion failed, please try again later", fileconversionexception); + return false; + } + } else { + return true; + } + } + + public static boolean b(MinecraftServer minecraftserver) { + IpBanList ipbanlist = new IpBanList(PlayerList.b); + + if (a.exists() && a.isFile()) { + if (ipbanlist.c().exists()) { + try { + ipbanlist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace + } catch (IOException filenotfoundexception) { + e.warn("Could not load existing file " + ipbanlist.c().getName() + ", " + filenotfoundexception.getMessage()); + } + // CraftBukkit end + } + + try { + HashMap hashmap = Maps.newHashMap(); + + a(a, (Map) hashmap); + Iterator iterator = hashmap.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + String[] astring = (String[]) hashmap.get(s); + Date date = astring.length > 1 ? b(astring[1], (Date) null) : null; + String s1 = astring.length > 2 ? astring[2] : null; + Date date1 = astring.length > 3 ? b(astring[3], (Date) null) : null; + String s2 = astring.length > 4 ? astring[4] : null; + + ipbanlist.add(new IpBanEntry(s, date, s1, date1, s2)); + } + + ipbanlist.save(); + c(a); + return true; + } catch (IOException ioexception) { + e.warn("Could not parse old ip banlist to convert it!", ioexception); + return false; + } + } else { + return true; + } + } + + public static boolean c(MinecraftServer minecraftserver) { + OpList oplist = new OpList(PlayerList.c); + + if (c.exists() && c.isFile()) { + if (oplist.c().exists()) { + try { + oplist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace + } catch (IOException filenotfoundexception) { + e.warn("Could not load existing file " + oplist.c().getName() + ", " + filenotfoundexception.getMessage()); + } + // CraftBukkit end + } + + try { + List list = Files.readLines(c, Charsets.UTF_8); + OpListProfileCallback oplistprofilecallback = new OpListProfileCallback(minecraftserver, oplist); + + a(minecraftserver, list, oplistprofilecallback); + oplist.save(); + c(c); + return true; + } catch (IOException ioexception) { + e.warn("Could not read old oplist to convert it!", ioexception); + return false; + } catch (FileConversionException fileconversionexception) { + e.error("Conversion failed, please try again later", fileconversionexception); + return false; + } + } else { + return true; + } + } + + public static boolean d(MinecraftServer minecraftserver) { + WhiteList whitelist = new WhiteList(PlayerList.d); + + if (d.exists() && d.isFile()) { + if (whitelist.c().exists()) { + try { + whitelist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace + } catch (IOException filenotfoundexception) { + e.warn("Could not load existing file " + whitelist.c().getName() + ", " + filenotfoundexception.getMessage()); + } + // CraftBukkit end + } + + try { + List list = Files.readLines(d, Charsets.UTF_8); + WhiteListProfileCallback whitelistprofilecallback = new WhiteListProfileCallback(minecraftserver, whitelist); + + a(minecraftserver, list, whitelistprofilecallback); + whitelist.save(); + c(d); + return true; + } catch (IOException ioexception) { + e.warn("Could not read old whitelist to convert it!", ioexception); + return false; + } catch (FileConversionException fileconversionexception) { + e.error("Conversion failed, please try again later", fileconversionexception); + return false; + } + } else { + return true; + } + } + + public static String a(String s) { + if (!UtilColor.b(s) && s.length() <= 16) { + MinecraftServer minecraftserver = MinecraftServer.getServer(); + GameProfile gameprofile = minecraftserver.getUserCache().getProfile(s); + + if (gameprofile != null && gameprofile.getId() != null) { + return gameprofile.getId().toString(); + } else if (!minecraftserver.N() && minecraftserver.getOnlineMode()) { + ArrayList arraylist = Lists.newArrayList(); + GameProfileLookupCallback gameprofilelookupcallback = new GameProfileLookupCallback(minecraftserver, arraylist); + + a(minecraftserver, Lists.newArrayList(new String[] { s}), gameprofilelookupcallback); + return arraylist.size() > 0 && ((GameProfile) arraylist.get(0)).getId() != null ? ((GameProfile) arraylist.get(0)).getId().toString() : ""; + } else { + return EntityHuman.a(new GameProfile((UUID) null, s)).toString(); + } + } else { + return s; + } + } + + public static boolean a(DedicatedServer dedicatedserver, PropertyManager propertymanager) { + File file1 = d(propertymanager); + File file2 = new File(file1.getParentFile(), "playerdata"); + File file3 = new File(file1.getParentFile(), "unknownplayers"); + + if (file1.exists() && file1.isDirectory()) { + File[] afile = file1.listFiles(); + ArrayList arraylist = Lists.newArrayList(); + File[] afile1 = afile; + int i = afile.length; + + for (int j = 0; j < i; ++j) { + File file4 = afile1[j]; + String s = file4.getName(); + + if (s.toLowerCase(Locale.ROOT).endsWith(".dat")) { + String s1 = s.substring(0, s.length() - ".dat".length()); + + if (s1.length() > 0) { + arraylist.add(s1); + } + } + } + + try { + String[] astring = (String[]) arraylist.toArray(new String[arraylist.size()]); + PlayerDatFileConverter playerdatfileconverter = new PlayerDatFileConverter(dedicatedserver, file2, file3, file1, astring); + + a(dedicatedserver, Lists.newArrayList(astring), playerdatfileconverter); + return true; + } catch (FileConversionException fileconversionexception) { + e.error("Conversion failed, please try again later", fileconversionexception); + return false; + } + } else { + return true; + } + } + + private static void b(File file1) { + if (file1.exists()) { + if (!file1.isDirectory()) { + throw new FileConversionException("Can\'t create directory " + file1.getName() + " in world save directory.", (PredicateEmptyList) null); + } + } else if (!file1.mkdirs()) { + throw new FileConversionException("Can\'t create directory " + file1.getName() + " in world save directory.", (PredicateEmptyList) null); + } + } + + public static boolean a(PropertyManager propertymanager) { + boolean flag = b(propertymanager); + + flag = flag && c(propertymanager); + return flag; + } + + private static boolean b(PropertyManager propertymanager) { + boolean flag = false; + + if (b.exists() && b.isFile()) { + flag = true; + } + + boolean flag1 = false; + + if (a.exists() && a.isFile()) { + flag1 = true; + } + + boolean flag2 = false; + + if (c.exists() && c.isFile()) { + flag2 = true; + } + + boolean flag3 = false; + + if (d.exists() && d.isFile()) { + flag3 = true; + } + + if (!flag && !flag1 && !flag2 && !flag3) { + return true; + } else { + e.warn("**** FAILED TO START THE SERVER AFTER ACCOUNT CONVERSION!"); + e.warn("** please remove the following files and restart the server:"); + if (flag) { + e.warn("* " + b.getName()); + } + + if (flag1) { + e.warn("* " + a.getName()); + } + + if (flag2) { + e.warn("* " + c.getName()); + } + + if (flag3) { + e.warn("* " + d.getName()); + } + + return false; + } + } + + private static boolean c(PropertyManager propertymanager) { + File file1 = d(propertymanager); + + if (file1.exists() && file1.isDirectory()) { + String[] astring = file1.list(new DatFilenameFilter()); + + if (astring.length > 0) { + e.warn("**** DETECTED OLD PLAYER FILES IN THE WORLD SAVE"); + e.warn("**** THIS USUALLY HAPPENS WHEN THE AUTOMATIC CONVERSION FAILED IN SOME WAY"); + e.warn("** please restart the server and if the problem persists, remove the directory \'{}\'", new Object[] { file1.getPath()}); + return false; + } + } + + return true; + } + + private static File d(PropertyManager propertymanager) { + String s = propertymanager.getString("level-name", "world"); + File file1 = new File(MinecraftServer.getServer().server.getWorldContainer(), s); // CraftBukkit - Respect container setting + + return new File(file1, "players"); + } + + private static void c(File file1) { + File file2 = new File(file1.getName() + ".converted"); + + file1.renameTo(file2); + } + + private static Date b(String s, Date date) { + Date date1; + + try { + date1 = ExpirableListEntry.a.parse(s); + } catch (ParseException parseexception) { + date1 = date; + } + + return date1; + } + + static Logger a() { + return e; + } + + static Date a(String s, Date date) { + return b(s, date); + } + + static void a(File file1) { + b(file1); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Navigation.java b/vspigot-server/src/main/java/net/minecraft/server/Navigation.java new file mode 100644 index 0000000..7e4c298 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Navigation.java @@ -0,0 +1,373 @@ +package net.minecraft.server; + +// Poweruser start +import net.valorhcf.pathsearch.PositionPathSearchType; +import net.valorhcf.pathsearch.jobs.PathSearchJob; +import net.valorhcf.pathsearch.jobs.PathSearchJobNavigationEntity; +import net.valorhcf.pathsearch.jobs.PathSearchJobNavigationPosition; +//Poweruser end + +public class Navigation { + + protected EntityInsentient a; // Poweruser - private -> protected + protected World b; // Poweruser - private -> protected + private PathEntity c; + private double d; + private AttributeInstance e; + private boolean f; + private int g; + private int h; + private Vec3D i = Vec3D.a(0.0D, 0.0D, 0.0D); + protected boolean j = true; // Poweruser - private -> protected + protected boolean k; // Poweruser - private -> protected + protected boolean l; // Poweruser - private -> protected + protected boolean m; // Poweruser - private -> protected + + // Poweruser start + public void setSearchResult(PathSearchJobNavigationEntity pathSearch) { } + + public void setSearchResult(PathSearchJobNavigationPosition pathSearch) { } + + public PathEntity a(PositionPathSearchType type, double d0, double d1, double d2) { + return this.a(d0, d1, d2); + } + + public boolean a(PositionPathSearchType type, double d0, double d1, double d2, double d3) { + return this.a(d0, d1, d2, d3); + } + + public void cleanUpExpiredSearches() { } + + public void cancelSearch(PathSearchJob pathSearch) { } + // Poweruser end + + public Navigation(EntityInsentient entityinsentient, World world) { + this.a = entityinsentient; + this.b = world; + this.e = entityinsentient.getAttributeInstance(GenericAttributes.b); + } + + public void a(boolean flag) { + this.l = flag; + } + + public boolean a() { + return this.l; + } + + public void b(boolean flag) { + this.k = flag; + } + + public void c(boolean flag) { + this.j = flag; + } + + public boolean c() { + return this.k; + } + + public void d(boolean flag) { + this.f = flag; + } + + public void a(double d0) { + this.d = d0; + } + + public void e(boolean flag) { + this.m = flag; + } + + public float d() { + return (float) this.e.getValue(); + } + + public PathEntity a(double d0, double d1, double d2) { + return !this.l() ? null : this.b.a(this.a, MathHelper.floor(d0), (int) d1, MathHelper.floor(d2), this.d(), this.j, this.k, this.l, this.m); + } + + public boolean a(double d0, double d1, double d2, double d3) { + PathEntity pathentity = this.a((double) MathHelper.floor(d0), (double) ((int) d1), (double) MathHelper.floor(d2)); + + return this.a(pathentity, d3); + } + + public PathEntity a(Entity entity) { + return !this.l() ? null : this.b.findPath(this.a, entity, this.d(), this.j, this.k, this.l, this.m); + } + + public boolean a(Entity entity, double d0) { + PathEntity pathentity = this.a(entity); + + return pathentity != null ? this.a(pathentity, d0) : false; + } + + public boolean a(PathEntity pathentity, double d0) { + if (pathentity == null) { + this.c = null; + return false; + } else { + if (!pathentity.a(this.c)) { + this.c = pathentity; + } + + if (this.f) { + this.n(); + } + + if (this.c.d() == 0) { + return false; + } else { + this.d = d0; + Vec3D vec3d = this.j(); + + this.h = this.g; + this.i.a = vec3d.a; + this.i.b = vec3d.b; + this.i.c = vec3d.c; + return true; + } + } + } + + public PathEntity e() { + return this.c; + } + + public void f() { + ++this.g; + if (!this.g()) { + if (this.l()) { + this.i(); + } + + if (!this.g()) { + Vec3D vec3d = this.c.a((Entity) this.a); + + if (vec3d != null) { + this.a.getControllerMove().a(vec3d.a, vec3d.b, vec3d.c, this.d); + } + } + } + } + + private void i() { + Vec3D vec3d = this.j(); + int i = this.c.d(); + + for (int j = this.c.e(); j < this.c.d(); ++j) { + if (this.c.a(j).b != (int) vec3d.b) { + i = j; + break; + } + } + + float f = this.a.width * this.a.width; + + int k; + + for (k = this.c.e(); k < i; ++k) { + if (vec3d.distanceSquared(this.c.a(this.a, k)) < (double) f) { + this.c.c(k + 1); + } + } + + k = MathHelper.f(this.a.width); + int l = (int) this.a.length + 1; + int i1 = k; + + for (int j1 = i - 1; j1 >= this.c.e(); --j1) { + if (this.a(vec3d, this.c.a(this.a, j1), k, l, i1)) { + this.c.c(j1); + break; + } + } + + if (this.g - this.h > 100) { + if (vec3d.distanceSquared(this.i) < 2.25D) { + this.h(); + } + + this.h = this.g; + this.i.a = vec3d.a; + this.i.b = vec3d.b; + this.i.c = vec3d.c; + } + } + + public boolean g() { + return this.c == null || this.c.b(); + } + + public void h() { + this.c = null; + } + + private Vec3D j() { + return Vec3D.a(this.a.locX, (double) this.k(), this.a.locZ); + } + + private int k() { + if (this.a.M() && this.m) { + int i = (int) this.a.boundingBox.b; + Block block = this.b.getType(MathHelper.floor(this.a.locX), i, MathHelper.floor(this.a.locZ)); + int j = 0; + + do { + if (block != Blocks.WATER && block != Blocks.STATIONARY_WATER) { + return i; + } + + ++i; + block = this.b.getType(MathHelper.floor(this.a.locX), i, MathHelper.floor(this.a.locZ)); + ++j; + } while (j <= 16); + + return (int) this.a.boundingBox.b; + } else { + return (int) (this.a.boundingBox.b + 0.5D); + } + } + + protected boolean l() { // Poweruser - private -> protected + return this.a.onGround || this.m && this.m() || this.a.am() && this.a instanceof EntityZombie && this.a.vehicle instanceof EntityChicken; + } + + private boolean m() { + return this.a.M() || this.a.P(); + } + + private void n() { + if (!this.b.i(MathHelper.floor(this.a.locX), (int) (this.a.boundingBox.b + 0.5D), MathHelper.floor(this.a.locZ))) { + for (int i = 0; i < this.c.d(); ++i) { + PathPoint pathpoint = this.c.a(i); + + if (this.b.i(pathpoint.a, pathpoint.b, pathpoint.c)) { + this.c.b(i - 1); + return; + } + } + } + } + + private boolean a(Vec3D vec3d, Vec3D vec3d1, int i, int j, int k) { + int l = MathHelper.floor(vec3d.a); + int i1 = MathHelper.floor(vec3d.c); + double d0 = vec3d1.a - vec3d.a; + double d1 = vec3d1.c - vec3d.c; + double d2 = d0 * d0 + d1 * d1; + + if (d2 < 1.0E-8D) { + return false; + } else { + double d3 = 1.0D / Math.sqrt(d2); + + d0 *= d3; + d1 *= d3; + i += 2; + k += 2; + if (!this.a(l, (int) vec3d.b, i1, i, j, k, vec3d, d0, d1)) { + return false; + } else { + i -= 2; + k -= 2; + double d4 = 1.0D / Math.abs(d0); + double d5 = 1.0D / Math.abs(d1); + double d6 = (double) (l * 1) - vec3d.a; + double d7 = (double) (i1 * 1) - vec3d.c; + + if (d0 >= 0.0D) { + ++d6; + } + + if (d1 >= 0.0D) { + ++d7; + } + + d6 /= d0; + d7 /= d1; + int j1 = d0 < 0.0D ? -1 : 1; + int k1 = d1 < 0.0D ? -1 : 1; + int l1 = MathHelper.floor(vec3d1.a); + int i2 = MathHelper.floor(vec3d1.c); + int j2 = l1 - l; + int k2 = i2 - i1; + + do { + if (j2 * j1 <= 0 && k2 * k1 <= 0) { + return true; + } + + if (d6 < d7) { + d6 += d4; + l += j1; + j2 = l1 - l; + } else { + d7 += d5; + i1 += k1; + k2 = i2 - i1; + } + } while (this.a(l, (int) vec3d.b, i1, i, j, k, vec3d, d0, d1)); + + return false; + } + } + } + + private boolean a(int i, int j, int k, int l, int i1, int j1, Vec3D vec3d, double d0, double d1) { + int k1 = i - l / 2; + int l1 = k - j1 / 2; + + if (!this.b(k1, j, l1, l, i1, j1, vec3d, d0, d1)) { + return false; + } else { + for (int i2 = k1; i2 < k1 + l; ++i2) { + for (int j2 = l1; j2 < l1 + j1; ++j2) { + double d2 = (double) i2 + 0.5D - vec3d.a; + double d3 = (double) j2 + 0.5D - vec3d.c; + + if (d2 * d0 + d3 * d1 >= 0.0D) { + Block block = this.b.getType(i2, j - 1, j2); + Material material = block.getMaterial(); + + if (material == Material.AIR) { + return false; + } + + if (material == Material.WATER && !this.a.M()) { + return false; + } + + if (material == Material.LAVA) { + return false; + } + } + } + } + + return true; + } + } + + private boolean b(int i, int j, int k, int l, int i1, int j1, Vec3D vec3d, double d0, double d1) { + for (int k1 = i; k1 < i + l; ++k1) { + for (int l1 = j; l1 < j + i1; ++l1) { + for (int i2 = k; i2 < k + j1; ++i2) { + double d2 = (double) k1 + 0.5D - vec3d.a; + double d3 = (double) i2 + 0.5D - vec3d.c; + + if (d2 * d0 + d3 * d1 >= 0.0D) { + Block block = this.b.getType(k1, l1, i2); + + if (!block.b(this.b, k1, l1, i2)) { + return false; + } + } + } + } + } + + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NetworkManager.java b/vspigot-server/src/main/java/net/minecraft/server/NetworkManager.java new file mode 100644 index 0000000..fe2a66a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NetworkManager.java @@ -0,0 +1,414 @@ +package net.minecraft.server; + +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import javax.crypto.SecretKey; + +import net.valorhcf.ValorSpigot; +import net.valorhcf.handler.PacketHandler; +import net.minecraft.util.com.google.common.collect.Queues; +import net.minecraft.util.com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.minecraft.util.com.mojang.authlib.properties.Property; +import net.minecraft.util.io.netty.channel.Channel; +import net.minecraft.util.io.netty.channel.ChannelHandlerContext; +import net.minecraft.util.io.netty.channel.SimpleChannelInboundHandler; +import net.minecraft.util.io.netty.channel.local.LocalChannel; +import net.minecraft.util.io.netty.channel.local.LocalServerChannel; +import net.minecraft.util.io.netty.channel.nio.NioEventLoopGroup; +import net.minecraft.util.io.netty.handler.timeout.TimeoutException; +import net.minecraft.util.io.netty.util.AttributeKey; +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; +import net.minecraft.util.org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; +// Spigot start +import com.google.common.collect.ImmutableSet; +import org.spigotmc.SpigotCompressor; +import org.spigotmc.SpigotDecompressor; +// Spigot end +// Guardian start +import org.bukkit.event.player.GuardianEvent; +import org.bukkit.Bukkit; +// Guardian end + +// Poweruser start +import org.spigotmc.CustomTimingsHandler; +import org.bukkit.craftbukkit.SpigotTimings; +// Poweruser end + +public class NetworkManager extends SimpleChannelInboundHandler { + + private static final Logger i = LogManager.getLogger(); + public static final Marker a = MarkerManager.getMarker("NETWORK"); + public static final Marker b = MarkerManager.getMarker("NETWORK_PACKETS", a); + public static final Marker c = MarkerManager.getMarker("NETWORK_STAT", a); + public static final AttributeKey d = new AttributeKey("protocol"); + public static final AttributeKey e = new AttributeKey("receivable_packets"); + public static final AttributeKey f = new AttributeKey("sendable_packets"); + public static final NioEventLoopGroup g = new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build()); + public static final NetworkStatistics h = new NetworkStatistics(); + private final boolean j; + private final Queue k = Queues.newConcurrentLinkedQueue(); + // private final Queue l = Queues.newConcurrentLinkedQueue(); // MineHQ + public Channel m; + // Spigot Start + public SocketAddress n; + public java.util.UUID spoofedUUID; + public Property[] spoofedProfile; + public boolean preparing = true; + // Spigot End + private PacketListener o; + private EnumProtocol p; + private IChatBaseComponent q; + private boolean r; + // Spigot Start + public static final AttributeKey protocolVersion = new AttributeKey("protocol_version"); + public static final ImmutableSet SUPPORTED_VERSIONS = ImmutableSet.of(4, 5, 47); + public static final int CURRENT_VERSION = 5; + public static int getVersion(Channel attr) + { + Integer ver = attr.attr( protocolVersion ).get(); + return ( ver != null ) ? ver : CURRENT_VERSION; + } + public int getVersion() + { + return getVersion( this.m ); + } + // Spigot End + + // Poweruser start + private boolean lockDownIncomingTraffic = false; + + protected boolean lockDownIncomingTraffic() { + boolean oldValue = this.lockDownIncomingTraffic; + this.lockDownIncomingTraffic = true; + return oldValue; + } + // Poweruser end + + // Guardian start + private Packet[] packets = new Packet[10]; + private long[] limitTimes = new long[12]; + public long lastTickNetworkProcessed = MinecraftServer.currentTick; + public long ticksSinceLastPacket = -1L; + private int numOfKillAuraB = 0; + private List numOfKillAuraBLogs = new ArrayList(); + private int numOfT = 0; + private List numOfKillAuraTLogs = new ArrayList(); + private long lastKillAuraKTick = MinecraftServer.currentTick; + public long currentTime = System.currentTimeMillis(); + public long lastVehicleTime = -1L; + public int numOfFlyingPacketsInARow = 0; + // Guardian end + + public static final GenericFutureListener[] emptyListenerArray = new GenericFutureListener[0]; // Poweruser + + public NetworkManager(boolean flag) { + this.j = flag; + + // Guardian start + this.limitTimes[0] = 4000L; + this.limitTimes[1] = 4000L; + this.limitTimes[2] = 4000L; + this.limitTimes[3] = 4000L; + this.limitTimes[4] = 5000L; + this.limitTimes[5] = 6000L; + this.limitTimes[6] = 7000L; + this.limitTimes[7] = 7000L; + this.limitTimes[8] = 7000L; + this.limitTimes[9] = 7000L; + this.limitTimes[10] = 7000L; + this.limitTimes[11] = 7000L; + // Guardian end + } + + public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception { // CraftBukkit - throws Exception + super.channelActive(channelhandlercontext); + this.m = channelhandlercontext.channel(); + this.n = this.m.remoteAddress(); + // Spigot Start + this.preparing = false; + // Spigot End + this.a(EnumProtocol.HANDSHAKING); + } + + public void a(EnumProtocol enumprotocol) { + this.p = (EnumProtocol) this.m.attr(d).getAndSet(enumprotocol); + this.m.attr(e).set(enumprotocol.a(this.j)); + this.m.attr(f).set(enumprotocol.b(this.j)); + this.m.config().setAutoRead(true); + i.debug("Enabled auto read"); + } + + public void channelInactive(ChannelHandlerContext channelhandlercontext) { + this.close(new ChatMessage("disconnect.endOfStream", new Object[0])); + } + + public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) { + ChatMessage chatmessage; + + if (throwable instanceof TimeoutException) { + chatmessage = new ChatMessage("disconnect.timeout", new Object[0]); + } else { + chatmessage = new ChatMessage("disconnect.genericReason", new Object[] { "Internal Exception: " + throwable}); + } + + this.close(chatmessage); + if (MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot + } + + protected void a(ChannelHandlerContext channelhandlercontext, Packet packet) { + if (this.m.isOpen() && !this.lockDownIncomingTraffic) { // Poweruser + if (packet.a()) { + packet.handle(this.o); + if (packet instanceof PacketPlayInKeepAlive) { + this.k.add(packet); + } + + if (this.o instanceof PlayerConnection) { + try { + for (PacketHandler handler : ValorSpigot.INSTANCE.getPacketHandlers()) { + handler.handleReceivedPacket((PlayerConnection) this.o, packet); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } else { + this.k.add(packet); + } + } + } + + public void a(PacketListener packetlistener) { + Validate.notNull(packetlistener, "packetListener", new Object[0]); + i.debug("Set listener of {} to {}", new Object[] { this, packetlistener}); + this.o = packetlistener; + } + + public void handle(Packet packet, GenericFutureListener... agenericfuturelistener) { + if (this.m != null && this.m.isOpen()) { + // this.i(); // MineHQ + this.b(packet, agenericfuturelistener); + } else { + // this.l.add(new QueuedPacket(packet, agenericfuturelistener)); // MineHQ + } + } + + private void b(Packet packet, GenericFutureListener[] agenericfuturelistener) { + EnumProtocol enumprotocol = EnumProtocol.a(packet); + EnumProtocol enumprotocol1 = (EnumProtocol) this.m.attr(d).get(); + + if (enumprotocol1 != enumprotocol) { + i.debug("Disabled auto read"); + this.m.config().setAutoRead(false); + } + + if (this.m.eventLoop().inEventLoop()) { + /* Poweruser - is done in QueuedProtocolSwitch.execute(..) + if (enumprotocol != enumprotocol1) { + this.a(enumprotocol); + } + */ + + QueuedProtocolSwitch.execute(this, enumprotocol, enumprotocol1, packet, agenericfuturelistener); // Poweruser + } else { + this.m.eventLoop().execute(new QueuedProtocolSwitch(this, enumprotocol, enumprotocol1, packet, agenericfuturelistener)); + } + } + + // MineHQ start - remove unneeded packet queue + /* + private void i() { + if (this.m != null && this.m.isOpen()) { + // PaperSpigot start - Improve Network Manager packet handling + QueuedPacket queuedpacket; + while ((queuedpacket = (QueuedPacket) this.l.poll()) != null) { + this.b(QueuedPacket.a(queuedpacket), QueuedPacket.b(queuedpacket)); + } + // PaperSpigot end + } + } + */ + // MineHQ end + + public void a() { + // this.i(); // MineHQ + EnumProtocol enumprotocol = (EnumProtocol) this.m.attr(d).get(); + + if (this.p != enumprotocol) { + if (this.p != null) { + this.o.a(this.p, enumprotocol); + } + + this.p = enumprotocol; + } + + if (this.o != null) { + boolean processed = false; // Guardian + // PaperSpigot start - Improve Network Manager packet handling - Configurable packets per player per tick processing + Packet packet; + for (int i = org.github.paperspigot.PaperSpigotConfig.maxPacketsPerPlayer; (packet = (Packet) this.k.poll()) != null && i >= 0; --i) { + // PaperSpigot end + + // CraftBukkit start + if (!this.isConnected() || !this.m.config().isAutoRead()) { + continue; + } + // CraftBukkit end + + // Poweruser start + if(this.lockDownIncomingTraffic) { + this.k.clear(); + break; + } + // Poweruser end + + // Guardian start + if (!processed) { + this.ticksSinceLastPacket = (MinecraftServer.currentTick - this.lastTickNetworkProcessed); + this.lastTickNetworkProcessed = MinecraftServer.currentTick; + this.currentTime = System.currentTimeMillis(); + processed = true; + } + + if (o instanceof PlayerConnection) { + PlayerConnection connection = (PlayerConnection) o; + + if ((packet instanceof PacketPlayInKeepAlive)) { + ((PlayerConnection)this.o).handleKeepAliveSync((PacketPlayInKeepAlive)packet); + continue; + } + + if (((packet instanceof PacketPlayInChat)) || ((packet instanceof PacketPlayInCustomPayload))) { + packet.handle(this.o); + + try { + for (PacketHandler handler : ValorSpigot.INSTANCE.getPacketHandlers()) { + handler.handleReceivedPacket((PlayerConnection) this.o, packet); + } + } catch (Exception e) { + e.printStackTrace(); + } + + continue; + } + } + // Guardian end + + // Poweruser start + CustomTimingsHandler packetHandlerTimer = SpigotTimings.getPacketHandlerTimings(packet); + packetHandlerTimer.startTiming(); + try { + packet.handle(this.o); + } finally { + packetHandlerTimer.stopTiming(); + } + + if (this.o instanceof PlayerConnection) { + try { + for (PacketHandler handler : ValorSpigot.INSTANCE.getPacketHandlers()) { + handler.handleReceivedPacket((PlayerConnection) this.o, packet); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + // Poweruser end + + + } + + this.o.a(); + } + + this.m.flush(); + } + + // Guardian start + private void runSync(final GuardianEvent event) { + MinecraftServer.getServer().processQueue.add(new Runnable() { + + public void run() { + Bukkit.getPluginManager().callEvent(event); + } + + }); + } + // Guardian end + + public SocketAddress getSocketAddress() { + return this.n; + } + + public void close(IChatBaseComponent ichatbasecomponent) { + // Spigot Start + this.preparing = false; + this.k.clear(); // Spigot Update - 20140921a + // this.l.clear(); // Spigot Update - 20140921a // MineHQ + // Spigot End + if (this.m.isOpen()) { + this.m.close(); + this.q = ichatbasecomponent; + } + } + + public boolean c() { + return this.m instanceof LocalChannel || this.m instanceof LocalServerChannel; + } + + public void a(SecretKey secretkey) { + this.m.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(MinecraftEncryption.a(2, secretkey))); + this.m.pipeline().addBefore("prepender", "encrypt", new PacketEncrypter(MinecraftEncryption.a(1, secretkey))); + this.r = true; + } + + public boolean isConnected() { + return this.m != null && this.m.isOpen(); + } + + public PacketListener getPacketListener() { + return this.o; + } + + public IChatBaseComponent f() { + return this.q; + } + + public void g() { + this.m.config().setAutoRead(false); + } + + protected void channelRead0(ChannelHandlerContext channelhandlercontext, Object object) { + this.a(channelhandlercontext, (Packet) object); + } + + static Channel a(NetworkManager networkmanager) { + return networkmanager.m; + } + + // Spigot Start + public SocketAddress getRawAddress() + { + return this.m.remoteAddress(); + } + // Spigot End + + + // Spigot start - protocol patch + public void enableCompression() { + // Fix ProtocolLib compatibility + if ( m.pipeline().get("protocol_lib_decoder") != null ) { + m.pipeline().addBefore( "protocol_lib_decoder", "decompress", new SpigotDecompressor() ); + } else { + m.pipeline().addBefore( "decoder", "decompress", new SpigotDecompressor() ); + } + + m.pipeline().addBefore( "encoder", "compress", new SpigotCompressor() ); + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NextTickListEntry.java b/vspigot-server/src/main/java/net/minecraft/server/NextTickListEntry.java new file mode 100644 index 0000000..f63c095 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NextTickListEntry.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +public class NextTickListEntry implements Comparable { + + private static long f; + private final Block g; + public int a; + public int b; + public int c; + public long d; + public int e; + private long h; + + public NextTickListEntry(int i, int j, int k, Block block) { + this.h = (long) (f++); + this.a = i; + this.b = j; + this.c = k; + this.g = block; + } + + public boolean equals(Object object) { + if (!(object instanceof NextTickListEntry)) { + return false; + } else { + NextTickListEntry nextticklistentry = (NextTickListEntry) object; + + return this.a == nextticklistentry.a && this.b == nextticklistentry.b && this.c == nextticklistentry.c && Block.a(this.g, nextticklistentry.g); + } + } + + public int hashCode() { + return (c & 0xff) | ((a & 0x7fff) << 8) | ((b & 0x7fff) << 24) | ((a < 0) ? 0x0080000000 : 0) | ((b < 0) ? 0x0000008000 : 0); + } + + public NextTickListEntry a(long i) { + this.d = i; + return this; + } + + public void a(int i) { + this.e = i; + } + + public int compareTo(NextTickListEntry nextticklistentry) { + return this.d < nextticklistentry.d ? -1 : (this.d > nextticklistentry.d ? 1 : (this.e != nextticklistentry.e ? this.e - nextticklistentry.e : (this.h < nextticklistentry.h ? -1 : (this.h > nextticklistentry.h ? 1 : 0)))); + } + + public String toString() { + return Block.getId(this.g) + ": (" + this.a + ", " + this.b + ", " + this.c + "), " + this.d + ", " + this.e + ", " + this.h; + } + + public Block a() { + return this.g; + } + + public int compareTo(Object object) { + return this.compareTo((NextTickListEntry) object); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/NibbleArray.java b/vspigot-server/src/main/java/net/minecraft/server/NibbleArray.java new file mode 100644 index 0000000..b28899e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/NibbleArray.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +public class NibbleArray { + + public final byte[] a; + + public NibbleArray(int i, int j) { + this.a = new byte[i >> 1]; + // this.b = j; // MineHQ + // this.c = j + 4; // MineHQ + } + + public NibbleArray(byte[] abyte, int i) { + // MineHQ start + if (abyte.length != 2048 || i != 4) { + throw new IllegalStateException("NibbleArrays should be 2048 in length with 4 bits per nibble."); + } + // MineHQ end + this.a = abyte; + } + + public int a(int i, int j, int k) { + // MineHQ start + int position = j << 8 | k << 4 | i; + return this.a[position >> 1] >> ((position & 1) << 2) & 15; + // MineHQ end + } + + public void a(int i, int j, int k, int l) { + // MineHQ start + int position = j << 8 | k << 4 | i; // MineHQ + int shift = (position & 1) << 2; + this.a[position >> 1] = (byte) (this.a[position >> 1] & ~(15 << shift) | (l & 15) << shift); + // MineHQ end + } + + // MineHQ start - chunk snapshot api + public NibbleArray clone() { + return new NibbleArray(a.clone(), 4); + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/OldChunkLoader.java b/vspigot-server/src/main/java/net/minecraft/server/OldChunkLoader.java new file mode 100644 index 0000000..fcb9912 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/OldChunkLoader.java @@ -0,0 +1,120 @@ +package net.minecraft.server; + +public class OldChunkLoader { + + public static OldChunk a(NBTTagCompound nbttagcompound) { + int i = nbttagcompound.getInt("xPos"); + int j = nbttagcompound.getInt("zPos"); + OldChunk oldchunk = new OldChunk(i, j); + + oldchunk.g = nbttagcompound.getByteArray("Blocks"); + oldchunk.f = new OldNibbleArray(nbttagcompound.getByteArray("Data"), 7); + oldchunk.e = new OldNibbleArray(nbttagcompound.getByteArray("SkyLight"), 7); + oldchunk.d = new OldNibbleArray(nbttagcompound.getByteArray("BlockLight"), 7); + oldchunk.c = nbttagcompound.getByteArray("HeightMap"); + oldchunk.b = nbttagcompound.getBoolean("TerrainPopulated"); + oldchunk.h = nbttagcompound.getList("Entities", 10); + oldchunk.i = nbttagcompound.getList("TileEntities", 10); + oldchunk.j = nbttagcompound.getList("TileTicks", 10); + + try { + oldchunk.a = nbttagcompound.getLong("LastUpdate"); + } catch (ClassCastException classcastexception) { + oldchunk.a = (long) nbttagcompound.getInt("LastUpdate"); + } + + return oldchunk; + } + + public static void a(OldChunk oldchunk, NBTTagCompound nbttagcompound, WorldChunkManager worldchunkmanager) { + nbttagcompound.setInt("xPos", oldchunk.k); + nbttagcompound.setInt("zPos", oldchunk.l); + nbttagcompound.setLong("LastUpdate", oldchunk.a); + int[] aint = new int[oldchunk.c.length]; + + for (int i = 0; i < oldchunk.c.length; ++i) { + aint[i] = oldchunk.c[i]; + } + + nbttagcompound.setIntArray("HeightMap", aint); + nbttagcompound.setBoolean("TerrainPopulated", oldchunk.b); + NBTTagList nbttaglist = new NBTTagList(); + + int j; + + for (int k = 0; k < 8; ++k) { + boolean flag = true; + + for (j = 0; j < 16 && flag; ++j) { + int l = 0; + + while (l < 16 && flag) { + int i1 = 0; + + while (true) { + if (i1 < 16) { + int j1 = j << 11 | i1 << 7 | l + (k << 4); + byte b0 = oldchunk.g[j1]; + + if (b0 == 0) { + ++i1; + continue; + } + + flag = false; + } + + ++l; + break; + } + } + } + + if (!flag) { + byte[] abyte = new byte[4096]; + NibbleArray nibblearray = new NibbleArray(abyte.length, 4); + NibbleArray nibblearray1 = new NibbleArray(abyte.length, 4); + NibbleArray nibblearray2 = new NibbleArray(abyte.length, 4); + + for (int k1 = 0; k1 < 16; ++k1) { + for (int l1 = 0; l1 < 16; ++l1) { + for (int i2 = 0; i2 < 16; ++i2) { + int j2 = k1 << 11 | i2 << 7 | l1 + (k << 4); + byte b1 = oldchunk.g[j2]; + + abyte[l1 << 8 | i2 << 4 | k1] = (byte) (b1 & 255); + nibblearray.a(k1, l1, i2, oldchunk.f.a(k1, l1 + (k << 4), i2)); + nibblearray1.a(k1, l1, i2, oldchunk.e.a(k1, l1 + (k << 4), i2)); + nibblearray2.a(k1, l1, i2, oldchunk.d.a(k1, l1 + (k << 4), i2)); + } + } + } + + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Y", (byte) (k & 255)); + nbttagcompound1.setByteArray("Blocks", abyte); + nbttagcompound1.setByteArray("Data", nibblearray.a); + nbttagcompound1.setByteArray("SkyLight", nibblearray1.a); + nbttagcompound1.setByteArray("BlockLight", nibblearray2.a); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Sections", nbttaglist); + byte[] abyte1 = new byte[256]; + + for (int k2 = 0; k2 < 16; ++k2) { + for (j = 0; j < 16; ++j) { + abyte1[j << 4 | k2] = (byte) (worldchunkmanager.getBiome(oldchunk.k << 4 | k2, oldchunk.l << 4 | j).id & 255); + } + } + + nbttagcompound.setByteArray("Biomes", abyte1); + nbttagcompound.set("Entities", oldchunk.h); + nbttagcompound.set("TileEntities", oldchunk.i); + if (oldchunk.j != null) { + nbttagcompound.set("TileTicks", oldchunk.j); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Packet.java b/vspigot-server/src/main/java/net/minecraft/server/Packet.java new file mode 100644 index 0000000..7a1e8f0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Packet.java @@ -0,0 +1,90 @@ +package net.minecraft.server; + +import java.io.IOException; + +import net.minecraft.util.com.google.common.collect.BiMap; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public abstract class Packet { + + private static final Logger a = LogManager.getLogger(); + public final long timestamp = System.currentTimeMillis(); // CraftBukkit + + public Packet() {} + + public static Packet a(BiMap bimap, int i) { + try { + Class oclass = (Class) bimap.get(Integer.valueOf(i)); + + return oclass == null ? null : (Packet) oclass.newInstance(); + } catch (Exception exception) { + a.error("Couldn\'t create packet " + i, exception); + return null; + } + } + + public static void a(ByteBuf bytebuf, byte[] abyte) { + // Spigot start - protocol patch + if (bytebuf instanceof PacketDataSerializer) + { + PacketDataSerializer packetDataSerializer = (PacketDataSerializer) bytebuf; + if (packetDataSerializer.version >= 20) { + packetDataSerializer.b( abyte.length ); + } else { + bytebuf.writeShort( abyte.length ); + } + } else + { + bytebuf.writeShort( abyte.length ); + } + // Spigot end + bytebuf.writeBytes(abyte); + } + + public static byte[] a(ByteBuf bytebuf) throws IOException { // CraftBukkit - added throws + // Spigot start - protocol patch + short short1 = 0; + if (bytebuf instanceof PacketDataSerializer) + { + PacketDataSerializer packetDataSerializer = (PacketDataSerializer) bytebuf; + if (packetDataSerializer.version >= 20) { + short1 = (short) packetDataSerializer.a(); + } else { + short1 = bytebuf.readShort(); + } + } else + { + short1 = bytebuf.readShort(); + } + // Spigot end + + if (short1 < 0) { + throw new IOException("Key was smaller than nothing! Weird key!"); + } else { + byte[] abyte = new byte[short1]; + + bytebuf.readBytes(abyte); + return abyte; + } + } + + public abstract void a(PacketDataSerializer packetdataserializer) throws IOException; // CraftBukkit - added throws + + public abstract void b(PacketDataSerializer packetdataserializer) throws IOException; // CraftBukkit - added throws + + public abstract void handle(PacketListener packetlistener); + + public boolean a() { + return false; + } + + public String toString() { + return this.getClass().getSimpleName(); + } + + public String b() { + return ""; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketDataSerializer.java b/vspigot-server/src/main/java/net/minecraft/server/PacketDataSerializer.java new file mode 100644 index 0000000..3793c1c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketDataSerializer.java @@ -0,0 +1,837 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.nio.charset.Charset; +import java.util.UUID; + +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.buffer.ByteBufAllocator; +import net.minecraft.util.io.netty.buffer.ByteBufProcessor; + +import org.bukkit.craftbukkit.inventory.CraftItemStack; // CraftBukkit + +// Spigot start - protocol patch +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import net.minecraft.util.io.netty.buffer.Unpooled; +import net.minecraft.util.io.netty.buffer.ByteBufInputStream; +import net.minecraft.util.io.netty.buffer.ByteBufOutputStream; +import org.spigotmc.SpigotComponentReverter; +// Spigot end + +public class PacketDataSerializer extends ByteBuf { + + private final ByteBuf a; + // Spigot Start + public final int version; + + public PacketDataSerializer(ByteBuf bytebuf) { + this(bytebuf, NetworkManager.CURRENT_VERSION); + } + + public PacketDataSerializer(ByteBuf bytebuf, int version) { + this.a = bytebuf; + this.version = version; + } + + public void writePosition(int x, int y, int z) { + writeLong((((long) x & 0x3FFFFFFL) << 38) + | (((long) y & 0xFFFL) << 26) + | ((long) z & 0x3FFFFFFL)); + } + + public int readPositionX(long val) { + return (int) (val >> 38); + } + + public int readPositionY(long val) { + return (int) (val << 26 >> 52); + } + + public int readPositionZ(long val) { + return (int) (val << 38 >> 38); + } + + public void writeUUID(UUID uuid) { + writeLong(uuid.getMostSignificantBits()); + writeLong(uuid.getLeastSignificantBits()); + } + // Spigot End + + public static int a(int i) { + return (i & -128) == 0 ? 1 : ((i & -16384) == 0 ? 2 : ((i & -2097152) == 0 ? 3 : ((i & -268435456) == 0 ? 4 : 5))); + } + + public int a() { + int i = 0; + int j = 0; + + byte b0; + + do { + b0 = this.readByte(); + i |= (b0 & 127) << j++ * 7; + if (j > 5) { + throw new RuntimeException("VarInt too big"); + } + } while ((b0 & 128) == 128); + + return i; + } + + public void b(int i) { + while ((i & -128) != 0) { + this.writeByte(i & 127 | 128); + i >>>= 7; + } + + this.writeByte(i); + } + + // Spigot start - protocol patch + public void a(NBTTagCompound nbttagcompound) { + if (version < 28) { + if (nbttagcompound == null) { + this.writeShort(-1); + } else { + byte[] abyte = NBTCompressedStreamTools.a(nbttagcompound); + + this.writeShort((short) abyte.length); + this.writeBytes(abyte); + } + } else { + if (nbttagcompound == null) { + this.writeByte(0); + } else { + ByteBufOutputStream out = new ByteBufOutputStream(Unpooled.buffer()); + NBTCompressedStreamTools.a(nbttagcompound, (java.io.DataOutput) new DataOutputStream(out)); + writeBytes(out.buffer()); + out.buffer().release(); + } + } + } + + public NBTTagCompound b() { + if (version < 28) { + short short1 = this.readShort(); + + if (short1 < 0) { + return null; + } else { + byte[] abyte = new byte[short1]; + + this.readBytes(abyte); + return NBTCompressedStreamTools.a(abyte, new NBTReadLimiter(2097152L)); + } + } else { + int index = readerIndex(); + if (readByte() == 0) { + return null; + } + readerIndex(index); + return NBTCompressedStreamTools.a(new ByteBufInputStream(a), new NBTReadLimiter(2097152L)); // PaperSpigot - backport security fix + } + } + // Spigot end + + public void a(ItemStack itemstack) { + if (itemstack == null || itemstack.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem() + this.writeShort(-1); + } else { + // Spigot start - protocol patch + if (version >= 47) { + this.writeShort(org.spigotmc.SpigotDebreakifier.getItemId(Item.getId(itemstack.getItem()))); + } else { + this.writeShort(Item.getId(itemstack.getItem())); + } + // Spigot end + this.writeByte(itemstack.count); + this.writeShort(itemstack.getData()); + NBTTagCompound nbttagcompound = null; + + if (itemstack.getItem().usesDurability() || itemstack.getItem().s()) { + // Spigot start - filter + itemstack = itemstack.cloneItemStack(); + CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack)); + // Spigot end + nbttagcompound = itemstack.tag; + } + + // Spigot start - protocol patch + if (nbttagcompound != null && version >= 29) { + if (itemstack.getItem() == Items.WRITTEN_BOOK && nbttagcompound.hasKeyOfType("pages", 9)) { + nbttagcompound = (NBTTagCompound) nbttagcompound.clone(); + NBTTagList nbttaglist = nbttagcompound.getList("pages", 8); + NBTTagList newList = new NBTTagList(); + for (int i = 0; i < nbttaglist.size(); ++i) { + IChatBaseComponent[] parts = org.bukkit.craftbukkit.util.CraftChatMessage.fromString(nbttaglist.getString(i)); + IChatBaseComponent root = parts[0]; + for (int i1 = 1; i1 < parts.length; i1++) { + IChatBaseComponent c = parts[i1]; + root.a("\n"); + root.addSibling(c); + } + newList.add(new NBTTagString(ChatSerializer.a(root))); + } + nbttagcompound.set("pages", newList); + } + } + // Spigot end + + this.a(nbttagcompound); + } + } + + public ItemStack c() { + ItemStack itemstack = null; + short short1 = this.readShort(); + + if (short1 >= 0) { + byte b0 = this.readByte(); + short short2 = this.readShort(); + + itemstack = new ItemStack(Item.getById(short1), b0, short2); + itemstack.tag = this.b(); + // CraftBukkit start + if (itemstack.tag != null) { + + // Spigot start - protocol patch + if (version >= 29 + && itemstack.getItem() == Items.WRITTEN_BOOK + && itemstack.tag.hasKeyOfType("pages", 9)) { + NBTTagList nbttaglist = itemstack.tag.getList("pages", 8); + NBTTagList newList = new NBTTagList(); + for (int i = 0; i < nbttaglist.size(); ++i) { + IChatBaseComponent s = ChatSerializer.a(nbttaglist.getString(i)); + String newString = SpigotComponentReverter.toLegacy(s); + newList.add(new NBTTagString(newString)); + } + itemstack.tag.set("pages", newList); + } + // Spigot end + + CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack)); + } + // CraftBukkit end + } + + return itemstack; + } + + public String c(int i) throws IOException { // CraftBukkit - throws IOException + int j = this.a(); + + if (j > i * 4) { + throw new IOException("The received encoded string buffer length is longer than maximum allowed (" + j + " > " + i * 4 + ")"); + } else if (j < 0) { + throw new IOException("The received encoded string buffer length is less than zero! Weird string!"); + } else { + String s = new String(this.readBytes(j).array(), Charsets.UTF_8); + + if (s.length() > i) { + throw new IOException("The received string length is longer than maximum allowed (" + j + " > " + i + ")"); + } else { + return s; + } + } + } + + public void a(String s) throws IOException { // CraftBukkit - throws IOException + byte[] abyte = s.getBytes(Charsets.UTF_8); + + if (abyte.length > 32767) { + throw new IOException("String too big (was " + s.length() + " bytes encoded, max " + 32767 + ")"); + } else { + this.b(abyte.length); + this.writeBytes(abyte); + } + } + + public int capacity() { + return this.a.capacity(); + } + + public ByteBuf capacity(int i) { + return this.a.capacity(i); + } + + public int maxCapacity() { + return this.a.maxCapacity(); + } + + public ByteBufAllocator alloc() { + return this.a.alloc(); + } + + public ByteOrder order() { + return this.a.order(); + } + + public ByteBuf order(ByteOrder byteorder) { + return this.a.order(byteorder); + } + + public ByteBuf unwrap() { + return this.a.unwrap(); + } + + public boolean isDirect() { + return this.a.isDirect(); + } + + public int readerIndex() { + return this.a.readerIndex(); + } + + public ByteBuf readerIndex(int i) { + return this.a.readerIndex(i); + } + + public int writerIndex() { + return this.a.writerIndex(); + } + + public ByteBuf writerIndex(int i) { + return this.a.writerIndex(i); + } + + public ByteBuf setIndex(int i, int j) { + return this.a.setIndex(i, j); + } + + public int readableBytes() { + return this.a.readableBytes(); + } + + public int writableBytes() { + return this.a.writableBytes(); + } + + public int maxWritableBytes() { + return this.a.maxWritableBytes(); + } + + public boolean isReadable() { + return this.a.isReadable(); + } + + public boolean isReadable(int i) { + return this.a.isReadable(i); + } + + public boolean isWritable() { + return this.a.isWritable(); + } + + public boolean isWritable(int i) { + return this.a.isWritable(i); + } + + public ByteBuf clear() { + return this.a.clear(); + } + + public ByteBuf markReaderIndex() { + return this.a.markReaderIndex(); + } + + public ByteBuf resetReaderIndex() { + return this.a.resetReaderIndex(); + } + + public ByteBuf markWriterIndex() { + return this.a.markWriterIndex(); + } + + public ByteBuf resetWriterIndex() { + return this.a.resetWriterIndex(); + } + + public ByteBuf discardReadBytes() { + return this.a.discardReadBytes(); + } + + public ByteBuf discardSomeReadBytes() { + return this.a.discardSomeReadBytes(); + } + + public ByteBuf ensureWritable(int i) { + return this.a.ensureWritable(i); + } + + public int ensureWritable(int i, boolean flag) { + return this.a.ensureWritable(i, flag); + } + + public boolean getBoolean(int i) { + return this.a.getBoolean(i); + } + + public byte getByte(int i) { + return this.a.getByte(i); + } + + public short getUnsignedByte(int i) { + return this.a.getUnsignedByte(i); + } + + public short getShort(int i) { + return this.a.getShort(i); + } + + public int getUnsignedShort(int i) { + return this.a.getUnsignedShort(i); + } + + public int getMedium(int i) { + return this.a.getMedium(i); + } + + public int getUnsignedMedium(int i) { + return this.a.getUnsignedMedium(i); + } + + public int getInt(int i) { + return this.a.getInt(i); + } + + public long getUnsignedInt(int i) { + return this.a.getUnsignedInt(i); + } + + public long getLong(int i) { + return this.a.getLong(i); + } + + public char getChar(int i) { + return this.a.getChar(i); + } + + public float getFloat(int i) { + return this.a.getFloat(i); + } + + public double getDouble(int i) { + return this.a.getDouble(i); + } + + public ByteBuf getBytes(int i, ByteBuf bytebuf) { + return this.a.getBytes(i, bytebuf); + } + + public ByteBuf getBytes(int i, ByteBuf bytebuf, int j) { + return this.a.getBytes(i, bytebuf, j); + } + + public ByteBuf getBytes(int i, ByteBuf bytebuf, int j, int k) { + return this.a.getBytes(i, bytebuf, j, k); + } + + public ByteBuf getBytes(int i, byte[] abyte) { + return this.a.getBytes(i, abyte); + } + + public ByteBuf getBytes(int i, byte[] abyte, int j, int k) { + return this.a.getBytes(i, abyte, j, k); + } + + public ByteBuf getBytes(int i, ByteBuffer bytebuffer) { + return this.a.getBytes(i, bytebuffer); + } + + public ByteBuf getBytes(int i, OutputStream outputstream, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.getBytes(i, outputstream, j); + } + + public int getBytes(int i, GatheringByteChannel gatheringbytechannel, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.getBytes(i, gatheringbytechannel, j); + } + + public ByteBuf setBoolean(int i, boolean flag) { + return this.a.setBoolean(i, flag); + } + + public ByteBuf setByte(int i, int j) { + return this.a.setByte(i, j); + } + + public ByteBuf setShort(int i, int j) { + return this.a.setShort(i, j); + } + + public ByteBuf setMedium(int i, int j) { + return this.a.setMedium(i, j); + } + + public ByteBuf setInt(int i, int j) { + return this.a.setInt(i, j); + } + + public ByteBuf setLong(int i, long j) { + return this.a.setLong(i, j); + } + + public ByteBuf setChar(int i, int j) { + return this.a.setChar(i, j); + } + + public ByteBuf setFloat(int i, float f) { + return this.a.setFloat(i, f); + } + + public ByteBuf setDouble(int i, double d0) { + return this.a.setDouble(i, d0); + } + + public ByteBuf setBytes(int i, ByteBuf bytebuf) { + return this.a.setBytes(i, bytebuf); + } + + public ByteBuf setBytes(int i, ByteBuf bytebuf, int j) { + return this.a.setBytes(i, bytebuf, j); + } + + public ByteBuf setBytes(int i, ByteBuf bytebuf, int j, int k) { + return this.a.setBytes(i, bytebuf, j, k); + } + + public ByteBuf setBytes(int i, byte[] abyte) { + return this.a.setBytes(i, abyte); + } + + public ByteBuf setBytes(int i, byte[] abyte, int j, int k) { + return this.a.setBytes(i, abyte, j, k); + } + + public ByteBuf setBytes(int i, ByteBuffer bytebuffer) { + return this.a.setBytes(i, bytebuffer); + } + + public int setBytes(int i, InputStream inputstream, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.setBytes(i, inputstream, j); + } + + public int setBytes(int i, ScatteringByteChannel scatteringbytechannel, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.setBytes(i, scatteringbytechannel, j); + } + + public ByteBuf setZero(int i, int j) { + return this.a.setZero(i, j); + } + + public boolean readBoolean() { + return this.a.readBoolean(); + } + + public byte readByte() { + return this.a.readByte(); + } + + public short readUnsignedByte() { + return this.a.readUnsignedByte(); + } + + public short readShort() { + return this.a.readShort(); + } + + public int readUnsignedShort() { + return this.a.readUnsignedShort(); + } + + public int readMedium() { + return this.a.readMedium(); + } + + public int readUnsignedMedium() { + return this.a.readUnsignedMedium(); + } + + public int readInt() { + return this.a.readInt(); + } + + public long readUnsignedInt() { + return this.a.readUnsignedInt(); + } + + public long readLong() { + return this.a.readLong(); + } + + public char readChar() { + return this.a.readChar(); + } + + public float readFloat() { + return this.a.readFloat(); + } + + public double readDouble() { + return this.a.readDouble(); + } + + public ByteBuf readBytes(int i) { + return this.a.readBytes(i); + } + + public ByteBuf readSlice(int i) { + return this.a.readSlice(i); + } + + public ByteBuf readBytes(ByteBuf bytebuf) { + return this.a.readBytes(bytebuf); + } + + public ByteBuf readBytes(ByteBuf bytebuf, int i) { + return this.a.readBytes(bytebuf, i); + } + + public ByteBuf readBytes(ByteBuf bytebuf, int i, int j) { + return this.a.readBytes(bytebuf, i, j); + } + + public ByteBuf readBytes(byte[] abyte) { + return this.a.readBytes(abyte); + } + + public ByteBuf readBytes(byte[] abyte, int i, int j) { + return this.a.readBytes(abyte, i, j); + } + + public ByteBuf readBytes(ByteBuffer bytebuffer) { + return this.a.readBytes(bytebuffer); + } + + public ByteBuf readBytes(OutputStream outputstream, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.readBytes(outputstream, i); + } + + public int readBytes(GatheringByteChannel gatheringbytechannel, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.readBytes(gatheringbytechannel, i); + } + + public ByteBuf skipBytes(int i) { + return this.a.skipBytes(i); + } + + public ByteBuf writeBoolean(boolean flag) { + return this.a.writeBoolean(flag); + } + + public ByteBuf writeByte(int i) { + return this.a.writeByte(i); + } + + public ByteBuf writeShort(int i) { + return this.a.writeShort(i); + } + + public ByteBuf writeMedium(int i) { + return this.a.writeMedium(i); + } + + public ByteBuf writeInt(int i) { + return this.a.writeInt(i); + } + + public ByteBuf writeLong(long i) { + return this.a.writeLong(i); + } + + public ByteBuf writeChar(int i) { + return this.a.writeChar(i); + } + + public ByteBuf writeFloat(float f) { + return this.a.writeFloat(f); + } + + public ByteBuf writeDouble(double d0) { + return this.a.writeDouble(d0); + } + + public ByteBuf writeBytes(ByteBuf bytebuf) { + return this.a.writeBytes(bytebuf); + } + + public ByteBuf writeBytes(ByteBuf bytebuf, int i) { + return this.a.writeBytes(bytebuf, i); + } + + public ByteBuf writeBytes(ByteBuf bytebuf, int i, int j) { + return this.a.writeBytes(bytebuf, i, j); + } + + public ByteBuf writeBytes(byte[] abyte) { + return this.a.writeBytes(abyte); + } + + public ByteBuf writeBytes(byte[] abyte, int i, int j) { + return this.a.writeBytes(abyte, i, j); + } + + public ByteBuf writeBytes(ByteBuffer bytebuffer) { + return this.a.writeBytes(bytebuffer); + } + + public int writeBytes(InputStream inputstream, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.writeBytes(inputstream, i); + } + + public int writeBytes(ScatteringByteChannel scatteringbytechannel, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.writeBytes(scatteringbytechannel, i); + } + + public ByteBuf writeZero(int i) { + return this.a.writeZero(i); + } + + public int indexOf(int i, int j, byte b0) { + return this.a.indexOf(i, j, b0); + } + + public int bytesBefore(byte b0) { + return this.a.bytesBefore(b0); + } + + public int bytesBefore(int i, byte b0) { + return this.a.bytesBefore(i, b0); + } + + public int bytesBefore(int i, int j, byte b0) { + return this.a.bytesBefore(i, j, b0); + } + + public int forEachByte(ByteBufProcessor bytebufprocessor) { + return this.a.forEachByte(bytebufprocessor); + } + + public int forEachByte(int i, int j, ByteBufProcessor bytebufprocessor) { + return this.a.forEachByte(i, j, bytebufprocessor); + } + + public int forEachByteDesc(ByteBufProcessor bytebufprocessor) { + return this.a.forEachByteDesc(bytebufprocessor); + } + + public int forEachByteDesc(int i, int j, ByteBufProcessor bytebufprocessor) { + return this.a.forEachByteDesc(i, j, bytebufprocessor); + } + + public ByteBuf copy() { + return this.a.copy(); + } + + public ByteBuf copy(int i, int j) { + return this.a.copy(i, j); + } + + public ByteBuf slice() { + return this.a.slice(); + } + + public ByteBuf slice(int i, int j) { + return this.a.slice(i, j); + } + + public ByteBuf duplicate() { + return this.a.duplicate(); + } + + public int nioBufferCount() { + return this.a.nioBufferCount(); + } + + public ByteBuffer nioBuffer() { + return this.a.nioBuffer(); + } + + public ByteBuffer nioBuffer(int i, int j) { + return this.a.nioBuffer(i, j); + } + + public ByteBuffer internalNioBuffer(int i, int j) { + return this.a.internalNioBuffer(i, j); + } + + public ByteBuffer[] nioBuffers() { + return this.a.nioBuffers(); + } + + public ByteBuffer[] nioBuffers(int i, int j) { + return this.a.nioBuffers(i, j); + } + + public boolean hasArray() { + return this.a.hasArray(); + } + + public byte[] array() { + return this.a.array(); + } + + public int arrayOffset() { + return this.a.arrayOffset(); + } + + public boolean hasMemoryAddress() { + return this.a.hasMemoryAddress(); + } + + public long memoryAddress() { + return this.a.memoryAddress(); + } + + public String toString(Charset charset) { + return this.a.toString(charset); + } + + public String toString(int i, int j, Charset charset) { + return this.a.toString(i, j, charset); + } + + public int hashCode() { + return this.a.hashCode(); + } + + public boolean equals(Object object) { + return this.a.equals(object); + } + + public int compareTo(ByteBuf bytebuf) { + return this.a.compareTo(bytebuf); + } + + public String toString() { + return this.a.toString(); + } + + public ByteBuf retain(int i) { + return this.a.retain(i); + } + + public ByteBuf retain() { + return this.a.retain(); + } + + public int refCnt() { + return this.a.refCnt(); + } + + public boolean release() { + return this.a.release(); + } + + public boolean release(int i) { + return this.a.release(i); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketDecoder.java b/vspigot-server/src/main/java/net/minecraft/server/PacketDecoder.java new file mode 100644 index 0000000..5213f1f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketDecoder.java @@ -0,0 +1,49 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.util.List; + +import net.minecraft.util.com.google.common.collect.BiMap; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.channel.ChannelHandlerContext; +import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; + +public class PacketDecoder extends ByteToMessageDecoder { + + private static final Logger a = LogManager.getLogger(); + private static final Marker b = MarkerManager.getMarker("PACKET_RECEIVED", NetworkManager.b); + private final NetworkStatistics c; + + public PacketDecoder(NetworkStatistics networkstatistics) { + this.c = networkstatistics; + } + + protected void decode(ChannelHandlerContext channelhandlercontext, ByteBuf bytebuf, List list) throws IOException { + int i = bytebuf.readableBytes(); + + if (i != 0) { + PacketDataSerializer packetdataserializer = new PacketDataSerializer( bytebuf, NetworkManager.getVersion( channelhandlercontext.channel() ) ); // Spigot + int j = packetdataserializer.a(); + Packet packet = Packet.a((BiMap) channelhandlercontext.channel().attr(NetworkManager.e).get(), j); + + if (packet == null) { + throw new IOException("Bad packet id " + j); + } else { + packet.a(packetdataserializer); + if (packetdataserializer.readableBytes() > 0) { + throw new IOException("Packet was larger than I expected, found " + packetdataserializer.readableBytes() + " bytes extra whilst reading packet " + j); + } else { + list.add(packet); + this.c.a(j, (long) i); + if (a.isDebugEnabled()) { + a.debug(b, " IN: [{}:{}] {}[{}]", new Object[] { channelhandlercontext.channel().attr(NetworkManager.d).get(), Integer.valueOf(j), packet.getClass().getName(), packet.b()}); + } + } + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketEncoder.java b/vspigot-server/src/main/java/net/minecraft/server/PacketEncoder.java new file mode 100644 index 0000000..7994daa --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketEncoder.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +import java.io.IOException; + +import net.minecraft.util.com.google.common.collect.BiMap; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.channel.ChannelHandlerContext; +import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; + +public class PacketEncoder extends MessageToByteEncoder { + + private static final Logger a = LogManager.getLogger(); + private static final Marker b = MarkerManager.getMarker("PACKET_SENT", NetworkManager.b); + private final NetworkStatistics c; + + public PacketEncoder(NetworkStatistics networkstatistics) { + this.c = networkstatistics; + } + + protected void a(ChannelHandlerContext channelhandlercontext, Packet packet, ByteBuf bytebuf) throws IOException { + Integer integer = (Integer) ((BiMap) channelhandlercontext.channel().attr(NetworkManager.f).get()).inverse().get(packet.getClass()); + + if (a.isDebugEnabled()) { + a.debug(b, "OUT: [{}:{}] {}[{}]", new Object[] { channelhandlercontext.channel().attr(NetworkManager.d).get(), integer, packet.getClass().getName(), packet.b()}); + } + + if (integer == null) { + throw new IOException("Can\'t serialize unregistered packet"); + } else { + PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf, NetworkManager.getVersion(channelhandlercontext.channel())); // Spigot + + packetdataserializer.b(integer.intValue()); + packet.b(packetdataserializer); + this.c.b(integer.intValue(), (long) packetdataserializer.readableBytes()); + } + } + + protected void encode(ChannelHandlerContext channelhandlercontext, Object object, ByteBuf bytebuf) throws IOException { + this.a(channelhandlercontext, (Packet) object, bytebuf); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketHandshakingInSetProtocol.java b/vspigot-server/src/main/java/net/minecraft/server/PacketHandshakingInSetProtocol.java new file mode 100644 index 0000000..39692ee --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketHandshakingInSetProtocol.java @@ -0,0 +1,47 @@ +package net.minecraft.server; + +import java.io.IOException; // CraftBukkit + +public class PacketHandshakingInSetProtocol extends Packet { + + private int a; + public String b; // CraftBukkit private -> public + public int c; // CraftBukkit private -> public + private EnumProtocol d; + + public PacketHandshakingInSetProtocol() {} + + public void a(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + this.a = packetdataserializer.a(); + this.b = packetdataserializer.c(Short.MAX_VALUE); // Spigot + this.c = packetdataserializer.readUnsignedShort(); + this.d = EnumProtocol.a(packetdataserializer.a()); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + packetdataserializer.b(this.a); + packetdataserializer.a(this.b); + packetdataserializer.writeShort(this.c); + packetdataserializer.b(this.d.c()); + } + + public void a(PacketHandshakingInListener packethandshakinginlistener) { + packethandshakinginlistener.a(this); + } + + public boolean a() { + return true; + } + + public EnumProtocol c() { + return this.d; + } + + public int d() { + return this.a; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketHandshakingInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketLoginOutSuccess.java b/vspigot-server/src/main/java/net/minecraft/server/PacketLoginOutSuccess.java new file mode 100644 index 0000000..a244f00 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketLoginOutSuccess.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.util.UUID; + +import net.minecraft.util.com.mojang.authlib.GameProfile; + +public class PacketLoginOutSuccess extends Packet { + + private GameProfile a; + + public PacketLoginOutSuccess() {} + + public PacketLoginOutSuccess(GameProfile gameprofile) { + this.a = gameprofile; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + String s = packetdataserializer.c(36); + String s1 = packetdataserializer.c(16); + UUID uuid = UUID.fromString(s); + + this.a = new GameProfile(uuid, s1); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + UUID uuid = this.a.getId(); + + packetdataserializer.a(uuid == null ? "" : ( ( packetdataserializer.version >= 5 ) ? uuid.toString() : uuid.toString().replaceAll( "-", "" ) ) ); + packetdataserializer.a(this.a.getName()); + } + + public void a(PacketLoginOutListener packetloginoutlistener) { + packetloginoutlistener.a(this); + } + + public boolean a() { + return true; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketLoginOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInArmAnimation.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInArmAnimation.java new file mode 100644 index 0000000..91d9e58 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInArmAnimation.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +public class PacketPlayInArmAnimation extends Packet { + + private int a; + private int b; + + public PacketPlayInArmAnimation() {} + + public void a(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readByte(); + } else { + b = 1; + } + // Spigot end + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeInt(this.a); + packetdataserializer.writeByte(this.b); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public String b() { + return String.format("id=%d, type=%d", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.b)}); + } + + public int d() { + return this.b; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInBlockDig.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInBlockDig.java new file mode 100644 index 0000000..e85ba8c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInBlockDig.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +public class PacketPlayInBlockDig extends Packet { + + private int a; + private int b; + private int c; + private int face; + private int e; + + public PacketPlayInBlockDig() {} + + public void a(PacketDataSerializer packetdataserializer) { + this.e = packetdataserializer.readUnsignedByte(); + // Spigot start + if ( packetdataserializer.version < 16) + { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readUnsignedByte(); + this.c = packetdataserializer.readInt(); + } else + { + long position = packetdataserializer.readLong(); + a = packetdataserializer.readPositionX( position ); + b = packetdataserializer.readPositionY( position ); + c = packetdataserializer.readPositionZ( position ); + } + // Spigot end + this.face = packetdataserializer.readUnsignedByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeByte(this.e); + packetdataserializer.writeInt(this.a); + packetdataserializer.writeByte(this.b); + packetdataserializer.writeInt(this.c); + packetdataserializer.writeByte(this.face); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public int c() { + return this.a; + } + + public int d() { + return this.b; + } + + public int e() { + return this.c; + } + + public int f() { + return this.face; + } + + public int g() { + return this.e; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java new file mode 100644 index 0000000..b9cea4c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java @@ -0,0 +1,88 @@ +package net.minecraft.server; + +public class PacketPlayInBlockPlace extends Packet { + + private int a; + private int b; + private int c; + private int d; + private ItemStack e; + private float f; + private float g; + private float h; + + public PacketPlayInBlockPlace() {} + + public void a(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readUnsignedByte(); + this.c = packetdataserializer.readInt(); + } else + { + long position = packetdataserializer.readLong(); + a = packetdataserializer.readPositionX( position ); + b = packetdataserializer.readPositionY( position ); + c = packetdataserializer.readPositionZ( position ); + } + // Spigot end + this.d = packetdataserializer.readUnsignedByte(); + this.e = packetdataserializer.c(); + this.f = (float) packetdataserializer.readUnsignedByte() / 16.0F; + this.g = (float) packetdataserializer.readUnsignedByte() / 16.0F; + this.h = (float) packetdataserializer.readUnsignedByte() / 16.0F; + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeInt(this.a); + packetdataserializer.writeByte(this.b); + packetdataserializer.writeInt(this.c); + packetdataserializer.writeByte(this.d); + packetdataserializer.a(this.e); + packetdataserializer.writeByte((int) (this.f * 16.0F)); + packetdataserializer.writeByte((int) (this.g * 16.0F)); + packetdataserializer.writeByte((int) (this.h * 16.0F)); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public int c() { + return this.a; + } + + public int d() { + return this.b; + } + + public int e() { + return this.c; + } + + public int getFace() { + return this.d; + } + + public ItemStack getItemStack() { + return this.e; + } + + public float h() { + return this.f; + } + + public float i() { + return this.g; + } + + public float j() { + return this.h; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInChat.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInChat.java new file mode 100644 index 0000000..5fdb4cc --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInChat.java @@ -0,0 +1,76 @@ +package net.minecraft.server; + +import java.io.IOException; // CraftBukkit + +import net.valorhcf.ThreadingManager; // Poweruser +import net.valorhcf.ThreadingManager.TaskQueueWorker; // Poweruser + +public class PacketPlayInChat extends Packet { + + private String message; + + public PacketPlayInChat() {} + + public PacketPlayInChat(String s) { + if (s.length() > 100) { + s = s.substring(0, 100); + } + + this.message = s; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + this.message = packetdataserializer.c(100); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + packetdataserializer.a(this.message); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public String b() { + return String.format("message=\'%s\'", new Object[] { this.message}); + } + + public String c() { + return this.message; + } + + // CraftBukkit start - make chat async + @Override + public boolean a() { + return !this.message.startsWith("/"); + } + // CraftBukkit end + + private static TaskQueueWorker taskQueue; // Poweruser + // Spigot Start + public void handle(final PacketListener packetlistener) + { + if ( a() ) + { + // Poweruser start + TaskQueueWorker worker = taskQueue; + if(worker == null) { + taskQueue = worker = ThreadingManager.createTaskQueue(); + } + + worker.queueTask(new Runnable() // Poweruser + // Poweruser end + { + + @Override + public void run() + { + PacketPlayInChat.this.a( (PacketPlayInListener) packetlistener ); + } + } ); + return; + } + // Spigot End + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java new file mode 100644 index 0000000..43df03a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java @@ -0,0 +1,29 @@ +package net.minecraft.server; + +public class PacketPlayInCloseWindow extends Packet { + + private int a; + + public PacketPlayInCloseWindow() {} + + // CraftBukkit start - Add constructor + public PacketPlayInCloseWindow(int id) { + this.a = id; + } + // CraftBukkit end + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeByte(this.a); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInCustomPayload.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInCustomPayload.java new file mode 100644 index 0000000..54ac5b7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInCustomPayload.java @@ -0,0 +1,53 @@ +package net.minecraft.server; + +import java.io.IOException; // CraftBukkit + +public class PacketPlayInCustomPayload extends Packet { + + private String tag; + public int length; // CraftBukkit - private -> public + private byte[] data; + + public PacketPlayInCustomPayload() {} + + public void a(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + this.tag = packetdataserializer.c(20); + // Spigot start - protocol patch + if ( packetdataserializer.version < 29 ) + { + this.length = packetdataserializer.readShort(); + } else + { + this.length = packetdataserializer.readableBytes(); + } + // Spigot end + if (this.length > 0 && this.length < 32767) { + this.data = new byte[this.length]; + packetdataserializer.readBytes(this.data); + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + packetdataserializer.a(this.tag); + packetdataserializer.writeShort((short) this.length); + if (this.data != null) { + packetdataserializer.writeBytes(this.data); + } + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public String c() { + return this.tag; + } + + public byte[] e() { + return this.data; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInEntityAction.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInEntityAction.java new file mode 100644 index 0000000..5e4a377 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInEntityAction.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +public class PacketPlayInEntityAction extends Packet { + + private int a; + private int animation; + private int c; + + public PacketPlayInEntityAction() {} + + public void a(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + this.a = packetdataserializer.readInt(); + this.animation = packetdataserializer.readByte(); + this.c = packetdataserializer.readInt(); + } else + { + a = packetdataserializer.a(); + animation = packetdataserializer.readUnsignedByte() + 1; + c = packetdataserializer.a(); + } + // Spigot end + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeInt(this.a); + packetdataserializer.writeByte(this.animation); + packetdataserializer.writeInt(this.c); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public int d() { + return this.animation; + } + + public int e() { + return this.c; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInKeepAlive.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInKeepAlive.java new file mode 100644 index 0000000..5c88cf1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInKeepAlive.java @@ -0,0 +1,40 @@ +package net.minecraft.server; + +public class PacketPlayInKeepAlive extends Packet { + + private int a; + + public PacketPlayInKeepAlive() {} + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + this.a = packetdataserializer.readInt(); + } else + { + this.a = packetdataserializer.a(); + } + // Spigot end + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeInt(this.a); + } + + public boolean a() { + return true; + } + + public int c() { + return this.a; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInPosition.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInPosition.java new file mode 100644 index 0000000..2f3a2ec --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInPosition.java @@ -0,0 +1,37 @@ +package net.minecraft.server; + +public class PacketPlayInPosition extends PacketPlayInFlying { + + public PacketPlayInPosition() { + this.hasPos = true; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.x = packetdataserializer.readDouble(); + // Spigot start - protocol patch + if (packetdataserializer.version < 16) + { + this.y = packetdataserializer.readDouble(); + this.stance = packetdataserializer.readDouble(); + } else + { + this.y = packetdataserializer.readDouble(); + this.stance = y + 1.62; + } + // Spigot end + this.z = packetdataserializer.readDouble(); + super.a(packetdataserializer); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeDouble(this.x); + packetdataserializer.writeDouble(this.y); + packetdataserializer.writeDouble(this.stance); + packetdataserializer.writeDouble(this.z); + super.b(packetdataserializer); + } + + public void handle(PacketListener packetlistener) { + super.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInPositionLook.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInPositionLook.java new file mode 100644 index 0000000..0266592 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInPositionLook.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +public class PacketPlayInPositionLook extends PacketPlayInFlying { + + public PacketPlayInPositionLook() { + this.hasPos = true; + this.hasLook = true; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.x = packetdataserializer.readDouble(); + // Spigot start - protocol patch + if (packetdataserializer.version < 16) + { + this.y = packetdataserializer.readDouble(); + this.stance = packetdataserializer.readDouble(); + } else + { + this.y = packetdataserializer.readDouble(); + this.stance = y + 1.62; + } + // Spigot end + this.z = packetdataserializer.readDouble(); + this.yaw = packetdataserializer.readFloat(); + this.pitch = packetdataserializer.readFloat(); + super.a(packetdataserializer); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeDouble(this.x); + packetdataserializer.writeDouble(this.y); + packetdataserializer.writeDouble(this.stance); + packetdataserializer.writeDouble(this.z); + packetdataserializer.writeFloat(this.yaw); + packetdataserializer.writeFloat(this.pitch); + super.b(packetdataserializer); + } + + public void handle(PacketListener packetlistener) { + super.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInSettings.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInSettings.java new file mode 100644 index 0000000..ea51d91 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInSettings.java @@ -0,0 +1,83 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayInSettings extends Packet { + + private String a; + private int b; + private EnumChatVisibility c; + private boolean d; + private EnumDifficulty e; + private boolean f; + + // Spigot start - protocol patch + public int version; + public int flags; + // Spigot end + + public PacketPlayInSettings() {} + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.c(7); + this.b = packetdataserializer.readByte(); + this.c = EnumChatVisibility.a(packetdataserializer.readByte()); + this.d = packetdataserializer.readBoolean(); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + this.e = EnumDifficulty.getById( packetdataserializer.readByte() ); + this.f = packetdataserializer.readBoolean(); + } else + { + flags = packetdataserializer.readUnsignedByte(); + } + version = packetdataserializer.version; + // Spigot end + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a(this.a); + packetdataserializer.writeByte(this.b); + packetdataserializer.writeByte(this.c.a()); + packetdataserializer.writeBoolean(this.d); + packetdataserializer.writeByte(this.e.a()); + packetdataserializer.writeBoolean(this.f); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public String c() { + return this.a; + } + + public int d() { + return this.b; + } + + public EnumChatVisibility e() { + return this.c; + } + + public boolean f() { + return this.d; + } + + public EnumDifficulty g() { + return this.e; + } + + public boolean h() { + return this.f; + } + + public String b() { + return String.format("lang=\'%s\', view=%d, chat=%s, col=%b, difficulty=%s, cape=%b", new Object[] { this.a, Integer.valueOf(this.b), this.c, Boolean.valueOf(this.d), this.e, Boolean.valueOf(this.f)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInSteerVehicle.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInSteerVehicle.java new file mode 100644 index 0000000..c26bf77 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInSteerVehicle.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +public class PacketPlayInSteerVehicle extends Packet { + + private float a; + private float b; + private boolean c; + private boolean d; + + public PacketPlayInSteerVehicle() {} + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readFloat(); + this.b = packetdataserializer.readFloat(); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + this.c = packetdataserializer.readBoolean(); + this.d = packetdataserializer.readBoolean(); + } else { + int flags = packetdataserializer.readUnsignedByte(); + c = (flags & 0x1) != 0; + d = (flags & 0x2) != 0; + } + // Spigot end + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeFloat(this.a); + packetdataserializer.writeFloat(this.b); + packetdataserializer.writeBoolean(this.c); + packetdataserializer.writeBoolean(this.d); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public float c() { + return this.a; + } + + public float d() { + return this.b; + } + + public boolean e() { + return this.c; + } + + public boolean f() { + return this.d; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInTabComplete.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInTabComplete.java new file mode 100644 index 0000000..5909876 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInTabComplete.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +import net.minecraft.util.org.apache.commons.lang3.StringUtils; + +import java.io.IOException; + +public class PacketPlayInTabComplete extends Packet { + + private String a; + + public PacketPlayInTabComplete() {} + + public PacketPlayInTabComplete(String s) { + this.a = s; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.c(32767); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 37 ) + { + if (packetdataserializer.readBoolean()) { + long position = packetdataserializer.readLong(); + } + } + // Spigot end + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a(StringUtils.substring(this.a, 0, 32767)); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public String c() { + return this.a; + } + + public String b() { + return String.format("message=\'%s\'", new Object[] { this.a}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInUpdateSign.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInUpdateSign.java new file mode 100644 index 0000000..f6cf5c8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInUpdateSign.java @@ -0,0 +1,80 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayInUpdateSign extends Packet { + + private int a; + private int b; + private int c; + private String[] d; + + public PacketPlayInUpdateSign() {} + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readShort(); + this.c = packetdataserializer.readInt(); + } else + { + long position = packetdataserializer.readLong(); + a = packetdataserializer.readPositionX( position ); + b = packetdataserializer.readPositionY( position ); + c = packetdataserializer.readPositionZ( position ); + } + // Spigot end + this.d = new String[4]; + + for (int i = 0; i < 4; ++i) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 21 ) + { + this.d[ i ] = packetdataserializer.c( 15 ); + } else + { + this.d[ i ] = ChatSerializer.a( packetdataserializer.c( Short.MAX_VALUE ) ).c(); + } + if (this.d[i].length() > 15) { + this.d[i] = this.d[i].substring( 0, 15 ); + } + // Spigot end + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.writeInt(this.a); + packetdataserializer.writeShort(this.b); + packetdataserializer.writeInt(this.c); + + for (int i = 0; i < 4; ++i) { + packetdataserializer.a(this.d[i]); + } + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public int c() { + return this.a; + } + + public int d() { + return this.b; + } + + public int e() { + return this.c; + } + + public String[] f() { + return this.d; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInUseEntity.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInUseEntity.java new file mode 100644 index 0000000..6dcf324 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInUseEntity.java @@ -0,0 +1,50 @@ +package net.minecraft.server; + +public class PacketPlayInUseEntity extends Packet { + + public int a; + private EnumEntityUseAction action; + + public PacketPlayInUseEntity() { + } + + public void a(PacketDataSerializer packetdataserializer) { + // Spigot start + if (packetdataserializer.version < 16) { + this.a = packetdataserializer.readInt(); + this.action = EnumEntityUseAction.values()[packetdataserializer.readByte() % EnumEntityUseAction.values().length]; + } else { + this.a = packetdataserializer.a(); + int val = packetdataserializer.a(); + if (val == 2) { + packetdataserializer.readFloat(); + packetdataserializer.readFloat(); + packetdataserializer.readFloat(); + } else { + this.action = EnumEntityUseAction.values()[val % EnumEntityUseAction.values().length]; + } + } + // Spigot end + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeInt(this.a); + packetdataserializer.writeByte(this.action.ordinal()); + } + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public Entity a(World world) { + return world.getEntity(this.a); + } + + public EnumEntityUseAction c() { + return this.action; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInWindowClick.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInWindowClick.java new file mode 100644 index 0000000..87daa8e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayInWindowClick.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +public class PacketPlayInWindowClick extends Packet { + + private int a; + public int slot; // Spigot + private int button; + private short d; + private ItemStack item; + private int shift; + + public PacketPlayInWindowClick() {} + + public void a(PacketPlayInListener packetplayinlistener) { + packetplayinlistener.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readByte(); + this.slot = packetdataserializer.readShort(); + this.button = packetdataserializer.readByte(); + this.d = packetdataserializer.readShort(); + this.shift = packetdataserializer.readByte(); + this.item = packetdataserializer.c(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeByte(this.a); + packetdataserializer.writeShort(this.slot); + packetdataserializer.writeByte(this.button); + packetdataserializer.writeShort(this.d); + packetdataserializer.writeByte(this.shift); + packetdataserializer.a(this.item); + } + + public String b() { + return this.item != null ? String.format("id=%d, slot=%d, button=%d, type=%d, itemid=%d, itemcount=%d, itemaux=%d", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.slot), Integer.valueOf(this.button), Integer.valueOf(this.shift), Integer.valueOf(Item.getId(this.item.getItem())), Integer.valueOf(this.item.count), Integer.valueOf(this.item.getData())}) : String.format("id=%d, slot=%d, button=%d, type=%d, itemid=-1", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.slot), Integer.valueOf(this.button), Integer.valueOf(this.shift)}); + } + + public int c() { + return this.a; + } + + public int d() { + return this.slot; + } + + public int e() { + return this.button; + } + + public short f() { + return this.d; + } + + public ItemStack g() { + return this.item; + } + + public int h() { + return this.shift; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayInListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBed.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBed.java new file mode 100644 index 0000000..b361f38 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBed.java @@ -0,0 +1,49 @@ +package net.minecraft.server; + +public class PacketPlayOutBed extends Packet { + + private int a; + private int b; + private int c; + private int d; + + public PacketPlayOutBed() {} + + public PacketPlayOutBed(EntityHuman entityhuman, int i, int j, int k) { + this.b = i; + this.c = j; + this.d = k; + this.a = entityhuman.getId(); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readInt(); + this.c = packetdataserializer.readByte(); + this.d = packetdataserializer.readInt(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + packetdataserializer.writeInt( this.b ); + packetdataserializer.writeByte( this.c ); + packetdataserializer.writeInt( this.d ); + } else + { + packetdataserializer.b( a ); + packetdataserializer.writePosition( b, c, d ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockAction.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockAction.java new file mode 100644 index 0000000..959d052 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockAction.java @@ -0,0 +1,56 @@ +package net.minecraft.server; + +public class PacketPlayOutBlockAction extends Packet { + + private int a; + private int b; + private int c; + private int d; + private int e; + private Block f; + + public PacketPlayOutBlockAction() {} + + public PacketPlayOutBlockAction(int i, int j, int k, Block block, int l, int i1) { + this.a = i; + this.b = j; + this.c = k; + this.d = l; + this.e = i1; + this.f = block; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readShort(); + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readUnsignedByte(); + this.e = packetdataserializer.readUnsignedByte(); + this.f = Block.getById(packetdataserializer.a() & 4095); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + packetdataserializer.writeShort( this.b ); + packetdataserializer.writeInt( this.c ); + } else + { + packetdataserializer.writePosition( a, b, c ); + } + // Spigot end + packetdataserializer.writeByte(this.d); + packetdataserializer.writeByte(this.e); + packetdataserializer.b(Block.getId(this.f) & 4095); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockBreakAnimation.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockBreakAnimation.java new file mode 100644 index 0000000..1d70129 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockBreakAnimation.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +public class PacketPlayOutBlockBreakAnimation extends Packet { + + private int a; + private int b; + private int c; + private int d; + private int e; + + public PacketPlayOutBlockBreakAnimation() {} + + public PacketPlayOutBlockBreakAnimation(int i, int j, int k, int l, int i1) { + this.a = i; + this.b = j; + this.c = k; + this.d = l; + this.e = i1; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.a(); + this.b = packetdataserializer.readInt(); + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readInt(); + this.e = packetdataserializer.readUnsignedByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.b(this.a); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.b ); + packetdataserializer.writeInt( this.c ); + packetdataserializer.writeInt( this.d ); + } else + { + packetdataserializer.writePosition( b, c, d ); + } + // Spigot end + packetdataserializer.writeByte(this.e); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockChange.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockChange.java new file mode 100644 index 0000000..cf9680a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutBlockChange.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +public class PacketPlayOutBlockChange extends Packet { + + public int a; + public int b; + public int c; + public Block block; // CraftBukkit - public + public int data; // CraftBukkit - public + public boolean fake = false; + + public PacketPlayOutBlockChange() {} + + public PacketPlayOutBlockChange(int i, int j, int k, World world) { + this.a = i; + this.b = j; + this.c = k; + this.block = world.getType(i, j, k); + this.data = world.getData(i, j, k); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readUnsignedByte(); + this.c = packetdataserializer.readInt(); + this.block = Block.getById(packetdataserializer.a()); + this.data = packetdataserializer.readUnsignedByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 25 ) + { + packetdataserializer.writeInt( this.a ); + packetdataserializer.writeByte( this.b ); + packetdataserializer.writeInt( this.c ); + packetdataserializer.b( Block.getId( this.block ) ); + packetdataserializer.writeByte(this.data); + } else + { + packetdataserializer.writePosition( a, b, c ); + int id = Block.getId( this.block ); + data = org.spigotmc.SpigotDebreakifier.getCorrectedData( id, data ); + packetdataserializer.b( (id << 4) | this.data ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("type=%d, data=%d, x=%d, y=%d, z=%d", new Object[] { Integer.valueOf(Block.getId(this.block)), Integer.valueOf(this.data), Integer.valueOf(this.a), Integer.valueOf(this.b), Integer.valueOf(this.c)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutChat.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutChat.java new file mode 100644 index 0000000..db6cfe0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutChat.java @@ -0,0 +1,76 @@ +package net.minecraft.server; + +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.ComponentSerializer; + +import java.io.IOException; + +public class PacketPlayOutChat extends Packet { + + private IChatBaseComponent a; + public BaseComponent[] components; // Spigot - Update 20140909b + private boolean b; + private int pos; // Spigot - Update 20141001a + + public PacketPlayOutChat() { + this.b = true; + } + + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent) { + this(ichatbasecomponent, true); + } + + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent, int pos) { + this(ichatbasecomponent, pos, true); + } + + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent, boolean flag) { + this(ichatbasecomponent, 0, flag); + } + + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent, int pos, boolean flag) { + this.b = true; + this.a = ichatbasecomponent; + this.b = flag; + this.pos = pos; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = ChatSerializer.a(packetdataserializer.c(32767)); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + // Spigot start - Update 20140909b + if (components != null) + { + packetdataserializer.a( ComponentSerializer.toString(components) ); + } + else { + packetdataserializer.a( ChatSerializer.a(a) ); + } + // Spigot end + + // Spigot start - protocol patch + if ( packetdataserializer.version >= 16 ) + { + packetdataserializer.writeByte( this.pos ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("message=\'%s\'", new Object[] { this.a}); + } + + public boolean d() { + return this.b; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutCollect.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutCollect.java new file mode 100644 index 0000000..8eff9e5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutCollect.java @@ -0,0 +1,41 @@ +package net.minecraft.server; + +public class PacketPlayOutCollect extends Packet { + + private int a; + private int b; + + public PacketPlayOutCollect() {} + + public PacketPlayOutCollect(int i, int j) { + this.a = i; + this.b = j; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readInt(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + packetdataserializer.writeInt( this.b ); + } else + { + packetdataserializer.b( this.a ); + packetdataserializer.b( this.b ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutCustomPayload.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutCustomPayload.java new file mode 100644 index 0000000..e6d9165 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutCustomPayload.java @@ -0,0 +1,59 @@ +package net.minecraft.server; + +import net.minecraft.util.io.netty.buffer.ByteBuf; + +import java.io.IOException; + +public class PacketPlayOutCustomPayload extends Packet { + + private String tag; + private byte[] data; + + public PacketPlayOutCustomPayload() {} + + public PacketPlayOutCustomPayload(String s, ByteBuf bytebuf) { + this(s, bytebuf.array()); + } + + public PacketPlayOutCustomPayload(String s, byte[] abyte) { + this.tag = s; + this.data = abyte; + if (abyte.length >= 1048576) { + throw new IllegalArgumentException("Payload may not be larger than 1048576 bytes"); + } + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.tag = packetdataserializer.c(20); + this.data = new byte[packetdataserializer.readUnsignedShort()]; + packetdataserializer.readBytes(this.data); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a(this.tag); + // Spigot start - protocol patch + if ( packetdataserializer.version < 29 ) + { + packetdataserializer.writeShort( this.data.length ); + } + if ( packetdataserializer.version >= 47 && tag.equals( "MC|Brand" ) ) + { + packetdataserializer.a( new String( data, "UTF-8" ) ); + return; + } + packetdataserializer.writeBytes(this.data); + if ( packetdataserializer.version >= 29 && tag.equals( "MC|AdvCdm" ) ) + { + packetdataserializer.writeBoolean( true ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntity.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntity.java new file mode 100644 index 0000000..be48529 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntity.java @@ -0,0 +1,50 @@ +package net.minecraft.server; + +public class PacketPlayOutEntity extends Packet { + + public int a; + public byte b; + public byte c; + public byte d; + public byte e; + public byte f; + public boolean g; + + public PacketPlayOutEntity() {} + + public PacketPlayOutEntity(int i) { + this.a = i; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + } else + { + packetdataserializer.b( a ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d", new Object[] { Integer.valueOf(this.a)}); + } + + public String toString() { + return "Entity_" + super.toString(); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityDestroy.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityDestroy.java new file mode 100644 index 0000000..2fd1ed5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityDestroy.java @@ -0,0 +1,61 @@ +package net.minecraft.server; + +public class PacketPlayOutEntityDestroy extends Packet { + + // Rowin was here - Made public + public int[] a; + + public PacketPlayOutEntityDestroy() { + } + + public PacketPlayOutEntityDestroy(int... aint) { + this.a = aint; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = new int[packetdataserializer.readByte()]; + + for (int i = 0; i < this.a.length; ++i) { + this.a[i] = packetdataserializer.readInt(); + } + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol lib + if (packetdataserializer.version < 16) { + packetdataserializer.writeByte(this.a.length); + + for (int i = 0; i < this.a.length; ++i) { + packetdataserializer.writeInt(this.a[i]); + } + } else { + packetdataserializer.b(a.length); + for (int i : a) { + packetdataserializer.b(i); + } + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + StringBuilder stringbuilder = new StringBuilder(); + + for (int i = 0; i < this.a.length; ++i) { + if (i > 0) { + stringbuilder.append(", "); + } + + stringbuilder.append(this.a[i]); + } + + return String.format("entities=%d[%s]", new Object[]{Integer.valueOf(this.a.length), stringbuilder}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityEffect.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityEffect.java new file mode 100644 index 0000000..3693850 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityEffect.java @@ -0,0 +1,56 @@ +package net.minecraft.server; + +public class PacketPlayOutEntityEffect extends Packet { + + private int a; + private byte b; + private byte c; + private short d; + + public PacketPlayOutEntityEffect() {} + + public PacketPlayOutEntityEffect(int i, MobEffect mobeffect) { + this.a = i; + this.b = (byte) (mobeffect.getEffectId() & 255); + this.c = (byte) (mobeffect.getAmplifier() & 255); + if (mobeffect.getDuration() > 32767) { + this.d = 32767; + } else { + this.d = (short) mobeffect.getDuration(); + } + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readByte(); + this.c = packetdataserializer.readByte(); + this.d = packetdataserializer.readShort(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + packetdataserializer.writeByte( this.b ); + packetdataserializer.writeByte( this.c ); + packetdataserializer.writeShort( this.d ); + } else + { + packetdataserializer.b( a ); + packetdataserializer.writeByte( b ); + packetdataserializer.writeByte( c ); + packetdataserializer.b( d ); + packetdataserializer.writeBoolean( false ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityEquipment.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityEquipment.java new file mode 100644 index 0000000..1ca4f08 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityEquipment.java @@ -0,0 +1,47 @@ +package net.minecraft.server; + +public class PacketPlayOutEntityEquipment extends Packet { + + private int a; + private int b; + private ItemStack c; + + public PacketPlayOutEntityEquipment() {} + + public PacketPlayOutEntityEquipment(int i, int j, ItemStack itemstack) { + this.a = i; + this.b = j; + this.c = itemstack == null ? null : itemstack.cloneItemStack(); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readShort(); + this.c = packetdataserializer.c(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt(this.a); + } else { + packetdataserializer.b( this.a ); + } + // Spigot end + packetdataserializer.writeShort(this.b); + packetdataserializer.a(this.c); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("entity=%d, slot=%d, item=%s", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.b), this.c}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityHeadRotation.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityHeadRotation.java new file mode 100644 index 0000000..2e4abe1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityHeadRotation.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class PacketPlayOutEntityHeadRotation extends Packet { + + private int a; + private byte b; + + public PacketPlayOutEntityHeadRotation() {} + + public PacketPlayOutEntityHeadRotation(Entity entity, byte b0) { + this.a = entity.getId(); + this.b = b0; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + } else + { + packetdataserializer.b( a ); + } + // Spigot end + packetdataserializer.writeByte(this.b); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d, rot=%d", new Object[] { Integer.valueOf(this.a), Byte.valueOf(this.b)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityLook.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityLook.java new file mode 100644 index 0000000..617adb3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityLook.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class PacketPlayOutEntityLook extends PacketPlayOutEntity { + + private boolean onGround; // Spigot - protocol patch + + public PacketPlayOutEntityLook() { + this.g = true; + } + + public PacketPlayOutEntityLook(int i, byte b0, byte b1, boolean onGround) { // Spigot - protocol patch + super(i); + this.e = b0; + this.f = b1; + this.g = true; + this.onGround = onGround; // Spigot - protocol patch + } + + public void a(PacketDataSerializer packetdataserializer) { + super.a(packetdataserializer); + this.e = packetdataserializer.readByte(); + this.f = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + super.b(packetdataserializer); + packetdataserializer.writeByte(this.e); + packetdataserializer.writeByte(this.f); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 22 ) + { + packetdataserializer.writeBoolean( onGround ); + } + // Spigot end + } + + public String b() { + return super.b() + String.format(", yRot=%d, xRot=%d", new Object[] { Byte.valueOf(this.e), Byte.valueOf(this.f)}); + } + + public void handle(PacketListener packetlistener) { + super.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityMetadata.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityMetadata.java new file mode 100644 index 0000000..1d07cb8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityMetadata.java @@ -0,0 +1,86 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +public class PacketPlayOutEntityMetadata extends Packet { + + private int a; + private List b; + private boolean found = false; // MineHQ + + public PacketPlayOutEntityMetadata() {} + + public PacketPlayOutEntityMetadata(int i, DataWatcher datawatcher, boolean flag) { + this.a = i; + if (flag) { + this.b = datawatcher.c(); + } else { + this.b = datawatcher.b(); + } + } + + // Kohi start + // Constructor accepting List of change metadata + public PacketPlayOutEntityMetadata(int i, List list, boolean flag) { + this.a = i; + this.b = list; + } + + // replaces health with 1.0F + public PacketPlayOutEntityMetadata obfuscateHealth() { + Iterator iter = b.iterator(); + found = false; + + while (iter.hasNext()) { + WatchableObject watchable = (WatchableObject) iter.next(); + if (watchable.a() == 6) { + iter.remove(); + found = true; + } + } + + if (found) { + b.add(new WatchableObject(3, 6, Float.valueOf(1.0F))); + } + + return this; + } + // Kohi end + + // MineHQ start + public boolean didFindHealth() { + return this.found; + } + + public List getMetadata() { + return this.b; + } + // MineHQ end + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = DataWatcher.b(packetdataserializer); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + } else + { + packetdataserializer.b( a ); + } + DataWatcher.a(this.b, packetdataserializer, packetdataserializer.version); + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityTeleport.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityTeleport.java new file mode 100644 index 0000000..3b8dea5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityTeleport.java @@ -0,0 +1,89 @@ +package net.minecraft.server; + +public class PacketPlayOutEntityTeleport extends Packet { + + public int a; + public int b; + public int c; + public int d; + public byte e; + public byte f; + public boolean onGround; // Spigot - protocol patch + public boolean heightCorrection; // Spigot Update - 20140916a + + public PacketPlayOutEntityTeleport() {} + + public PacketPlayOutEntityTeleport(Entity entity) { + this.a = entity.getId(); + this.b = MathHelper.floor(entity.locX * 32.0D); + this.c = MathHelper.floor(entity.locY * 32.0D); + this.d = MathHelper.floor(entity.locZ * 32.0D); + this.e = (byte) ((int) (entity.yaw * 256.0F / 360.0F)); + this.f = (byte) ((int) (entity.pitch * 256.0F / 360.0F)); + } + + public PacketPlayOutEntityTeleport(int i, int j, int k, int l, byte b0, byte b1, boolean onGround, boolean heightCorrection) { // Spigot - protocol patch // Spigot Update - 20140916a + this.a = i; + this.b = j; + this.c = k; + this.d = l; + this.e = b0; + this.f = b1; + this.onGround = onGround; // Spigot - protocol patch + this.heightCorrection = heightCorrection; // Spigot Update - 20140916a + } + + /** + * PaperSpigot - Backwards compatible PacketPlayOutEntityTeleport contructor + */ + public PacketPlayOutEntityTeleport(int i, int j, int k, int l, byte b0, byte b1, boolean onGround) { + this(i, j, k, l, b0, b1, onGround, false); + } + + /** + * PaperSpigot - Backwards compatible PacketPlayOutEntityTeleport contructor + */ + public PacketPlayOutEntityTeleport(int i, int j, int k, int l, byte b0, byte b1) { + this(i, j, k, l, b0, b1, false, false); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readInt(); + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readInt(); + this.e = packetdataserializer.readByte(); + this.f = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + } else + { + packetdataserializer.b( a ); + } + // Spigot end + packetdataserializer.writeInt(this.b); + packetdataserializer.writeInt((packetdataserializer.version >= 16 && heightCorrection) ? (this.c - 16) : this.c); // Spigot Update - 20140916a + packetdataserializer.writeInt(this.d); + packetdataserializer.writeByte(this.e); + packetdataserializer.writeByte(this.f); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 22 ) + { + packetdataserializer.writeBoolean( onGround ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityVelocity.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityVelocity.java new file mode 100644 index 0000000..801994b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutEntityVelocity.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +public class PacketPlayOutEntityVelocity extends Packet { + + // Guardian start: Make all of these public + public int a; + public int b; + public int c; + public int d; + // Guardian end + + public PacketPlayOutEntityVelocity() {} + + public PacketPlayOutEntityVelocity(Entity entity) { + this(entity.getId(), entity.motX, entity.motY, entity.motZ); + } + + public PacketPlayOutEntityVelocity(int i, double d0, double d1, double d2) { + this.a = i; + double d3 = 3.9D; + + if (d0 < -d3) { + d0 = -d3; + } + + if (d1 < -d3) { + d1 = -d3; + } + + if (d2 < -d3) { + d2 = -d3; + } + + if (d0 > d3) { + d0 = d3; + } + + if (d1 > d3) { + d1 = d3; + } + + if (d2 > d3) { + d2 = d3; + } + + this.b = (int) (d0 * 8000.0D); + this.c = (int) (d1 * 8000.0D); + this.d = (int) (d2 * 8000.0D); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readShort(); + this.c = packetdataserializer.readShort(); + this.d = packetdataserializer.readShort(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + } else + { + packetdataserializer.b( a ); + } + // Spigot end + packetdataserializer.writeShort(this.b); + packetdataserializer.writeShort(this.c); + packetdataserializer.writeShort(this.d); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d, x=%.2f, y=%.2f, z=%.2f", new Object[] { Integer.valueOf(this.a), Float.valueOf((float) this.b / 8000.0F), Float.valueOf((float) this.c / 8000.0F), Float.valueOf((float) this.d / 8000.0F)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutExperience.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutExperience.java new file mode 100644 index 0000000..35788b2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutExperience.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +public class PacketPlayOutExperience extends Packet { + + private float a; + private int b; + private int c; + + public PacketPlayOutExperience() {} + + public PacketPlayOutExperience(float f, int i, int j) { + this.a = f; + this.b = i; + this.c = j; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readFloat(); + this.c = packetdataserializer.readShort(); + this.b = packetdataserializer.readShort(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeFloat(this.a); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeShort( this.c ); + packetdataserializer.writeShort( this.b ); + } else + { + packetdataserializer.b( c ); + packetdataserializer.b( b ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutExplosion.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutExplosion.java new file mode 100644 index 0000000..d11dd3f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutExplosion.java @@ -0,0 +1,83 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; + +public class PacketPlayOutExplosion extends Packet +{ + private double a; + private double b; + private double c; + private float d; + private List e; + public float f; + public float g; + public float h; + + public PacketPlayOutExplosion() { + } + + public PacketPlayOutExplosion(final double a, final double b, final double c, final float d, final List list, final Vec3D vec3D) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = new ArrayList(list); + if (vec3D != null) { + this.f = (float)vec3D.a; + this.g = (float)vec3D.b; + this.h = (float)vec3D.c; + } + } + + @Override + public void a(final PacketDataSerializer packetDataSerializer) { + this.a = packetDataSerializer.readFloat(); + this.b = packetDataSerializer.readFloat(); + this.c = packetDataSerializer.readFloat(); + this.d = packetDataSerializer.readFloat(); + final int int1 = packetDataSerializer.readInt(); + this.e = new ArrayList(int1); + final int n = (int)this.a; + final int n2 = (int)this.b; + final int n3 = (int)this.c; + for (int i = 0; i < int1; ++i) { + this.e.add(new ChunkPosition(packetDataSerializer.readByte() + n, packetDataSerializer.readByte() + n2, packetDataSerializer.readByte() + n3)); + } + this.f = packetDataSerializer.readFloat(); + this.g = packetDataSerializer.readFloat(); + this.h = packetDataSerializer.readFloat(); + } + + @Override + public void b(final PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeFloat((float)this.a); + packetDataSerializer.writeFloat((float)this.b); + packetDataSerializer.writeFloat((float)this.c); + packetDataSerializer.writeFloat(this.d); + packetDataSerializer.writeInt(this.e.size()); + final int n = (int)this.a; + final int n2 = (int)this.b; + final int n3 = (int)this.c; + for (final Object object : this.e) { + ChunkPosition chunkPosition = (ChunkPosition) object; + final int i = chunkPosition.x - n; + final int j = chunkPosition.y - n2; + final int k = chunkPosition.z - n3; + packetDataSerializer.writeByte(i); + packetDataSerializer.writeByte(j); + packetDataSerializer.writeByte(k); + } + packetDataSerializer.writeFloat(this.f); + packetDataSerializer.writeFloat(this.g); + packetDataSerializer.writeFloat(this.h); + } + + public void a(final PacketPlayOutListener packetPlayOutListener) { + packetPlayOutListener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutKeepAlive.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutKeepAlive.java new file mode 100644 index 0000000..e5feea6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutKeepAlive.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class PacketPlayOutKeepAlive extends Packet { + + private int a; + + public PacketPlayOutKeepAlive() {} + + public PacketPlayOutKeepAlive(int i) { + this.a = i; + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start + if ( packetdataserializer.version >= 32 ) + { + packetdataserializer.b( this.a ); + } else + { + packetdataserializer.writeInt( this.a ); + } + // Spigot end + } + + public boolean a() { + return true; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } + + public int getA() { + return this.a; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutLogin.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutLogin.java new file mode 100644 index 0000000..9ebf9b5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutLogin.java @@ -0,0 +1,77 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayOutLogin extends Packet { + + private int a; + private boolean b; + private EnumGamemode c; + private int d; + private EnumDifficulty e; + private int f; + private WorldType g; + + public PacketPlayOutLogin() {} + + public PacketPlayOutLogin(int i, EnumGamemode enumgamemode, boolean flag, int j, EnumDifficulty enumdifficulty, int k, WorldType worldtype) { + this.a = i; + this.d = j; + this.e = enumdifficulty; + this.c = enumgamemode; + this.f = k; + this.b = flag; + this.g = worldtype; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readInt(); + short short1 = packetdataserializer.readUnsignedByte(); + + this.b = (short1 & 8) == 8; + int i = short1 & -9; + + this.c = EnumGamemode.getById(i); + this.d = packetdataserializer.readByte(); + this.e = EnumDifficulty.getById(packetdataserializer.readUnsignedByte()); + this.f = packetdataserializer.readUnsignedByte(); + this.g = WorldType.getType(packetdataserializer.c(16)); + if (this.g == null) { + this.g = WorldType.NORMAL; + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.writeInt(this.a); + int i = this.c.getId(); + + if (this.b) { + i |= 8; + } + + packetdataserializer.writeByte(i); + packetdataserializer.writeByte(this.d); + packetdataserializer.writeByte(this.e.a()); + packetdataserializer.writeByte(this.f); + packetdataserializer.a(this.g.name()); + + // Spigot start - protocol patch + if ( packetdataserializer.version >= 29 ) + { + packetdataserializer.writeBoolean( false ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("eid=%d, gameType=%d, hardcore=%b, dimension=%d, difficulty=%s, maxplayers=%d", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.c.getId()), Boolean.valueOf(this.b), Integer.valueOf(this.d), this.e, Integer.valueOf(this.f)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMap.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMap.java new file mode 100644 index 0000000..b45a9ce --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMap.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +import java.util.Arrays; + +public class PacketPlayOutMap extends Packet { + + private int a; + private byte[] b; + private byte scale; // Spigot - protocol patch + + public PacketPlayOutMap() {} + + // Spigot start - protocol patch + public PacketPlayOutMap(int i, byte[] abyte, byte scale) { + this.scale = scale; + // Spigot end + this.a = i; + this.b = abyte; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.a(); + this.b = new byte[packetdataserializer.readUnsignedShort()]; + packetdataserializer.readBytes(this.b); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.b(this.a); + if ( packetdataserializer.version < 27 ) + { + packetdataserializer.writeShort( this.b.length ); + packetdataserializer.writeBytes( this.b ); + } else { + packetdataserializer.writeByte( scale ); + if (b[0] == 1) { + int count = (b.length - 1) / 3; + packetdataserializer.b( count ); + for (int i = 0; i < count; i++) { + packetdataserializer.writeByte( b[1 + i * 3] ); + packetdataserializer.writeByte( b[2 + i * 3] ); + packetdataserializer.writeByte( b[3 + i * 3] ); + } + } else { + packetdataserializer.b( 0 ); + } + + if (b[0] == 0) { + packetdataserializer.writeByte( 1 ); + int rows = (b.length - 3); + packetdataserializer.writeByte( rows ); + packetdataserializer.writeByte( b[1] ); + packetdataserializer.writeByte( b[2] ); + a( packetdataserializer, Arrays.copyOfRange(b, 3, b.length) ); + } else { + packetdataserializer.writeByte( 0 ); + } + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d, length=%d", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.b.length)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java new file mode 100644 index 0000000..cc5df10 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java @@ -0,0 +1,283 @@ +package net.minecraft.server; + +import net.valorhcf.ReusableByteArray; + +import java.io.IOException; +import java.util.zip.Deflater; + +public class PacketPlayOutMapChunk extends Packet { + + private int a; + private int b; + private int c; + private int d; + private static final ReusableByteArray bufferCache = new ReusableByteArray(164196); // MineHQ + private byte[] f; + private boolean g; + private int h; + private static byte[] i = new byte[196864]; + + private World world; // MineHQ - use world instead of chunk + private int mask; // Spigot + + public PacketPlayOutMapChunk() {} + + // Spigot start - protocol patch + public PacketPlayOutMapChunk(Chunk chunk, boolean flag, int i, int version) { + this.world = chunk.world; + this.mask = i; + this.a = chunk.locX; + this.b = chunk.locZ; + this.g = flag; + // MineHQ start - don't need to do chunkmap for unload chunk packets + if (i == 0 && this.g) { + return; + } + // MineHQ end + ChunkMap chunkmap = chunk.getChunkMap(flag, i, version); // MineHQ + + this.d = chunkmap.c; + this.c = chunkmap.b; + + this.f = chunkmap.a; + } + + // MineHQ start - constructor for unload chunk packets + public static PacketPlayOutMapChunk unload(int x, int z) { + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(); + packet.a = x; + packet.b = z; + packet.g = true; + return packet; + } + // MineHQ end + + public static int c() { + return 196864; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + // MineHQ start - this is client code + /* + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readInt(); + this.g = packetdataserializer.readBoolean(); + this.c = packetdataserializer.readShort(); + this.d = packetdataserializer.readShort(); + this.h = packetdataserializer.readInt(); + if (i.length < this.h) { + i = new byte[this.h]; + } + + packetdataserializer.readBytes(i, 0, this.h); + int i = 0; + + int j; + + for (j = 0; j < 16; ++j) { + i += this.c >> j & 1; + } + + j = 12288 * i; + if (this.g) { + j += 256; + } + + this.f = new byte[j]; + Inflater inflater = new Inflater(); + + inflater.setInput(PacketPlayOutMapChunk.i, 0, this.h); + + try { + inflater.inflate(this.f); + } catch (DataFormatException dataformatexception) { + throw new IOException("Bad compressed data format"); + } finally { + inflater.end(); + } + */ + // MineHQ end + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeInt(this.a); + packetdataserializer.writeInt(this.b); + packetdataserializer.writeBoolean(this.g); + packetdataserializer.writeShort((short) (this.c & '\uffff')); + // MineHQ start - don't send any data for unload chunks, the client still accepts the packets fine without it + if (this.g && this.c == 0) { + if (packetdataserializer.version < 27) { + packetdataserializer.writeShort((short) (this.d & '\uffff')); + packetdataserializer.writeInt(0); + } else { + packetdataserializer.b(0); + } + return; + } + // MineHQ end + // Spigot start - protocol patch + if ( packetdataserializer.version < 27 ) + { + this.world.spigotConfig.antiXrayInstance.obfuscate(this.a, this.b, mask, this.f, this.world, false); // Spigot + Deflater deflater = new Deflater(4); // Spigot + byte[] buffer; + try { + deflater.setInput(this.f, 0, this.f.length); + deflater.finish(); + buffer = bufferCache.get(this.f.length + 100); + this.h = deflater.deflate(buffer); + } finally { + deflater.end(); + } + packetdataserializer.writeShort( (short) ( this.d & '\uffff' ) ); + packetdataserializer.writeInt( this.h ); + packetdataserializer.writeBytes( buffer, 0, this.h ); + } else + { + this.world.spigotConfig.antiXrayInstance.obfuscate(this.a, this.b, mask, this.f, this.world, true); // Spigot + a( packetdataserializer, this.f ); + } + // Spigot end - protocol patch + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("x=%d, z=%d, full=%b, sects=%d, add=%d, size=%d", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.b), Boolean.valueOf(this.g), Integer.valueOf(this.c), Integer.valueOf(this.d), Integer.valueOf(this.h)}); + } + + // Spigot start - protocol patch + public static ChunkMap a(Chunk chunk, boolean flag, int i, int version) { + int j = 0; + ChunkSection[] achunksection = chunk.getSections(); + int k = 0; + ChunkMap chunkmap = new ChunkMap(); + byte[] abyte = PacketPlayOutMapChunk.i; + + if (flag) { + chunk.q = true; + } + + int l; + + for (l = 0; l < achunksection.length; ++l) { + if (achunksection[l] != null && (!flag || !achunksection[l].isEmpty()) && (i & 1 << l) != 0) { + chunkmap.b |= 1 << l; + // MineHQ start - 1.7 has no extended block IDs + /* + if (achunksection[l].getExtendedIdArray() != null) { + chunkmap.c |= 1 << l; + ++k; + } + */ + } + } + + if ( version < 24 ) + { + for ( l = 0; l < achunksection.length; ++l ) + { + if ( achunksection[ l ] != null && ( !flag || !achunksection[ l ].isEmpty() ) && ( i & 1 << l ) != 0 ) + { + byte[] abyte1 = achunksection[ l ].getIdArray(); + + System.arraycopy( abyte1, 0, abyte, j, abyte1.length ); + j += abyte1.length; + } + } + } else { + for ( l = 0; l < achunksection.length; ++l ) + { + if ( achunksection[ l ] != null && ( !flag || !achunksection[ l ].isEmpty() ) && ( i & 1 << l ) != 0 ) + { + byte[] abyte1 = achunksection[ l ].getIdArray(); + NibbleArray nibblearray = achunksection[ l ].getDataArray(); + for ( int ind = 0; ind < abyte1.length; ind++ ) + { + int id = abyte1[ ind ] & 0xFF; + int px = ind & 0xF; + int py = ( ind >> 8 ) & 0xF; + int pz = ( ind >> 4 ) & 0xF; + int data = nibblearray.a( px, py, pz ); + if ( id == 90 && data == 0 ) + { + Blocks.PORTAL.updateShape( chunk.world, ( chunk.locX << 4 ) + px, ( l << 4 ) + py, ( chunk.locZ << 4 ) + pz ); + } else + { + data = org.spigotmc.SpigotDebreakifier.getCorrectedData( id, data ); + } + char val = (char) ( id << 4 | data ); + abyte[ j++ ] = (byte) ( val & 0xFF ); + abyte[ j++ ] = (byte) ( ( val >> 8 ) & 0xFF ); + } + } + } + } + + NibbleArray nibblearray; + + if ( version < 24 ) + { + for ( l = 0; l < achunksection.length; ++l ) + { + if ( achunksection[ l ] != null && ( !flag || !achunksection[ l ].isEmpty() ) && ( i & 1 << l ) != 0 ) + { + nibblearray = achunksection[ l ].getDataArray(); + System.arraycopy(nibblearray.a, 0, abyte, j, nibblearray.a.length); + j += nibblearray.a.length; + } + } + } + + for (l = 0; l < achunksection.length; ++l) { + if (achunksection[l] != null && (!flag || !achunksection[l].isEmpty()) && (i & 1 << l) != 0) { + nibblearray = achunksection[l].getEmittedLightArray(); + System.arraycopy(nibblearray.a, 0, abyte, j, nibblearray.a.length); + j += nibblearray.a.length; + } + } + + if (!chunk.world.worldProvider.g) { + for (l = 0; l < achunksection.length; ++l) { + if (achunksection[l] != null && (!flag || !achunksection[l].isEmpty()) && (i & 1 << l) != 0) { + nibblearray = achunksection[l].getSkyLightArray(); + System.arraycopy(nibblearray.a, 0, abyte, j, nibblearray.a.length); + j += nibblearray.a.length; + } + } + } + + // MineHQ start - 1.7 has no extended block IDs + /* + if (k > 0 && version < 24) { + for (l = 0; l < achunksection.length; ++l) { + if (achunksection[l] != null && (!flag || !achunksection[l].isEmpty()) && achunksection[l].getExtendedIdArray() != null && (i & 1 << l) != 0) { + nibblearray = achunksection[l].getExtendedIdArray(); + System.arraycopy(nibblearray.a, 0, abyte, j, nibblearray.a.length); + j += nibblearray.a.length; + } + } + } + */ + // MineHQ end + + if (flag) { + byte[] abyte2 = chunk.m(); + + System.arraycopy(abyte2, 0, abyte, j, abyte2.length); + j += abyte2.length; + } + + chunkmap.a = new byte[j]; + System.arraycopy(abyte, 0, chunkmap.a, 0, j); + return chunkmap; + } + // Spigot end - protocol patch + + @Override + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMapChunkBulk.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMapChunkBulk.java new file mode 100644 index 0000000..13bac95 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMapChunkBulk.java @@ -0,0 +1,248 @@ +package net.minecraft.server; + +import net.valorhcf.ReusableByteArray; + +import java.io.IOException; +import java.util.List; +import java.util.zip.Deflater; + +public class PacketPlayOutMapChunkBulk extends Packet { + + private int[] a; + private int[] b; + private int[] c; + private int[] d; + private byte[] buffer; + private static final ReusableByteArray bufferCache = new ReusableByteArray(820480); // MineHQ + private byte[][] inflatedBuffers; + private int size; + private boolean h; + private byte[] buildBuffer; // MineHQ + private static final ReusableByteArray buildBufferCache = new ReusableByteArray(820480); // MineHQ + // CraftBukkit start + static final ThreadLocal localDeflater = new ThreadLocal() { + @Override + protected Deflater initialValue() { + // Don't use higher compression level, slows things down too much + return new Deflater(4); // Spigot 6 -> 4 + } + }; + // CraftBukkit end + private World world; // Spigot + + public PacketPlayOutMapChunkBulk() {} + + public PacketPlayOutMapChunkBulk(List list, int version) { + int i = list.size(); + + this.a = new int[i]; + this.b = new int[i]; + this.c = new int[i]; + this.d = new int[i]; + this.inflatedBuffers = new byte[i][]; + this.h = !list.isEmpty() && !((Chunk) list.get(0)).world.worldProvider.g; + int j = 0; + + for (int k = 0; k < i; ++k) { + Chunk chunk = (Chunk) list.get(k); + ChunkMap chunkmap = chunk.getChunkMap(true, '\uffff', version); // MineHQ + + // Spigot start + world = chunk.world; + /* + if (buildBuffer.length < j + chunkmap.a.length) { + byte[] abyte = new byte[j + chunkmap.a.length]; + + System.arraycopy(buildBuffer, 0, abyte, 0, buildBuffer.length); + buildBuffer = abyte; + } + + System.arraycopy(chunkmap.a, 0, buildBuffer, j, chunkmap.a.length); + */ + // Spigot end + j += chunkmap.a.length; + this.a[k] = chunk.locX; + this.b[k] = chunk.locZ; + this.c[k] = chunkmap.b; + this.d[k] = chunkmap.c; + this.inflatedBuffers[k] = chunkmap.a; + } + + /* CraftBukkit start - Moved to compress() + Deflater deflater = new Deflater(-1); + + try { + deflater.setInput(buildBuffer, 0, j); + deflater.finish(); + this.buffer = new byte[j]; + this.size = deflater.deflate(this.buffer); + } finally { + deflater.end(); + } + */ + } + + // MineHQ start - constructor for "padding" chunk of all air + public static PacketPlayOutMapChunkBulk paddingChunk(World world, int x, int z) { + PacketPlayOutMapChunkBulk packet = new PacketPlayOutMapChunkBulk(); + packet.a = new int[]{x}; + packet.b = new int[]{z}; + packet.c = new int[]{0}; + packet.d = new int[]{0}; + packet.inflatedBuffers = new byte[][]{new byte[256]}; // still have to send biome array to actually create a chunk! + packet.h = !world.worldProvider.g; + return packet; + } + // MineHQ end + + // Add compression method + public void compress() { + if (this.buffer != null) { + return; + } + // Spigot start + int finalBufferSize = 0; + // Obfuscate all sections + for (int i = 0; i < a.length; i++) { + if (world != null) world.spigotConfig.antiXrayInstance.obfuscate(a[i], b[i], c[i], inflatedBuffers[i], world, false); // MineHQ - padding chunk + finalBufferSize += inflatedBuffers[i].length; + } + + // Now it's time to efficiently copy the chunk to the build buffer + buildBuffer = buildBufferCache.get(finalBufferSize); // MineHQ + int bufferLocation = 0; + for (int i = 0; i < a.length; i++) { + System.arraycopy(inflatedBuffers[i], 0, buildBuffer, bufferLocation, inflatedBuffers[i].length); + bufferLocation += inflatedBuffers[i].length; + } + // Spigot end + + Deflater deflater = localDeflater.get(); + deflater.reset(); + deflater.setInput(this.buildBuffer, 0, finalBufferSize); // MineHQ + deflater.finish(); + + this.buffer = bufferCache.get(finalBufferSize + 100); // MineHQ + this.size = deflater.deflate(this.buffer); + } + // CraftBukkit end + + public static int c() { + return 5; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - throws IOException + // MineHQ start - this is client code + /* + short short1 = packetdataserializer.readShort(); + + this.size = packetdataserializer.readInt(); + this.h = packetdataserializer.readBoolean(); + this.a = new int[short1]; + this.b = new int[short1]; + this.c = new int[short1]; + this.d = new int[short1]; + this.inflatedBuffers = new byte[short1][]; + if (buildBuffer.length < this.size) { + buildBuffer = new byte[this.size]; + } + + packetdataserializer.readBytes(buildBuffer, 0, this.size); + byte[] abyte = new byte[PacketPlayOutMapChunk.c() * short1]; + Inflater inflater = new Inflater(); + + inflater.setInput(buildBuffer, 0, this.size); + + try { + inflater.inflate(abyte); + } catch (DataFormatException dataformatexception) { + throw new IOException("Bad compressed data format"); + } finally { + inflater.end(); + } + + int i = 0; + + for (int j = 0; j < short1; ++j) { + this.a[j] = packetdataserializer.readInt(); + this.b[j] = packetdataserializer.readInt(); + this.c[j] = packetdataserializer.readShort(); + this.d[j] = packetdataserializer.readShort(); + int k = 0; + int l = 0; + + int i1; + + for (i1 = 0; i1 < 16; ++i1) { + k += this.c[j] >> i1 & 1; + l += this.d[j] >> i1 & 1; + } + + i1 = 2048 * 4 * k + 256; + i1 += 2048 * l; + if (this.h) { + i1 += 2048 * k; + } + + this.inflatedBuffers[j] = new byte[i1]; + System.arraycopy(abyte, i, this.inflatedBuffers[j], 0, i1); + i += i1; + } + */ + // MineHQ end + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - throws IOException + if ( packetdataserializer.version < 27 ) + { + compress(); // CraftBukkit + packetdataserializer.writeShort( this.a.length ); + packetdataserializer.writeInt( this.size ); + packetdataserializer.writeBoolean( this.h ); + packetdataserializer.writeBytes( this.buffer, 0, this.size ); + + for (int i = 0; i < this.a.length; ++i) { + packetdataserializer.writeInt(this.a[i]); + packetdataserializer.writeInt(this.b[i]); + packetdataserializer.writeShort((short) (this.c[i] & '\uffff')); + packetdataserializer.writeShort( (short) ( this.d[i] & '\uffff' ) ); + } + } else + { + packetdataserializer.writeBoolean( this.h ); + packetdataserializer.b( this.a.length ); + + for (int i = 0; i < this.a.length; ++i) { + packetdataserializer.writeInt(this.a[i]); + packetdataserializer.writeInt(this.b[i]); + packetdataserializer.writeShort((short) (this.c[i] & '\uffff')); + } + for (int i = 0; i < this.a.length; ++i) { + world.spigotConfig.antiXrayInstance.obfuscate(a[i], b[i], c[i], inflatedBuffers[i], world, true); + packetdataserializer.writeBytes( inflatedBuffers[i] ); + } + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + StringBuilder stringbuilder = new StringBuilder(); + + for (int i = 0; i < this.a.length; ++i) { + if (i > 0) { + stringbuilder.append(", "); + } + + stringbuilder.append(String.format("{x=%d, z=%d, sections=%d, adds=%d, data=%d}", new Object[] { Integer.valueOf(this.a[i]), Integer.valueOf(this.b[i]), Integer.valueOf(this.c[i]), Integer.valueOf(this.d[i]), Integer.valueOf(this.inflatedBuffers[i].length)})); + } + + return String.format("size=%d, chunks=%d[%s]", new Object[] { Integer.valueOf(this.size), Integer.valueOf(this.a.length), stringbuilder}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMultiBlockChange.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMultiBlockChange.java new file mode 100644 index 0000000..3ba05bf --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutMultiBlockChange.java @@ -0,0 +1,128 @@ +package net.minecraft.server; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PacketPlayOutMultiBlockChange extends Packet { + + private static final Logger a = LogManager.getLogger(); + public ChunkCoordIntPair b; + public byte[] c; + public int d; + // Spigot start - protocol patch + public short[] ashort; + public int[] blocks; + // Spigot end + public boolean fake = false; + + public PacketPlayOutMultiBlockChange() {} + + + protected static PacketPlayOutMultiBlockChange create(final ChunkCoordIntPair b, final byte[] c, final int d, final short[] ashort, final int[] blocks) { + final PacketPlayOutMultiBlockChange packetPlayOutMultiBlockChange = new PacketPlayOutMultiBlockChange(); + packetPlayOutMultiBlockChange.b = b; + packetPlayOutMultiBlockChange.c = c; + packetPlayOutMultiBlockChange.d = d; + packetPlayOutMultiBlockChange.ashort = ashort; + packetPlayOutMultiBlockChange.blocks = blocks; + return packetPlayOutMultiBlockChange; + } + + public PacketPlayOutMultiBlockChange(int i, short[] ashort, Chunk chunk) { + // Spigot start + // PaperSpigot start - Fix race condition in PacketPlayOutMultiBlockChange + this.ashort = new short[ashort.length]; + System.arraycopy(ashort, 0, this.ashort, 0, ashort.length); + // PaperSpigot end + // Spigot end + this.b = new ChunkCoordIntPair(chunk.locX, chunk.locZ); + this.d = i; + int j = 4 * i; + + try + { + ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream( j ); + DataOutputStream dataoutputstream = new DataOutputStream( bytearrayoutputstream ); + + // Spigot start + blocks = new int[i]; + for (int k = 0; k < i; ++k) { + int l = ashort[k] >> 12 & 15; + int i1 = ashort[k] >> 8 & 15; + int j1 = ashort[k] & 255; + + dataoutputstream.writeShort(ashort[k]); + int blockId = Block.getId( chunk.getType( l, j1, i1 ) ); + int data = chunk.getData( l, j1, i1 ); + data = org.spigotmc.SpigotDebreakifier.getCorrectedData( blockId, data ); + int id = ( blockId & 4095 ) << 4 | data & 15; + dataoutputstream.writeShort((short) id); + blocks[k] = id; + } + // Spigot end + + this.c = bytearrayoutputstream.toByteArray(); + if (this.c.length != j) { + throw new RuntimeException("Expected length " + j + " doesn't match received length " + this.c.length); + } + } catch (IOException ioexception) { + a.error("Couldn't create bulk block update packet", ioexception); + this.c = null; + } + } + + public void a(PacketDataSerializer packetdataserializer) { + this.b = new ChunkCoordIntPair(packetdataserializer.readInt(), packetdataserializer.readInt()); + this.d = packetdataserializer.readShort() & '\uffff'; + int i = packetdataserializer.readInt(); + + if (i > 0) { + this.c = new byte[i]; + packetdataserializer.readBytes(this.c); + } + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if (packetdataserializer.version < 25) + { + packetdataserializer.writeInt( this.b.x ); + packetdataserializer.writeInt( this.b.z ); + packetdataserializer.writeShort( (short) this.d ); + if ( this.c != null ) + { + packetdataserializer.writeInt( this.c.length ); + packetdataserializer.writeBytes( this.c ); + } else + { + packetdataserializer.writeInt( 0 ); + } + } else { + packetdataserializer.writeInt( this.b.x ); + packetdataserializer.writeInt( this.b.z ); + packetdataserializer.b( this.d ); + for ( int i = 0; i < d; i++ ) + { + packetdataserializer.writeShort( ashort[ i ] ); + packetdataserializer.b( blocks[ i ] ); + } + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("xc=%d, zc=%d, count=%d", this.b.x, this.b.z, this.d); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutNamedEntitySpawn.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutNamedEntitySpawn.java new file mode 100644 index 0000000..c253fc8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutNamedEntitySpawn.java @@ -0,0 +1,118 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.properties.Property; + +import java.io.IOException; // CraftBukkit + +public class PacketPlayOutNamedEntitySpawn extends Packet { + + private int a; + private GameProfile b; + private int c; + private int d; + private int e; + private byte f; + private byte g; + private int h; + private DataWatcher i; + private List j; + + public PacketPlayOutNamedEntitySpawn() {} + + public PacketPlayOutNamedEntitySpawn(EntityHuman entityhuman) { + this.a = entityhuman.getId(); + this.b = ((EntityPlayer) entityhuman).getDisguiseProfile(); // MineHQ - disguises + this.c = MathHelper.floor(entityhuman.locX * 32.0D); + this.d = MathHelper.floor(entityhuman.locY * 32.0D); + this.e = MathHelper.floor(entityhuman.locZ * 32.0D); + this.f = (byte) ((int) (entityhuman.yaw * 256.0F / 360.0F)); + this.g = (byte) ((int) (entityhuman.pitch * 256.0F / 360.0F)); + ItemStack itemstack = entityhuman.inventory.getItemInHand(); + + this.h = itemstack == null ? 0 : Item.getId(itemstack.getItem()); + this.i = entityhuman.getDataWatcher().clone(); // MineHQ + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + this.a = packetdataserializer.a(); + UUID uuid = UUID.fromString(packetdataserializer.c(36)); + + this.b = new GameProfile(uuid, packetdataserializer.c(16)); + int i = packetdataserializer.a(); + + for (int j = 0; j < i; ++j) { + String s = packetdataserializer.c(32767); + String s1 = packetdataserializer.c(32767); + String s2 = packetdataserializer.c(32767); + + this.b.getProperties().put(s, new Property(s, s1, s2)); + } + + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readInt(); + this.e = packetdataserializer.readInt(); + this.f = packetdataserializer.readByte(); + this.g = packetdataserializer.readByte(); + this.h = packetdataserializer.readShort(); + this.j = DataWatcher.b(packetdataserializer); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { // CraftBukkit - added throws + packetdataserializer.b(this.a); + + UUID uuid = this.b.getId(); + // Spigot start - protocol patch + if (packetdataserializer.version < 20) { + packetdataserializer.a( uuid == null ? "" : ( ( packetdataserializer.version >= 5 ) ? uuid.toString() : uuid.toString().replaceAll( "-", "" ) ) ); // Spigot + packetdataserializer.a(this.b.getName().length() > 16 ? this.b.getName().substring(0, 16) : this.b.getName()); // CraftBukkit - Limit name length to 16 characters + if (packetdataserializer.version >= 5 ) { // Spigot + packetdataserializer.b(this.b.getProperties().size()); + Iterator iterator = this.b.getProperties().values().iterator(); + + while (iterator.hasNext()) { + Property property = (Property) iterator.next(); + + packetdataserializer.a(property.getName()); + packetdataserializer.a(property.getValue()); + packetdataserializer.a(property.getSignature()); + } + } + } else + { + packetdataserializer.writeUUID( uuid ); + } + // Spigot end + + packetdataserializer.writeInt(this.c); + packetdataserializer.writeInt(this.d); + packetdataserializer.writeInt(this.e); + packetdataserializer.writeByte(this.f); + packetdataserializer.writeByte(this.g); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 47 ) + { + packetdataserializer.writeShort( org.spigotmc.SpigotDebreakifier.getItemId( this.h ) ); + } else + { + packetdataserializer.writeShort( this.h ); + } + this.i.a(packetdataserializer); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d, gameProfile=\'%s\', x=%.2f, y=%.2f, z=%.2f, carried=%d", new Object[] { Integer.valueOf(this.a), this.b, Float.valueOf((float) this.c / 32.0F), Float.valueOf((float) this.d / 32.0F), Float.valueOf((float) this.e / 32.0F), Integer.valueOf(this.h)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutOpenSignEditor.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutOpenSignEditor.java new file mode 100644 index 0000000..052da02 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutOpenSignEditor.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class PacketPlayOutOpenSignEditor extends Packet { + + private int a; + private int b; + private int c; + + public PacketPlayOutOpenSignEditor() {} + + public PacketPlayOutOpenSignEditor(int i, int j, int k) { + this.a = i; + this.b = j; + this.c = k; + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readInt(); + this.c = packetdataserializer.readInt(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + packetdataserializer.writeInt( this.b ); + packetdataserializer.writeInt( this.c ); + } else + { + packetdataserializer.writePosition( a, b, c ); + } + // Spigot end + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutOpenWindow.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutOpenWindow.java new file mode 100644 index 0000000..8e9ea33 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutOpenWindow.java @@ -0,0 +1,114 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.util.CraftChatMessage; + +import java.io.IOException; + +public class PacketPlayOutOpenWindow extends Packet { + + private int a; + private int b; + private String c; + private int d; + private boolean e; + private int f; + + public PacketPlayOutOpenWindow() {} + + public PacketPlayOutOpenWindow(int i, int j, String s, int k, boolean flag) { + if (s.length() > 32) s = s.substring( 0, 32 ); // Spigot - Cap window name to prevent client disconnects + this.a = i; + this.b = j; + this.c = s; + this.d = k; + this.e = flag; + } + + public PacketPlayOutOpenWindow(int i, int j, String s, int k, boolean flag, int l) { + this(i, j, s, k, flag); + this.f = l; + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readUnsignedByte(); + this.b = packetdataserializer.readUnsignedByte(); + this.c = packetdataserializer.c(32); + this.d = packetdataserializer.readUnsignedByte(); + this.e = packetdataserializer.readBoolean(); + if (this.b == 11) { + this.f = packetdataserializer.readInt(); + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeByte( this.a ); + packetdataserializer.writeByte( this.b ); + packetdataserializer.a( this.c ); + packetdataserializer.writeByte( this.d ); + packetdataserializer.writeBoolean( this.e ); + if ( this.b == 11 ) + { + packetdataserializer.writeInt( this.f ); + } + } else + { + packetdataserializer.writeByte( a ); + packetdataserializer.a( getInventoryString( b ) ); + if ( e ) + { + packetdataserializer.a( ChatSerializer.a( CraftChatMessage.fromString( c )[ 0 ] ) ); + } else + { + packetdataserializer.a( ChatSerializer.a( new ChatMessage( c ) ) ); + } + packetdataserializer.writeByte( d ); + if ( this.b == 11 ) + { + packetdataserializer.writeInt( this.f ); + } + } + } + + // Spigot start - protocol patch + private String getInventoryString(int b) + { + switch ( b ) { + case 0: + return "minecraft:chest"; + case 1: + return "minecraft:crafting_table"; + case 2: + return "minecraft:furnace"; + case 3: + return "minecraft:dispenser"; + case 4: + return "minecraft:enchanting_table"; + case 5: + return "minecraft:brewing_stand"; + case 6: + return "minecraft:villager"; + case 7: + return "minecraft:beacon"; + case 8: + return "minecraft:anvil"; + case 9: + return "minecraft:hopper"; + case 10: + return "minecraft:dropper"; + case 11: + return "EntityHorse"; + } + throw new IllegalArgumentException( "Unknown type " + b ); + } + // Spigot end + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutPlayerInfo.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutPlayerInfo.java new file mode 100644 index 0000000..37ad649 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutPlayerInfo.java @@ -0,0 +1,148 @@ +package net.minecraft.server; + +import java.io.IOException; +// Spigot start - protocol patch +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.properties.Property; +import net.minecraft.util.com.mojang.authlib.properties.PropertyMap; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +public class PacketPlayOutPlayerInfo extends Packet { + + private static final int ADD_PLAYER = 0; + private static final int UPDATE_GAMEMODE = 1; + private static final int UPDATE_LATENCY = 2; + private static final int UPDATE_DISPLAY_NAME = 3; + private static final int REMOVE_PLAYER = 4; + + //CavePvP public + // private int length; We don't batch (yet) + public int ping; + public int action; + public int gamemode; + public String username; + + public GameProfile player; + + public PacketPlayOutPlayerInfo() { + } + + /* removed to force breaking + public PacketPlayOutPlayerInfo(String s, boolean flag, int i) { + this.a = s; + this.b = flag; + this.c = i; + } + */ + + public static PacketPlayOutPlayerInfo addPlayer(EntityPlayer player) { + PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(); + packet.action = ADD_PLAYER; + packet.username = player.listName; + packet.player = player.getDisguiseProfile(); // MineHQ - disguises + packet.ping = player.ping; + packet.gamemode = player.playerInteractManager.getGameMode().getId(); + return packet; + } + + public static PacketPlayOutPlayerInfo updatePing(EntityPlayer player) { + PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(); + packet.action = UPDATE_LATENCY; + packet.username = player.listName; + packet.player = player.getDisguiseProfile(); // MineHQ - disguises + packet.ping = player.ping; + return packet; + } + + public static PacketPlayOutPlayerInfo updateGamemode(EntityPlayer player) { + PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(); + packet.action = UPDATE_GAMEMODE; + packet.username = player.listName; + packet.player = player.getDisguiseProfile(); // MineHQ - disguises + packet.gamemode = player.playerInteractManager.getGameMode().getId(); + return packet; + } + + public static PacketPlayOutPlayerInfo updateDisplayName(EntityPlayer player) { + PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(); + packet.action = UPDATE_DISPLAY_NAME; + packet.username = player.listName; + packet.player = player.getDisguiseProfile(); // MineHQ - disguises + return packet; + } + + public static PacketPlayOutPlayerInfo removePlayer(EntityPlayer player) { + PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(); + packet.action = REMOVE_PLAYER; + packet.username = player.listName; + packet.player = player.getDisguiseProfile(); // MineHQ - disguises + return packet; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + // Not needed + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + // PaperSpigot start - Fix scoreboard prefix and suffix in tab list + String username = this.username; + if (packetdataserializer.version >= 47 && action == ADD_PLAYER && username != null && username.equals(player.getName())) { + username = null; + } + // PaperSpigot end + if (packetdataserializer.version >= 20) { + packetdataserializer.b(action); + packetdataserializer.b(1); + packetdataserializer.writeUUID(player.getId()); + switch (action) { + case ADD_PLAYER: + packetdataserializer.a(player.getName()); + PropertyMap properties = player.getProperties(); + packetdataserializer.b(properties.size()); + for (Property property : properties.values()) { + packetdataserializer.a(property.getName()); + packetdataserializer.a(property.getValue()); + packetdataserializer.writeBoolean(property.hasSignature()); + if (property.hasSignature()) { + packetdataserializer.a(property.getSignature()); + } + } + packetdataserializer.b(gamemode); + packetdataserializer.b(ping); + packetdataserializer.writeBoolean(username != null); + if (username != null) { + packetdataserializer.a(ChatSerializer.a(CraftChatMessage.fromString(username)[0])); + } + break; + case UPDATE_GAMEMODE: + packetdataserializer.b(gamemode); + break; + case UPDATE_LATENCY: + packetdataserializer.b(ping); + break; + case UPDATE_DISPLAY_NAME: + packetdataserializer.writeBoolean(username != null); + if (username != null) { + packetdataserializer.a(ChatSerializer.a(CraftChatMessage.fromString(username)[0])); + } + break; + case REMOVE_PLAYER: + break; + + } + } else { + packetdataserializer.a(username); + packetdataserializer.writeBoolean(action != REMOVE_PLAYER); + packetdataserializer.writeShort(ping); + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} +// Spigot end \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutPosition.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutPosition.java new file mode 100644 index 0000000..7b88ddb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutPosition.java @@ -0,0 +1,62 @@ +package net.minecraft.server; + +public class PacketPlayOutPosition extends Packet { + + public double a; + public double b; + public double c; + public float d; + public float e; + public boolean f; + public byte relativeBitMask; // Spigot Update - 20141001a + + public PacketPlayOutPosition() {} + + public PacketPlayOutPosition(double d0, double d1, double d2, float f, float f1, boolean flag) { + this(d0, d1, d2, f, f1, flag, (byte)0); + } + + public PacketPlayOutPosition(double d0, double d1, double d2, float f, float f1, boolean flag, byte relativeBitMask) { + this.a = d0; + this.b = d1; + this.c = d2; + this.d = f; + this.e = f1; + this.f = flag; + this.relativeBitMask = relativeBitMask; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readDouble(); + this.b = packetdataserializer.readDouble(); + this.c = packetdataserializer.readDouble(); + this.d = packetdataserializer.readFloat(); + this.e = packetdataserializer.readFloat(); + this.f = packetdataserializer.readBoolean(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + packetdataserializer.writeDouble(this.a); + packetdataserializer.writeDouble(this.b - (packetdataserializer.version >= 16 ? 1.62 : 0)); + packetdataserializer.writeDouble(this.c); + packetdataserializer.writeFloat(this.d); + packetdataserializer.writeFloat(this.e); + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeBoolean( this.f ); + } else + { + packetdataserializer.writeByte( this.relativeBitMask ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRelEntityMove.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRelEntityMove.java new file mode 100644 index 0000000..e2ef02c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRelEntityMove.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class PacketPlayOutRelEntityMove extends PacketPlayOutEntity { + + private boolean onGround; // Spigot - protocol patch + + public PacketPlayOutRelEntityMove() {} + + public PacketPlayOutRelEntityMove(int i, byte b0, byte b1, byte b2, boolean onGround) { // Spigot - protocol patch + super(i); + this.b = b0; + this.c = b1; + this.d = b2; + this.onGround = onGround; // Spigot - protocol patch + } + + public void a(PacketDataSerializer packetdataserializer) { + super.a(packetdataserializer); + this.b = packetdataserializer.readByte(); + this.c = packetdataserializer.readByte(); + this.d = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + super.b(packetdataserializer); + packetdataserializer.writeByte(this.b); + packetdataserializer.writeByte(this.c); + packetdataserializer.writeByte(this.d); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 22 ) + { + packetdataserializer.writeBoolean( onGround ); + } + // Spigot end + } + + public String b() { + return super.b() + String.format(", xa=%d, ya=%d, za=%d", new Object[] { Byte.valueOf(this.b), Byte.valueOf(this.c), Byte.valueOf(this.d)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRelEntityMoveLook.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRelEntityMoveLook.java new file mode 100644 index 0000000..2ad234b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRelEntityMoveLook.java @@ -0,0 +1,53 @@ +package net.minecraft.server; + +public class PacketPlayOutRelEntityMoveLook extends PacketPlayOutEntity { + + private boolean onGround; // Spigot - protocol patch + + public PacketPlayOutRelEntityMoveLook() { + this.g = true; + } + + public PacketPlayOutRelEntityMoveLook(int i, byte b0, byte b1, byte b2, byte b3, byte b4, boolean onGround) { // Spigot - protocol patch + super(i); + this.b = b0; + this.c = b1; + this.d = b2; + this.e = b3; + this.f = b4; + this.g = true; + this.onGround = onGround; // Spigot - protocol patch + } + + public void a(PacketDataSerializer packetdataserializer) { + super.a(packetdataserializer); + this.b = packetdataserializer.readByte(); + this.c = packetdataserializer.readByte(); + this.d = packetdataserializer.readByte(); + this.e = packetdataserializer.readByte(); + this.f = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + super.b(packetdataserializer); + packetdataserializer.writeByte(this.b); + packetdataserializer.writeByte(this.c); + packetdataserializer.writeByte(this.d); + packetdataserializer.writeByte(this.e); + packetdataserializer.writeByte(this.f); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 22 ) + { + packetdataserializer.writeBoolean( onGround ); + } + // Spigot end + } + + public String b() { + return super.b() + String.format(", xa=%d, ya=%d, za=%d, yRot=%d, xRot=%d", new Object[] { Byte.valueOf(this.b), Byte.valueOf(this.c), Byte.valueOf(this.d), Byte.valueOf(this.e), Byte.valueOf(this.f)}); + } + + public void handle(PacketListener packetlistener) { + super.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRemoveEntityEffect.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRemoveEntityEffect.java new file mode 100644 index 0000000..f2c38c5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutRemoveEntityEffect.java @@ -0,0 +1,39 @@ +package net.minecraft.server; + +public class PacketPlayOutRemoveEntityEffect extends Packet { + + private int a; + private int b; + + public PacketPlayOutRemoveEntityEffect() {} + + public PacketPlayOutRemoveEntityEffect(int i, MobEffect mobeffect) { + this.a = i; + this.b = mobeffect.getEffectId(); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readUnsignedByte(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + } else { + packetdataserializer.b( a ); + } + // Spigot end + packetdataserializer.writeByte(this.b); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardObjective.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardObjective.java new file mode 100644 index 0000000..700e94e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardObjective.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayOutScoreboardObjective extends Packet { + + private String a; + private String b; + private int c; + + public PacketPlayOutScoreboardObjective() {} + + public PacketPlayOutScoreboardObjective(ScoreboardObjective scoreboardobjective, int i) { + this.a = scoreboardobjective.getName(); + this.b = scoreboardobjective.getDisplayName(); + this.c = i; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.c(16); + this.b = packetdataserializer.c(32); + this.c = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.a( this.a ); + packetdataserializer.a( this.b ); + packetdataserializer.writeByte( this.c ); + } else + { + packetdataserializer.a( a ); + packetdataserializer.writeByte( c ); + if ( c == 0 || c == 2 ) { + packetdataserializer.a( b ); + packetdataserializer.a( "integer" ); + } + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardScore.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardScore.java new file mode 100644 index 0000000..446e308 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardScore.java @@ -0,0 +1,66 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayOutScoreboardScore extends Packet { + + private String a = ""; + private String b = ""; + private int c; + private int d; + + public PacketPlayOutScoreboardScore() {} + + public PacketPlayOutScoreboardScore(ScoreboardScore scoreboardscore, int i) { + this.a = scoreboardscore.getPlayerName(); + this.b = scoreboardscore.getObjective().getName(); + this.c = scoreboardscore.getScore(); + this.d = i; + } + + public PacketPlayOutScoreboardScore(String s) { + this.a = s; + this.b = ""; + this.c = 0; + this.d = 1; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.c(16); + this.d = packetdataserializer.readByte(); + if (this.d != 1) { + this.b = packetdataserializer.c(16); + this.c = packetdataserializer.readInt(); + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a( this.a ); + packetdataserializer.writeByte( this.d ); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + if ( this.d != 1 ) + { + packetdataserializer.a( this.b ); + packetdataserializer.writeInt( this.c ); + } + } else + { + packetdataserializer.a( this.b ); + if ( this.d != 1 ) + { + packetdataserializer.b( c ); + } + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardTeam.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardTeam.java new file mode 100644 index 0000000..22416fb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutScoreboardTeam.java @@ -0,0 +1,114 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +public class PacketPlayOutScoreboardTeam extends Packet { + + private String a = ""; + private String b = ""; + private String c = ""; + private String d = ""; + private Collection e = new ArrayList(); + private int f; + private int g; + + public PacketPlayOutScoreboardTeam() {} + + public PacketPlayOutScoreboardTeam(ScoreboardTeam scoreboardteam, int i) { + if (16 < scoreboardteam.getName().length()) throw new IllegalArgumentException("Scoreboard team name '" + scoreboardteam.getName() + "' exceeds maximum length of 16."); + this.a = scoreboardteam.getName(); + this.f = i; + if (i == 0 || i == 2) { + if (16 < scoreboardteam.getDisplayName().length()) throw new IllegalArgumentException("Scoreboard team display name '" + scoreboardteam.getDisplayName() + "' exceeds maximum length of 16."); + this.b = scoreboardteam.getDisplayName(); + if (16 < scoreboardteam.getPrefix().length()) throw new IllegalArgumentException("Scoreboard team prefix '" + scoreboardteam.getPrefix() + "' exceeds maximum length of 16."); + this.c = scoreboardteam.getPrefix(); + if (16 < scoreboardteam.getSuffix().length()) throw new IllegalArgumentException("Scoreboard team suffix '" + scoreboardteam.getSuffix() + "' exceeds maximum length of 16."); + this.d = scoreboardteam.getSuffix(); + this.g = scoreboardteam.packOptionData(); + } + + if (i == 0) { + this.e.addAll(scoreboardteam.getPlayerNameSet()); + } + } + + public PacketPlayOutScoreboardTeam(ScoreboardTeam scoreboardteam, Collection collection, int i) { + if (i != 3 && i != 4) { + throw new IllegalArgumentException("Method must be join or leave for player constructor"); + } else if (collection != null && !collection.isEmpty()) { + this.f = i; + this.a = scoreboardteam.getName(); + this.e.addAll(collection); + } else { + throw new IllegalArgumentException("Players cannot be null/empty"); + } + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.c(16); + this.f = packetdataserializer.readByte(); + if (this.f == 0 || this.f == 2) { + this.b = packetdataserializer.c(32); + this.c = packetdataserializer.c(16); + this.d = packetdataserializer.c(16); + this.g = packetdataserializer.readByte(); + } + + if (this.f == 0 || this.f == 3 || this.f == 4) { + short short1 = packetdataserializer.readShort(); + + for (int i = 0; i < short1; ++i) { + this.e.add(packetdataserializer.c(40)); + } + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a(this.a); + packetdataserializer.writeByte(this.f); + if (this.f == 0 || this.f == 2) { + packetdataserializer.a(this.b); + packetdataserializer.a(this.c); + packetdataserializer.a(this.d); + packetdataserializer.writeByte(this.g); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 16 ) + { + packetdataserializer.a( "always" ); + packetdataserializer.writeByte( EnumChatFormat.WHITE.ordinal() ); + } + // Spigot end + } + + if (this.f == 0 || this.f == 3 || this.f == 4) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeShort( this.e.size() ); + } else + { + packetdataserializer.b( e.size() ); + } + // Spigot end + Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + packetdataserializer.a(s); + } + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSetSlot.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSetSlot.java new file mode 100644 index 0000000..8893b61 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSetSlot.java @@ -0,0 +1,36 @@ +package net.minecraft.server; + +public class PacketPlayOutSetSlot extends Packet { + + public int a; // Spigot + public int b; // Spigot + private ItemStack c; + + public PacketPlayOutSetSlot() {} + + public PacketPlayOutSetSlot(int i, int j, ItemStack itemstack) { + this.a = i; + this.b = j; + this.c = itemstack == null ? null : itemstack.cloneItemStack(); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readByte(); + this.b = packetdataserializer.readShort(); + this.c = packetdataserializer.c(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeByte(this.a); + packetdataserializer.writeShort(this.b); + packetdataserializer.a(this.c); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntity.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntity.java new file mode 100644 index 0000000..acb48aa --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntity.java @@ -0,0 +1,174 @@ +package net.minecraft.server; + +public class PacketPlayOutSpawnEntity extends Packet { + + // Rowin was here - Made public + public int a; + public int b; + public int c; + public int d; + public int e; + public int f; + public int g; + public int h; + public int i; + public int j; + public int k; + + public PacketPlayOutSpawnEntity() { + } + + public PacketPlayOutSpawnEntity(Entity entity, int i) { + this(entity, i, 0); + } + + public PacketPlayOutSpawnEntity(Entity entity, int i, int j) { + this.a = entity.getId(); + this.b = MathHelper.floor(entity.locX * 32.0D); + this.c = MathHelper.floor(entity.locY * 32.0D); + this.d = MathHelper.floor(entity.locZ * 32.0D); + this.h = MathHelper.d(entity.pitch * 256.0F / 360.0F); + this.i = MathHelper.d(entity.yaw * 256.0F / 360.0F); + this.j = i; + this.k = j; + if (j > 0) { + double d0 = entity.motX; + double d1 = entity.motY; + double d2 = entity.motZ; + double d3 = 3.9D; + + if (d0 < -d3) { + d0 = -d3; + } + + if (d1 < -d3) { + d1 = -d3; + } + + if (d2 < -d3) { + d2 = -d3; + } + + if (d0 > d3) { + d0 = d3; + } + + if (d1 > d3) { + d1 = d3; + } + + if (d2 > d3) { + d2 = d3; + } + + this.e = (int) (d0 * 8000.0D); + this.f = (int) (d1 * 8000.0D); + this.g = (int) (d2 * 8000.0D); + } + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.a(); + this.j = packetdataserializer.readByte(); + this.b = packetdataserializer.readInt(); + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readInt(); + this.h = packetdataserializer.readByte(); + this.i = packetdataserializer.readByte(); + this.k = packetdataserializer.readInt(); + if (this.k > 0) { + this.e = packetdataserializer.readShort(); + this.f = packetdataserializer.readShort(); + this.g = packetdataserializer.readShort(); + } + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.b(this.a); + packetdataserializer.writeByte(this.j); + // Spigot start - protocol patch + if (j == 71 && packetdataserializer.version >= 28) { + // North: 0 256 + // West: 64 192 + // South: 128 128 + // East: 192 320 + switch (k) { + case 0: + d += 32; + i = 0; + break; + case 1: + b -= 32; + i = 64; + break; + case 2: + d -= 32; + i = 128; + break; + case 3: + b += 32; + i = 192; + break; + } + } + if (j == 70 && packetdataserializer.version >= 36) { + int id = k & 0xFFFF; + int data = k >> 16; + k = id | (data << 12); + } + + if ((j == 50 || j == 70 || j == 74) && packetdataserializer.version >= 16) // Spigot Update - 20140916a + { + c -= 16; + } + + // Spigot end + packetdataserializer.writeInt(this.b); + packetdataserializer.writeInt(this.c); + packetdataserializer.writeInt(this.d); + packetdataserializer.writeByte(this.h); + packetdataserializer.writeByte(this.i); + packetdataserializer.writeInt(this.k); + if (this.k > 0) { + packetdataserializer.writeShort(this.e); + packetdataserializer.writeShort(this.f); + packetdataserializer.writeShort(this.g); + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d, type=%d, x=%.2f, y=%.2f, z=%.2f", new Object[]{Integer.valueOf(this.a), Integer.valueOf(this.j), Float.valueOf((float) this.b / 32.0F), Float.valueOf((float) this.c / 32.0F), Float.valueOf((float) this.d / 32.0F)}); + } + + public void a(int i) { + this.b = i; + } + + public void b(int i) { + this.c = i; + } + + public void c(int i) { + this.d = i; + } + + public void d(int i) { + this.e = i; + } + + public void e(int i) { + this.f = i; + } + + public void f(int i) { + this.g = i; + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntityLiving.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntityLiving.java new file mode 100644 index 0000000..cbff4ca --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntityLiving.java @@ -0,0 +1,108 @@ +package net.minecraft.server; + +import java.util.List; + +public class PacketPlayOutSpawnEntityLiving extends Packet { + + public int a; + private int b; + private int c; + private int d; + private int e; + private int f; + private int g; + private int h; + private byte i; + private byte j; + private byte k; + private DataWatcher l; + private List m; + + public PacketPlayOutSpawnEntityLiving() {} + + public PacketPlayOutSpawnEntityLiving(EntityLiving entityliving) { + this.a = entityliving.getId(); + this.b = (byte) EntityTypes.a(entityliving); + this.c = entityliving.as.a(entityliving.locX); + this.d = MathHelper.floor(entityliving.locY * 32.0D); + this.e = entityliving.as.a(entityliving.locZ); + this.i = (byte) ((int) (entityliving.yaw * 256.0F / 360.0F)); + this.j = (byte) ((int) (entityliving.pitch * 256.0F / 360.0F)); + this.k = (byte) ((int) (entityliving.aO * 256.0F / 360.0F)); + double d0 = 3.9D; + double d1 = entityliving.motX; + double d2 = entityliving.motY; + double d3 = entityliving.motZ; + + if (d1 < -d0) { + d1 = -d0; + } + + if (d2 < -d0) { + d2 = -d0; + } + + if (d3 < -d0) { + d3 = -d0; + } + + if (d1 > d0) { + d1 = d0; + } + + if (d2 > d0) { + d2 = d0; + } + + if (d3 > d0) { + d3 = d0; + } + + this.f = (int) (d1 * 8000.0D); + this.g = (int) (d2 * 8000.0D); + this.h = (int) (d3 * 8000.0D); + this.l = entityliving.getDataWatcher().clone(); // MineHQ + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.a(); + this.b = packetdataserializer.readByte() & 255; + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readInt(); + this.e = packetdataserializer.readInt(); + this.i = packetdataserializer.readByte(); + this.j = packetdataserializer.readByte(); + this.k = packetdataserializer.readByte(); + this.f = packetdataserializer.readShort(); + this.g = packetdataserializer.readShort(); + this.h = packetdataserializer.readShort(); + this.m = DataWatcher.b(packetdataserializer); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.b(this.a); + packetdataserializer.writeByte(this.b & 255); + packetdataserializer.writeInt(this.c); + packetdataserializer.writeInt(this.d); + packetdataserializer.writeInt(this.e); + packetdataserializer.writeByte(this.i); + packetdataserializer.writeByte(this.j); + packetdataserializer.writeByte(this.k); + packetdataserializer.writeShort(this.f); + packetdataserializer.writeShort(this.g); + packetdataserializer.writeShort(this.h); + this.l.a(packetdataserializer, packetdataserializer.version); // Spigot + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d, type=%d, x=%.2f, y=%.2f, z=%.2f, xd=%.2f, yd=%.2f, zd=%.2f", new Object[] { Integer.valueOf(this.a), Integer.valueOf(this.b), Float.valueOf((float) this.c / 32.0F), Float.valueOf((float) this.d / 32.0F), Float.valueOf((float) this.e / 32.0F), Float.valueOf((float) this.f / 8000.0F), Float.valueOf((float) this.g / 8000.0F), Float.valueOf((float) this.h / 8000.0F)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntityPainting.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntityPainting.java new file mode 100644 index 0000000..47efb16 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnEntityPainting.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayOutSpawnEntityPainting extends Packet { + + private int a; + private int b; + private int c; + private int d; + private int e; + private String f; + + public PacketPlayOutSpawnEntityPainting() {} + + public PacketPlayOutSpawnEntityPainting(EntityPainting entitypainting) { + this.a = entitypainting.getId(); + this.b = entitypainting.x; + this.c = entitypainting.y; + this.d = entitypainting.z; + this.e = entitypainting.direction; + this.f = entitypainting.art.B; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.a(); + this.f = packetdataserializer.c(EnumArt.A); + this.b = packetdataserializer.readInt(); + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readInt(); + this.e = packetdataserializer.readInt(); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.b(this.a); + packetdataserializer.a(this.f); + // Spigot start - protocol patch + if ( packetdataserializer.version >= 28 ) + { + // North: 0 256 + // West: 64 192 + // South: 128 128 + // East: 192 320 + switch ( e ) { + case 0: + d += 1; + break; + case 1: + b -= 1; + break; + case 2: + d -= 1; + break; + case 3: + b += 1; + break; + } + } + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.b ); + packetdataserializer.writeInt( this.c ); + packetdataserializer.writeInt( this.d ); + packetdataserializer.writeInt( this.e ); + } else + { + packetdataserializer.writePosition( b, c, d ); + packetdataserializer.writeByte( e ); + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public String b() { + return String.format("id=%d, type=%s, x=%d, y=%d, z=%d", new Object[] { Integer.valueOf(this.a), this.f, Integer.valueOf(this.b), Integer.valueOf(this.c), Integer.valueOf(this.d)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnPosition.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnPosition.java new file mode 100644 index 0000000..191d213 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutSpawnPosition.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +public class PacketPlayOutSpawnPosition extends Packet { + + public int x; // CraftBukkit - private -> public + public int y; // CraftBukkit - private -> public + public int z; // CraftBukkit - private -> public + + public PacketPlayOutSpawnPosition() {} + + public PacketPlayOutSpawnPosition(int i, int j, int k) { + this.x = i; + this.y = j; + this.z = k; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.x = packetdataserializer.readInt(); + this.y = packetdataserializer.readInt(); + this.z = packetdataserializer.readInt(); + } + + public void b(PacketDataSerializer packetdataserializer) { + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.x ); + packetdataserializer.writeInt( this.y ); + packetdataserializer.writeInt( this.z ); + + } else + { + packetdataserializer.writePosition( x, y, z ); + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public boolean a() { + return false; + } + + public String b() { + return String.format("x=%d, y=%d, z=%d", new Object[] { Integer.valueOf(this.x), Integer.valueOf(this.y), Integer.valueOf(this.z)}); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutTileEntityData.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutTileEntityData.java new file mode 100644 index 0000000..ed54ddd --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutTileEntityData.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +public class PacketPlayOutTileEntityData extends Packet { + + private int a; + private int b; + private int c; + private int d; + private NBTTagCompound e; + + public PacketPlayOutTileEntityData() {} + + public PacketPlayOutTileEntityData(int i, int j, int k, int l, NBTTagCompound nbttagcompound) { + this.a = i; + this.b = j; + this.c = k; + this.d = l; + this.e = nbttagcompound; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readShort(); + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readUnsignedByte(); + this.e = packetdataserializer.b(); + } + + public void b(PacketDataSerializer packetdataserializer) { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.a ); + packetdataserializer.writeShort( this.b ); + packetdataserializer.writeInt( this.c ); + } else + { + packetdataserializer.writePosition( a, b, c ); + } + // Spigot end + packetdataserializer.writeByte((byte) this.d); + packetdataserializer.a(this.e); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateAttributes.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateAttributes.java new file mode 100644 index 0000000..0c38297 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateAttributes.java @@ -0,0 +1,94 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +public class PacketPlayOutUpdateAttributes extends Packet { + + private int a; + private final List b = new ArrayList(); + + public PacketPlayOutUpdateAttributes() {} + + public PacketPlayOutUpdateAttributes(int i, Collection collection) { + this.a = i; + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + AttributeInstance attributeinstance = (AttributeInstance) iterator.next(); + + this.b.add(new AttributeSnapshot(this, attributeinstance.getAttribute().getName(), attributeinstance.b(), attributeinstance.c())); + } + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readInt(); + int i = packetdataserializer.readInt(); + + for (int j = 0; j < i; ++j) { + String s = packetdataserializer.c(64); + double d0 = packetdataserializer.readDouble(); + ArrayList arraylist = new ArrayList(); + short short1 = packetdataserializer.readShort(); + + for (int k = 0; k < short1; ++k) { + UUID uuid = new UUID(packetdataserializer.readLong(), packetdataserializer.readLong()); + + arraylist.add(new AttributeModifier(uuid, "Unknown synced attribute modifier", packetdataserializer.readDouble(), packetdataserializer.readByte())); + } + + this.b.add(new AttributeSnapshot(this, s, d0, arraylist)); + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16) + { + packetdataserializer.writeInt( this.a ); + } else + { + packetdataserializer.b( a ); + } + // Spigot end + packetdataserializer.writeInt(this.b.size()); + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + AttributeSnapshot attributesnapshot = (AttributeSnapshot) iterator.next(); + + packetdataserializer.a(attributesnapshot.a()); + packetdataserializer.writeDouble(attributesnapshot.b()); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeShort( attributesnapshot.c().size() ); + } else { + packetdataserializer.b( attributesnapshot.c().size() ); + } + // Spigot end + Iterator iterator1 = attributesnapshot.c().iterator(); + + while (iterator1.hasNext()) { + AttributeModifier attributemodifier = (AttributeModifier) iterator1.next(); + + packetdataserializer.writeLong(attributemodifier.a().getMostSignificantBits()); + packetdataserializer.writeLong(attributemodifier.a().getLeastSignificantBits()); + packetdataserializer.writeDouble(attributemodifier.d()); + packetdataserializer.writeByte(attributemodifier.c()); + } + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateHealth.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateHealth.java new file mode 100644 index 0000000..37ede05 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateHealth.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class PacketPlayOutUpdateHealth extends Packet { + + private float a; + private int b; + private float c; + + public PacketPlayOutUpdateHealth() {} + + public PacketPlayOutUpdateHealth(float f, int i, float f1) { + this.a = f; + this.b = i; + this.c = f1; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readFloat(); + this.b = packetdataserializer.readShort(); + this.c = packetdataserializer.readFloat(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeFloat(this.a); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeShort( this.b ); + } else + { + packetdataserializer.b( this.b ); + } + // Spigot end + packetdataserializer.writeFloat(this.c); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateSign.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateSign.java new file mode 100644 index 0000000..e68fc18 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutUpdateSign.java @@ -0,0 +1,66 @@ +package net.minecraft.server; + +import java.io.IOException; + +import org.bukkit.craftbukkit.util.CraftChatMessage; // Spigot - protocol patch + +public class PacketPlayOutUpdateSign extends Packet { + + private int x; + private int y; + private int z; + private String[] lines; + + public PacketPlayOutUpdateSign() {} + + public PacketPlayOutUpdateSign(int i, int j, int k, String[] astring) { + this.x = i; + this.y = j; + this.z = k; + this.lines = new String[] { astring[0], astring[1], astring[2], astring[3]}; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.x = packetdataserializer.readInt(); + this.y = packetdataserializer.readShort(); + this.z = packetdataserializer.readInt(); + this.lines = new String[4]; + + for (int i = 0; i < 4; ++i) { + this.lines[i] = packetdataserializer.c(15); + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.x ); + packetdataserializer.writeShort( this.y ); + packetdataserializer.writeInt( this.z ); + } else + { + packetdataserializer.writePosition( x, y, z ); + } + + for (int i = 0; i < 4; ++i) { + if ( packetdataserializer.version < 21 ) + { + packetdataserializer.a( this.lines[ i ] ); + } else + { + String line = ChatSerializer.a( CraftChatMessage.fromString( this.lines[ i ] )[ 0 ] ); + packetdataserializer.a( line ); + } + } + // Spigot end + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java new file mode 100644 index 0000000..2a96e2d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java @@ -0,0 +1,54 @@ +package net.minecraft.server; + +import java.util.List; + +public class PacketPlayOutWindowItems extends Packet { + + public int a; // Spigot + public ItemStack[] b; // Spigot + + public PacketPlayOutWindowItems() {} + + public PacketPlayOutWindowItems(int i, List list) { + this.a = i; + this.b = new ItemStack[list.size()]; + + for (int j = 0; j < this.b.length; ++j) { + ItemStack itemstack = (ItemStack) list.get(j); + + this.b[j] = itemstack == null ? null : itemstack.cloneItemStack(); + } + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readUnsignedByte(); + short short1 = packetdataserializer.readShort(); + + this.b = new ItemStack[short1]; + + for (int i = 0; i < short1; ++i) { + this.b[i] = packetdataserializer.c(); + } + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeByte(this.a); + packetdataserializer.writeShort(this.b.length); + ItemStack[] aitemstack = this.b; + int i = aitemstack.length; + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = aitemstack[j]; + + packetdataserializer.a(itemstack); + } + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWorldEvent.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWorldEvent.java new file mode 100644 index 0000000..a9577b8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWorldEvent.java @@ -0,0 +1,56 @@ +package net.minecraft.server; + +public class PacketPlayOutWorldEvent extends Packet { + + private int a; + private int b; + private int c; + private int d; + private int e; + private boolean f; + + public PacketPlayOutWorldEvent() {} + + public PacketPlayOutWorldEvent(int i, int j, int k, int l, int i1, boolean flag) { + this.a = i; + this.c = j; + this.d = k; + this.e = l; + this.b = i1; + this.f = flag; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a = packetdataserializer.readInt(); + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readByte() & 255; + this.e = packetdataserializer.readInt(); + this.b = packetdataserializer.readInt(); + this.f = packetdataserializer.readBoolean(); + } + + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeInt(this.a); + // Spigot start - protocol patch + if ( packetdataserializer.version < 16 ) + { + packetdataserializer.writeInt( this.c ); + packetdataserializer.writeByte( this.d & 255 ); + packetdataserializer.writeInt( this.e ); + } else + { + packetdataserializer.writePosition( c, d, e ); + } + // Spigot end + packetdataserializer.writeInt(this.b); + packetdataserializer.writeBoolean(this.f); + } + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWorldParticles.java b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWorldParticles.java new file mode 100644 index 0000000..07b564f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketPlayOutWorldParticles.java @@ -0,0 +1,174 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.util.HashMap; // Spigot + +public class PacketPlayOutWorldParticles extends Packet { + + private String a; + private float b; + private float c; + private float d; + private float e; + private float f; + private float g; + private float h; + private int i; + + public PacketPlayOutWorldParticles() {} + + public PacketPlayOutWorldParticles(String s, float f, float f1, float f2, float f3, float f4, float f5, float f6, int i) { + this.a = s; + this.b = f; + this.c = f1; + this.d = f2; + this.e = f3; + this.f = f4; + this.g = f5; + this.h = f6; + this.i = i; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.c(64); + this.b = packetdataserializer.readFloat(); + this.c = packetdataserializer.readFloat(); + this.d = packetdataserializer.readFloat(); + this.e = packetdataserializer.readFloat(); + this.f = packetdataserializer.readFloat(); + this.g = packetdataserializer.readFloat(); + this.h = packetdataserializer.readFloat(); + this.i = packetdataserializer.readInt(); + } + + // Spigot start - protocol patch + public void b(PacketDataSerializer packetdataserializer) throws IOException { + String[] parts = this.a.split( "_" ); + Particle particle = Particle.find( parts[ 0 ] ); + if (particle == null) particle = Particle.CRIT; + if ( packetdataserializer.version < 17 ) + { + packetdataserializer.a( this.a ); + } else + { + packetdataserializer.writeInt( particle.ordinal() ); + packetdataserializer.writeBoolean( false ); + } + packetdataserializer.writeFloat(this.b); + packetdataserializer.writeFloat(this.c); + packetdataserializer.writeFloat(this.d); + packetdataserializer.writeFloat(this.e); + packetdataserializer.writeFloat(this.f); + packetdataserializer.writeFloat(this.g); + packetdataserializer.writeFloat(this.h); + packetdataserializer.writeInt(this.i); + if ( packetdataserializer.version >= 17 ) + { + for ( int i = 0; i < particle.extra; i++ ) + { + int toWrite = 0; + if ( parts.length - 1 > i ) + { + try + { + toWrite = Integer.parseInt( parts[i + 1] ); + if ( particle.extra == 1 && parts.length == 3 ) + { + i++; + toWrite = toWrite | (Integer.parseInt( parts[i + 1] ) << 12); + } + } catch ( NumberFormatException e ) + { + + } + } + packetdataserializer.b( toWrite ); + } + } + } + // Spigot end + + public void a(PacketPlayOutListener packetplayoutlistener) { + packetplayoutlistener.a(this); + } + + public void handle(PacketListener packetlistener) { + this.a((PacketPlayOutListener) packetlistener); + } + + // Spigot start - protocol patch + private enum Particle + { + EXPLOSION_NORMAL( "explode" ), + EXPLOSION_LARGE( "largeexplode" ), + EXPLOSION_HUGE( "hugeexplosion" ), + FIREWORKS_SPARK( "fireworksSpark" ), + WATER_BUBBLE( "bubble" ), + WATER_SPLASH( "splash" ), + WATER_WAKE( "wake" ), + SUSPENDED( "suspended" ), + SUSPENDED_DEPTH( "depthsuspend" ), + CRIT( "crit" ), + CRIT_MAGIC( "magicCrit" ), + SMOKE_NORMAL( "smoke" ), + SMOKE_LARGE( "largesmoke" ), + SPELL( "spell" ), + SPELL_INSTANT( "instantSpell" ), + SPELL_MOB( "mobSpell" ), + SPELL_MOB_AMBIENT( "mobSpellAmbient" ), + SPELL_WITCH( "witchMagic" ), + DRIP_WATER( "dripWater" ), + DRIP_LAVA( "dripLava" ), + VILLAGER_ANGRY( "angryVillager" ), + VILLAGER_HAPPY( "happyVillager" ), + TOWN_AURA( "townaura" ), + NOTE( "note" ), + PORTAL( "portal" ), + ENCHANTMENT_TABLE( "enchantmenttable" ), + FLAME( "flame" ), + LAVA( "lava" ), + FOOTSTEP( "footstep" ), + CLOUD( "cloud" ), + REDSTONE( "reddust" ), + SNOWBALL( "snowballpoof" ), + SNOW_SHOVEL( "snowshovel" ), + SLIME( "slime" ), + HEART( "heart" ), + BARRIER( "barrier" ), + ICON_CRACK( "iconcrack", 2 ), + BLOCK_CRACK( "blockcrack", 1 ), + BLOCK_DUST( "blockdust", 1 ), + WATER_DROP( "droplet" ), + ITEM_TAKE( "take" ), + MOB_APPEARANCE( "mobappearance" ); + + public final String name; + public final int extra; + private final static HashMap particleMap = new HashMap(); + + Particle(String name) + { + this( name, 0 ); + } + + Particle(String name, int extra) + { + this.name = name; + this.extra = extra; + } + + public static Particle find(String part) + { + return particleMap.get( part ); + } + + static + { + for ( Particle particle : values() ) + { + particleMap.put( particle.name, particle ); + } + } + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PacketStatusListener.java b/vspigot-server/src/main/java/net/minecraft/server/PacketStatusListener.java new file mode 100644 index 0000000..cf68d57 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PacketStatusListener.java @@ -0,0 +1,135 @@ +package net.minecraft.server; + +import java.net.InetSocketAddress; + +// CraftBukkit start +import java.util.Iterator; + +import org.bukkit.craftbukkit.util.CraftIconCache; +import org.bukkit.entity.Player; + +import net.minecraft.util.com.mojang.authlib.GameProfile; +// CraftBukkit end + +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; + +public class PacketStatusListener implements PacketStatusInListener { + + private final MinecraftServer minecraftServer; + private final NetworkManager networkManager; + + public PacketStatusListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { + this.minecraftServer = minecraftserver; + this.networkManager = networkmanager; + } + + public void a(IChatBaseComponent ichatbasecomponent) {} + + public void a(EnumProtocol enumprotocol, EnumProtocol enumprotocol1) { + if (enumprotocol1 != EnumProtocol.STATUS) { + throw new UnsupportedOperationException("Unexpected change in protocol to " + enumprotocol1); + } + } + + public void a() {} + + public void a(PacketStatusInStart packetstatusinstart) { + // CraftBukkit start - fire ping event + final Object[] players = minecraftServer.getPlayerList().players.toArray(); + class ServerListPingEvent extends org.bukkit.event.server.ServerListPingEvent { + CraftIconCache icon = minecraftServer.server.getServerIcon(); + + ServerListPingEvent() { + super(((InetSocketAddress) networkManager.getSocketAddress()).getAddress(), minecraftServer.getMotd(), minecraftServer.getPlayerList().getMaxPlayers()); + } + + @Override + public void setServerIcon(org.bukkit.util.CachedServerIcon icon) { + if (!(icon instanceof CraftIconCache)) { + throw new IllegalArgumentException(icon + " was not created by " + org.bukkit.craftbukkit.CraftServer.class); + } + this.icon = (CraftIconCache) icon; + } + + @Override + public Iterator iterator() throws UnsupportedOperationException { + return new Iterator() { + int i; + int ret = Integer.MIN_VALUE; + EntityPlayer player; + + @Override + public boolean hasNext() { + if (player != null) { + return true; + } + final Object[] currentPlayers = players; + for (int length = currentPlayers.length, i = this.i; i < length; i++) { + final EntityPlayer player = (EntityPlayer) currentPlayers[i]; + if (player != null) { + this.i = i + 1; + this.player = player; + return true; + } + } + return false; + } + + @Override + public Player next() { + if (!hasNext()) { + throw new java.util.NoSuchElementException(); + } + final EntityPlayer player = this.player; + this.player = null; + this.ret = this.i - 1; + return player.getBukkitEntity(); + } + + @Override + public void remove() { + final Object[] currentPlayers = players; + final int i = this.ret; + if (i < 0 || currentPlayers[i] == null) { + throw new IllegalStateException(); + } + currentPlayers[i] = null; + } + }; + } + } + + ServerListPingEvent event = new ServerListPingEvent(); + this.minecraftServer.server.getPluginManager().callEvent(event); + + java.util.List profiles = new java.util.ArrayList(players.length); + for (Object player : players) { + if (player != null) { + profiles.add(((EntityPlayer) player).getProfile()); + } + } + + ServerPingPlayerSample playerSample = new ServerPingPlayerSample(event.getMaxPlayers(), profiles.size()); + // Spigot Start + if ( !profiles.isEmpty() ) + { + java.util.Collections.shuffle( profiles ); // This sucks, its inefficient but we have no simple way of doing it differently + profiles = profiles.subList( 0, Math.min( profiles.size(), org.spigotmc.SpigotConfig.playerSample ) ); // Cap the sample to n (or less) displayed players, ie: Vanilla behaviour + } + // Spigot End + playerSample.a(profiles.toArray(new GameProfile[profiles.size()])); + + ServerPing ping = new ServerPing(); + ping.setFavicon(event.icon.value); + ping.setMOTD(new ChatComponentText(event.getMotd())); + ping.setPlayerSample(playerSample); + ping.setServerInfo(new ServerPingServerData(minecraftServer.getServerModName() + " " + minecraftServer.getVersion(), networkManager.getVersion())); // TODO: Update when protocol changes + + this.networkManager.handle(new PacketStatusOutServerInfo(ping), NetworkManager.emptyListenerArray); // Poweruser + // CraftBukkit end + } + + public void a(PacketStatusInPing packetstatusinping) { + this.networkManager.handle(new PacketStatusOutPong(packetstatusinping.c()), NetworkManager.emptyListenerArray); // Poweruser + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Path.java b/vspigot-server/src/main/java/net/minecraft/server/Path.java new file mode 100644 index 0000000..90c3ef9 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Path.java @@ -0,0 +1,128 @@ +package net.minecraft.server; + +public class Path { + + private PathPoint[] a = new PathPoint[128]; // CraftBukkit - reduce default size + private int b; + + public Path() {} + + public PathPoint a(PathPoint pathpoint) { + if (pathpoint.d >= 0) { + throw new IllegalStateException("OW KNOWS!"); + } else { + if (this.b == this.a.length) { + PathPoint[] apathpoint = new PathPoint[this.b << 1]; + + System.arraycopy(this.a, 0, apathpoint, 0, this.b); + this.a = apathpoint; + } + + this.a[this.b] = pathpoint; + pathpoint.d = this.b; + this.a(this.b++); + return pathpoint; + } + } + + public void a() { + this.b = 0; + } + + public PathPoint c() { + PathPoint pathpoint = this.a[0]; + + this.a[0] = this.a[--this.b]; + this.a[this.b] = null; + if (this.b > 0) { + this.b(0); + } + + pathpoint.d = -1; + return pathpoint; + } + + public void a(PathPoint pathpoint, float f) { + float f1 = pathpoint.g; + + pathpoint.g = f; + if (f < f1) { + this.a(pathpoint.d); + } else { + this.b(pathpoint.d); + } + } + + private void a(int i) { + PathPoint pathpoint = this.a[i]; + + int j; + + for (float f = pathpoint.g; i > 0; i = j) { + j = i - 1 >> 1; + PathPoint pathpoint1 = this.a[j]; + + if (f >= pathpoint1.g) { + break; + } + + this.a[i] = pathpoint1; + pathpoint1.d = i; + } + + this.a[i] = pathpoint; + pathpoint.d = i; + } + + private void b(int i) { + PathPoint pathpoint = this.a[i]; + float f = pathpoint.g; + + while (true) { + int j = 1 + (i << 1); + int k = j + 1; + + if (j >= this.b) { + break; + } + + PathPoint pathpoint1 = this.a[j]; + float f1 = pathpoint1.g; + PathPoint pathpoint2; + float f2; + + if (k >= this.b) { + pathpoint2 = null; + f2 = Float.POSITIVE_INFINITY; + } else { + pathpoint2 = this.a[k]; + f2 = pathpoint2.g; + } + + if (f1 < f2) { + if (f1 >= f) { + break; + } + + this.a[i] = pathpoint1; + pathpoint1.d = i; + i = j; + } else { + if (f2 >= f) { + break; + } + + this.a[i] = pathpoint2; + pathpoint2.d = i; + i = k; + } + } + + this.a[i] = pathpoint; + pathpoint.d = i; + } + + public boolean e() { + return this.b == 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Pathfinder.java b/vspigot-server/src/main/java/net/minecraft/server/Pathfinder.java new file mode 100644 index 0000000..2181fda --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Pathfinder.java @@ -0,0 +1,293 @@ +package net.minecraft.server; + +public class Pathfinder { + + private IBlockAccess a; + private Path b = new Path(); + private IntHashMap c = new IntHashMap(); + private PathPoint[] d = new PathPoint[32]; + private boolean e; + private boolean f; + private boolean g; + private boolean h; + + public Pathfinder(IBlockAccess iblockaccess, boolean flag, boolean flag1, boolean flag2, boolean flag3) { + this.a = iblockaccess; + this.e = flag; + this.f = flag1; + this.g = flag2; + this.h = flag3; + } + + public PathEntity a(Entity entity, Entity entity1, float f) { + return this.a(entity, entity1.locX, entity1.boundingBox.b, entity1.locZ, f); + } + + public PathEntity a(Entity entity, int i, int j, int k, float f) { + return this.a(entity, (double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F), f); + } + + private PathEntity a(Entity entity, double d0, double d1, double d2, float f) { + this.b.a(); + this.c.c(); + boolean flag = this.g; + int i = MathHelper.floor(entity.boundingBox.b + 0.5D); + + if (this.h && entity.M()) { + i = (int) entity.boundingBox.b; + + for (Block block = this.a.getType(MathHelper.floor(entity.locX), i, MathHelper.floor(entity.locZ)); block == Blocks.WATER || block == Blocks.STATIONARY_WATER; block = this.a.getType(MathHelper.floor(entity.locX), i, MathHelper.floor(entity.locZ))) { + ++i; + } + + flag = this.g; + this.g = false; + } else { + i = MathHelper.floor(entity.boundingBox.b + 0.5D); + } + + PathPoint pathpoint = this.a(MathHelper.floor(entity.boundingBox.a), i, MathHelper.floor(entity.boundingBox.c)); + PathPoint pathpoint1 = this.a(MathHelper.floor(d0 - (double) (entity.width / 2.0F)), MathHelper.floor(d1), MathHelper.floor(d2 - (double) (entity.width / 2.0F))); + PathPoint pathpoint2 = new PathPoint(MathHelper.d(entity.width + 1.0F), MathHelper.d(entity.length + 1.0F), MathHelper.d(entity.width + 1.0F)); + PathEntity pathentity = this.a(entity, pathpoint, pathpoint1, pathpoint2, f); + + this.g = flag; + return pathentity; + } + + private PathEntity a(Entity entity, PathPoint pathpoint, PathPoint pathpoint1, PathPoint pathpoint2, float f) { + pathpoint.e = 0.0F; + pathpoint.f = pathpoint.b(pathpoint1); + pathpoint.g = pathpoint.f; + this.b.a(); + this.b.a(pathpoint); + PathPoint pathpoint3 = pathpoint; + + while (!this.b.e()) { + PathPoint pathpoint4 = this.b.c(); + + if (pathpoint4.equals(pathpoint1)) { + return this.a(pathpoint, pathpoint1); + } + + if (pathpoint4.b(pathpoint1) < pathpoint3.b(pathpoint1)) { + pathpoint3 = pathpoint4; + } + + pathpoint4.i = true; + int i = this.b(entity, pathpoint4, pathpoint2, pathpoint1, f); + + for (int j = 0; j < i; ++j) { + PathPoint pathpoint5 = this.d[j]; + float f1 = pathpoint4.e + pathpoint4.b(pathpoint5); + + if (!pathpoint5.a() || f1 < pathpoint5.e) { + pathpoint5.h = pathpoint4; + pathpoint5.e = f1; + pathpoint5.f = pathpoint5.b(pathpoint1); + if (pathpoint5.a()) { + this.b.a(pathpoint5, pathpoint5.e + pathpoint5.f); + } else { + pathpoint5.g = pathpoint5.e + pathpoint5.f; + this.b.a(pathpoint5); + } + } + } + } + + if (pathpoint3 == pathpoint) { + return null; + } else { + return this.a(pathpoint, pathpoint3); + } + } + + private int b(Entity entity, PathPoint pathpoint, PathPoint pathpoint1, PathPoint pathpoint2, float f) { + int i = 0; + byte b0 = 0; + + if (this.a(entity, pathpoint.a, pathpoint.b + 1, pathpoint.c, pathpoint1) == 1) { + b0 = 1; + } + + PathPoint pathpoint3 = this.a(entity, pathpoint.a, pathpoint.b, pathpoint.c + 1, pathpoint1, b0); + PathPoint pathpoint4 = this.a(entity, pathpoint.a - 1, pathpoint.b, pathpoint.c, pathpoint1, b0); + PathPoint pathpoint5 = this.a(entity, pathpoint.a + 1, pathpoint.b, pathpoint.c, pathpoint1, b0); + PathPoint pathpoint6 = this.a(entity, pathpoint.a, pathpoint.b, pathpoint.c - 1, pathpoint1, b0); + + if (pathpoint3 != null && !pathpoint3.i && pathpoint3.a(pathpoint2) < f) { + this.d[i++] = pathpoint3; + } + + if (pathpoint4 != null && !pathpoint4.i && pathpoint4.a(pathpoint2) < f) { + this.d[i++] = pathpoint4; + } + + if (pathpoint5 != null && !pathpoint5.i && pathpoint5.a(pathpoint2) < f) { + this.d[i++] = pathpoint5; + } + + if (pathpoint6 != null && !pathpoint6.i && pathpoint6.a(pathpoint2) < f) { + this.d[i++] = pathpoint6; + } + + return i; + } + + private PathPoint a(Entity entity, int i, int j, int k, PathPoint pathpoint, int l) { + PathPoint pathpoint1 = null; + int i1 = this.a(entity, i, j, k, pathpoint); + + if (i1 == 2) { + return this.a(i, j, k); + } else { + if (i1 == 1) { + pathpoint1 = this.a(i, j, k); + } + + if (pathpoint1 == null && l > 0 && i1 != -3 && i1 != -4 && this.a(entity, i, j + l, k, pathpoint) == 1) { + pathpoint1 = this.a(i, j + l, k); + j += l; + } + + if (pathpoint1 != null) { + int j1 = 0; + int k1 = 0; + + while (j > 0) { + k1 = this.a(entity, i, j - 1, k, pathpoint); + if (this.g && k1 == -1) { + return null; + } + + if (k1 != 1) { + break; + } + + if (j1++ >= entity.ax()) { + return null; + } + + --j; + if (j > 0) { + pathpoint1 = this.a(i, j, k); + } + } + + if (k1 == -2) { + return null; + } + } + + return pathpoint1; + } + } + + private final PathPoint a(int i, int j, int k) { + int l = PathPoint.a(i, j, k); + PathPoint pathpoint = (PathPoint) this.c.get(l); + + if (pathpoint == null) { + pathpoint = new PathPoint(i, j, k); + this.c.a(l, pathpoint); + } + + return pathpoint; + } + + public int a(Entity entity, int i, int j, int k, PathPoint pathpoint) { + return a(entity, i, j, k, pathpoint, this.g, this.f, this.e); + } + + public static int a(Entity entity, int i, int j, int k, PathPoint pathpoint, boolean flag, boolean flag1, boolean flag2) { + // Poweruser start + return a(entity.world, entity, i, j, k, pathpoint, flag, flag1, flag2); + } + + public int a(IBlockAccess blockaccess, Entity entity, int i, int j, int k, PathPoint pathpoint) { + return a(blockaccess, entity, i, j, k, pathpoint, this.g, this.f, this.e); + } + + public static int a(IBlockAccess blockaccess, Entity entity, int i, int j, int k, PathPoint pathpoint, boolean flag, boolean flag1, boolean flag2) { + // Poweruser end + boolean flag3 = false; + + for (int l = i; l < i + pathpoint.a; ++l) { + for (int i1 = j; i1 < j + pathpoint.b; ++i1) { + for (int j1 = k; j1 < k + pathpoint.c; ++j1) { + Block block = blockaccess.getType(l, i1, j1); // Poweruser - entity.world -> blockaccess + + if (block.getMaterial() != Material.AIR) { + if (block == Blocks.TRAP_DOOR) { + flag3 = true; + } else if (block != Blocks.WATER && block != Blocks.STATIONARY_WATER) { + if (!flag2 && block == Blocks.WOODEN_DOOR) { + return 0; + } + } else { + if (flag) { + return -1; + } + + flag3 = true; + } + + int k1 = block.b(); + + if (k1 == 9) { // Poweruser + int l1 = MathHelper.floor(entity.locX); + int i2 = MathHelper.floor(entity.locY); + int j2 = MathHelper.floor(entity.locZ); + + if (blockaccess.getType(l1, i2, j2).b() != 9 && blockaccess.getType(l1, i2 - 1, j2).b() != 9) { // Poweruser - entity.world -> blockaccess + return -3; + } + } else if (!block.b(entity.world, l, i1, j1) && (!flag1 || block != Blocks.WOODEN_DOOR)) { + if (k1 == 11 || block == Blocks.FENCE_GATE || k1 == 32) { + return -3; + } + + if (block == Blocks.TRAP_DOOR) { + return -4; + } + + Material material = block.getMaterial(); + + if (material != Material.LAVA) { + return 0; + } + + if (!entity.P(blockaccess)) { // Poweruser + return -2; + } + } + } + } + } + } + + return flag3 ? 2 : 1; + } + + private PathEntity a(PathPoint pathpoint, PathPoint pathpoint1) { + int i = 1; + + PathPoint pathpoint2; + + for (pathpoint2 = pathpoint1; pathpoint2.h != null; pathpoint2 = pathpoint2.h) { + ++i; + } + + PathPoint[] apathpoint = new PathPoint[i]; + + pathpoint2 = pathpoint1; + --i; + + for (apathpoint[i] = pathpoint1; pathpoint2.h != null; apathpoint[i] = pathpoint2) { + pathpoint2 = pathpoint2.h; + --i; + } + + return new PathEntity(apathpoint); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalArrowAttack.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalArrowAttack.java new file mode 100644 index 0000000..cb2af02 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalArrowAttack.java @@ -0,0 +1,105 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + +public class PathfinderGoalArrowAttack extends PathfinderGoal { + + private final EntityInsentient a; + private final IRangedEntity b; + private EntityLiving c; + private int d; + private double e; + private int f; + private int g; + private int h; + private float i; + private float j; + + public PathfinderGoalArrowAttack(IRangedEntity irangedentity, double d0, int i, float f) { + this(irangedentity, d0, i, i, f); + } + + public PathfinderGoalArrowAttack(IRangedEntity irangedentity, double d0, int i, int j, float f) { + this.d = -1; + if (!(irangedentity instanceof EntityLiving)) { + throw new IllegalArgumentException("ArrowAttackGoal requires Mob implements RangedAttackMob"); + } else { + this.b = irangedentity; + this.a = (EntityInsentient) irangedentity; + this.e = d0; + this.g = i; + this.h = j; + this.i = f; + this.j = f * f; + this.a(3); + } + } + + public boolean a() { + EntityLiving entityliving = this.a.getGoalTarget(); + + if (entityliving == null) { + return false; + } else { + this.c = entityliving; + return true; + } + } + + public boolean b() { + return this.a() || !this.a.getNavigation().g(); + } + + public void d() { + // CraftBukkit start + EntityTargetEvent.TargetReason reason = this.c.isAlive() ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; + org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent((Entity) b, null, reason); + // CraftBukkit end + this.c = null; + this.f = 0; + this.d = -1; + } + + public void e() { + double d0 = this.a.e(this.c.locX, this.c.boundingBox.b, this.c.locZ); + boolean flag = this.a.getEntitySenses().canSee(this.c); + + if (flag) { + ++this.f; + } else { + this.f = 0; + } + + if (d0 <= (double) this.j && this.f >= 20) { + this.a.getNavigation().h(); + } else { + this.a.getNavigation().a((Entity) this.c, this.e); + } + + this.a.getControllerLook().a(this.c, 30.0F, 30.0F); + float f; + + if (--this.d == 0) { + if (d0 > (double) this.j || !flag) { + return; + } + + f = MathHelper.sqrt(d0) / this.i; + float f1 = f; + + if (f < 0.1F) { + f1 = 0.1F; + } + + if (f1 > 1.0F) { + f1 = 1.0F; + } + + this.b.a(this.c, f1); + this.d = MathHelper.d(f * (float) (this.h - this.g) + (float) this.g); + } else if (this.d < 0) { + f = MathHelper.sqrt(d0) / this.i; + this.d = MathHelper.d(f * (float) (this.h - this.g) + (float) this.g); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalAvoidPlayer.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalAvoidPlayer.java new file mode 100644 index 0000000..4a577d6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalAvoidPlayer.java @@ -0,0 +1,82 @@ +package net.minecraft.server; + +import java.util.List; + +public class PathfinderGoalAvoidPlayer extends PathfinderGoal { + + public final IEntitySelector a = new EntitySelectorViewable(this); + private EntityCreature b; + private double c; + private double d; + private Entity e; + private float f; + private PathEntity g; + private Navigation h; + private Class i; + + public PathfinderGoalAvoidPlayer(EntityCreature entitycreature, Class oclass, float f, double d0, double d1) { + this.b = entitycreature; + this.i = oclass; + this.f = f; + this.c = d0; + this.d = d1; + this.h = entitycreature.getNavigation(); + this.a(1); + } + + public boolean a() { + if (this.i == EntityHuman.class) { + if (this.b instanceof EntityTameableAnimal && ((EntityTameableAnimal) this.b).isTamed()) { + return false; + } + + this.e = this.b.world.findNearbyPlayer(this.b, (double) this.f); + if (this.e == null) { + return false; + } + } else { + List list = this.b.world.a(this.i, this.b.boundingBox.grow((double) this.f, 3.0D, (double) this.f), this.a); + + if (list.isEmpty()) { + return false; + } + + this.e = (Entity) list.get(0); + } + + Vec3D vec3d = RandomPositionGenerator.b(this.b, 16, 7, Vec3D.a(this.e.locX, this.e.locY, this.e.locZ)); + + if (vec3d == null) { + return false; + } else if (this.e.e(vec3d.a, vec3d.b, vec3d.c) < this.e.f((Entity) this.b)) { + return false; + } else { + this.g = this.h.a(net.valorhcf.pathsearch.PositionPathSearchType.AVOIDPLAYER, vec3d.a, vec3d.b, vec3d.c); // Poweruser + return this.g == null ? false : this.g.b(vec3d); + } + } + + public boolean b() { + return !this.h.g(); + } + + public void c() { + this.h.a(this.g, this.c); + } + + public void d() { + this.e = null; + } + + public void e() { + if (this.b.f(this.e) < 49.0D) { + this.b.getNavigation().a(this.d); + } else { + this.b.getNavigation().a(this.c); + } + } + + static EntityCreature a(PathfinderGoalAvoidPlayer pathfindergoalavoidplayer) { + return pathfindergoalavoidplayer.b; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java new file mode 100644 index 0000000..727f5f0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java @@ -0,0 +1,59 @@ +package net.minecraft.server; + +public class PathfinderGoalBreakDoor extends PathfinderGoalDoorInteract { + + private int i; + private int j = -1; + + public PathfinderGoalBreakDoor(EntityInsentient entityinsentient) { + super(entityinsentient); + } + + public boolean a() { + return !super.a() ? false : (!this.a.world.getGameRules().getBoolean("mobGriefing") ? false : !this.e.f((IBlockAccess) this.a.world, this.b, this.c, this.d)); // CraftBukkit - Fix decompilation issue by casting world to IBlockAccess + } + + public void c() { + super.c(); + this.i = 0; + } + + public boolean b() { + double d0 = this.a.e((double) this.b, (double) this.c, (double) this.d); + + return this.i <= 240 && !this.e.f((IBlockAccess) this.a.world, this.b, this.c, this.d) && d0 < 4.0D; // CraftBukkit - Fix decompilation issue by casting world to IBlockAccess + } + + public void d() { + super.d(); + this.a.world.d(this.a.getId(), this.b, this.c, this.d, -1); + } + + public void e() { + super.e(); + if (this.a.aI().nextInt(20) == 0) { + this.a.world.triggerEffect(1010, this.b, this.c, this.d, 0); + } + + ++this.i; + int i = (int) ((float) this.i / 240.0F * 10.0F); + + if (i != this.j) { + this.a.world.d(this.a.getId(), this.b, this.c, this.d, i); + this.j = i; + } + + if (this.i == 240 && this.a.world.difficulty == EnumDifficulty.HARD) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreakDoorEvent(this.a, this.b, this.c, this.d).isCancelled()) { + this.c(); + return; + } + // CraftBukkit end + + this.a.world.setAir(this.b, this.c, this.d); + this.a.world.triggerEffect(1012, this.b, this.c, this.d, 0); + this.a.world.triggerEffect(2001, this.b, this.c, this.d, Block.getId(this.e)); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalBreed.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalBreed.java new file mode 100644 index 0000000..65f8690 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalBreed.java @@ -0,0 +1,112 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +public class PathfinderGoalBreed extends PathfinderGoal { + + private EntityAnimal d; + World a; + private EntityAnimal e; + int b; + double c; + + public PathfinderGoalBreed(EntityAnimal entityanimal, double d0) { + this.d = entityanimal; + this.a = entityanimal.world; + this.c = d0; + this.a(3); + } + + public boolean a() { + if (!this.d.ce()) { + return false; + } else { + this.e = this.f(); + return this.e != null; + } + } + + public boolean b() { + return this.e.isAlive() && this.e.ce() && this.b < 60; + } + + public void d() { + this.e = null; + this.b = 0; + } + + public void e() { + this.d.getControllerLook().a(this.e, 10.0F, (float) this.d.x()); + this.d.getNavigation().a((Entity) this.e, this.c); + ++this.b; + if (this.b >= 60 && this.d.f(this.e) < 9.0D) { + this.g(); + } + } + + private EntityAnimal f() { + float f = 8.0F; + List list = this.a.a(this.d.getClass(), this.d.boundingBox.grow((double) f, (double) f, (double) f)); + double d0 = Double.MAX_VALUE; + EntityAnimal entityanimal = null; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityAnimal entityanimal1 = (EntityAnimal) iterator.next(); + + if (this.d.mate(entityanimal1) && this.d.f(entityanimal1) < d0) { + entityanimal = entityanimal1; + d0 = this.d.f(entityanimal1); + } + } + + return entityanimal; + } + + private void g() { + EntityAgeable entityageable = this.d.createChild(this.e); + + if (entityageable != null) { + // CraftBukkit start - set persistence for tame animals + if (entityageable instanceof EntityTameableAnimal && ((EntityTameableAnimal) entityageable).isTamed()) { + entityageable.persistent = true; + } + // CraftBukkit end + EntityHuman entityhuman = this.d.cd(); + + if (entityhuman == null && this.e.cd() != null) { + entityhuman = this.e.cd(); + } + + if (entityhuman != null) { + entityhuman.a(StatisticList.x); + if (this.d instanceof EntityCow) { + entityhuman.a((Statistic) AchievementList.H); + } + } + + this.d.setAge(6000); + this.e.setAge(6000); + this.d.cf(); + this.e.cf(); + entityageable.setAge(-24000); + entityageable.setPositionRotation(this.d.locX, this.d.locY, this.d.locZ, 0.0F, 0.0F); + this.a.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + Random random = this.d.aI(); + + for (int i = 0; i < 7; ++i) { + double d0 = random.nextGaussian() * 0.02D; + double d1 = random.nextGaussian() * 0.02D; + double d2 = random.nextGaussian() * 0.02D; + + this.a.addParticle("heart", this.d.locX + (double) (random.nextFloat() * this.d.width * 2.0F) - (double) this.d.width, this.d.locY + 0.5D + (double) (random.nextFloat() * this.d.length), this.d.locZ + (double) (random.nextFloat() * this.d.width * 2.0F) - (double) this.d.width, d0, d1, d2); + } + + if (this.a.getGameRules().getBoolean("doMobLoot")) { + this.a.addEntity(new EntityExperienceOrb(this.a, this.d.locX, this.d.locY, this.d.locZ, random.nextInt(7) + 1)); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalEatTile.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalEatTile.java new file mode 100644 index 0000000..a38f2a2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalEatTile.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.Material; +// CraftBukkit end + +public class PathfinderGoalEatTile extends PathfinderGoal { + + private EntityInsentient b; + private World c; + int a; + + public PathfinderGoalEatTile(EntityInsentient entityinsentient) { + this.b = entityinsentient; + this.c = entityinsentient.world; + this.a(7); + } + + public boolean a() { + if (this.b.aI().nextInt(this.b.isBaby() ? 50 : 1000) != 0) { + return false; + } else { + int i = MathHelper.floor(this.b.locX); + int j = MathHelper.floor(this.b.locY); + int k = MathHelper.floor(this.b.locZ); + + return this.c.getType(i, j, k) == Blocks.LONG_GRASS && this.c.getData(i, j, k) == 1 ? true : this.c.getType(i, j - 1, k) == Blocks.GRASS; + } + } + + public void c() { + this.a = 40; + this.c.broadcastEntityEffect(this.b, (byte) 10); + this.b.getNavigation().h(); + } + + public void d() { + this.a = 0; + } + + public boolean b() { + return this.a > 0; + } + + public int f() { + return this.a; + } + + public void e() { + this.a = Math.max(0, this.a - 1); + if (this.a == 4) { + int i = MathHelper.floor(this.b.locX); + int j = MathHelper.floor(this.b.locY); + int k = MathHelper.floor(this.b.locZ); + + if (this.c.getType(i, j, k) == Blocks.LONG_GRASS) { + // CraftBukkit + if (!CraftEventFactory.callEntityChangeBlockEvent(this.b, this.b.world.getWorld().getBlockAt(i, j, k), Material.AIR, !this.c.getGameRules().getBoolean("mobGriefing")).isCancelled()) { + this.c.setAir(i, j, k, false); + } + + this.b.p(); + } else if (this.c.getType(i, j - 1, k) == Blocks.GRASS) { + // CraftBukkit + if (!CraftEventFactory.callEntityChangeBlockEvent(this.b, this.b.world.getWorld().getBlockAt(i, j - 1, k), Material.DIRT, !this.c.getGameRules().getBoolean("mobGriefing")).isCancelled()) { + this.c.triggerEffect(2001, i, j - 1, k, Block.getId(Blocks.GRASS)); + this.c.setTypeAndData(i, j - 1, k, Blocks.DIRT, 0, 2); + } + + this.b.p(); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalFleeSun.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalFleeSun.java new file mode 100644 index 0000000..035d785 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalFleeSun.java @@ -0,0 +1,65 @@ +package net.minecraft.server; + +import java.util.Random; + +public class PathfinderGoalFleeSun extends PathfinderGoal { + + private EntityCreature a; + private double b; + private double c; + private double d; + private double e; + private World f; + + public PathfinderGoalFleeSun(EntityCreature entitycreature, double d0) { + this.a = entitycreature; + this.e = d0; + this.f = entitycreature.world; + this.a(1); + } + + public boolean a() { + if (!this.f.w()) { + return false; + } else if (!this.a.isBurning()) { + return false; + } else if (!this.f.i(MathHelper.floor(this.a.locX), (int) this.a.boundingBox.b, MathHelper.floor(this.a.locZ))) { + return false; + } else { + Vec3D vec3d = this.f(); + + if (vec3d == null) { + return false; + } else { + this.b = vec3d.a; + this.c = vec3d.b; + this.d = vec3d.c; + return true; + } + } + } + + public boolean b() { + return !this.a.getNavigation().g(); + } + + public void c() { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.FLEESUN, this.b, this.c, this.d, this.e); // Poweruser + } + + private Vec3D f() { + Random random = this.a.aI(); + + for (int i = 0; i < 10; ++i) { + int j = MathHelper.floor(this.a.locX + (double) random.nextInt(20) - 10.0D); + int k = MathHelper.floor(this.a.boundingBox.b + (double) random.nextInt(6) - 3.0D); + int l = MathHelper.floor(this.a.locZ + (double) random.nextInt(20) - 10.0D); + + if (!this.f.i(j, k, l) && this.a.a(j, k, l) < 0.0F) { + return Vec3D.a((double) j, (double) k, (double) l); + } + } + + return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java new file mode 100644 index 0000000..4f476e6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +public class PathfinderGoalHurtByTarget extends PathfinderGoalTarget { + + boolean a; + private int b; + + public PathfinderGoalHurtByTarget(EntityCreature entitycreature, boolean flag) { + super(entitycreature, false); + this.a = flag; + this.a(1); + } + + public boolean a() { + int i = this.c.aK(); + + return i != this.b && this.a(this.c.getLastDamager(), false); + } + + public void c() { + this.c.setGoalTarget(this.c.getLastDamager()); + this.b = this.c.aK(); + if (this.a) { + double d0 = this.f(); + List list = this.c.world.a(this.c.getClass(), AxisAlignedBB.a(this.c.locX, this.c.locY, this.c.locZ, this.c.locX + 1.0D, this.c.locY + 1.0D, this.c.locZ + 1.0D).grow(d0, 10.0D, d0)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityCreature entitycreature = (EntityCreature) iterator.next(); + + if (this.c != entitycreature && entitycreature.getGoalTarget() == null && !entitycreature.c(this.c.getLastDamager())) { + // CraftBukkit start - call EntityTargetEvent + org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(entitycreature, this.c.getLastDamager(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY); + if (event.isCancelled()) { + continue; + } + entitycreature.setGoalTarget(event.getTarget() == null ? null : ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle()); + // CraftBukkit end + } + } + } + + super.c(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalJumpOnBlock.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalJumpOnBlock.java new file mode 100644 index 0000000..58d3209 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalJumpOnBlock.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +public class PathfinderGoalJumpOnBlock extends PathfinderGoal { + + private final EntityOcelot a; + private final double b; + private int c; + private int d; + private int e; + private int f; + private int g; + private int h; + + public PathfinderGoalJumpOnBlock(EntityOcelot entityocelot, double d0) { + this.a = entityocelot; + this.b = d0; + this.a(5); + } + + public boolean a() { + return this.a.isTamed() && !this.a.isSitting() && this.a.aI().nextDouble() <= 0.006500000134110451D && this.f(); + } + + public boolean b() { + return this.c <= this.e && this.d <= 60 && this.a(this.a.world, this.f, this.g, this.h); + } + + public void c() { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.JUMPONBLOCK, (double) ((float) this.f) + 0.5D, (double) (this.g + 1), (double) ((float) this.h) + 0.5D, this.b); // Poweruser + this.c = 0; + this.d = 0; + this.e = this.a.aI().nextInt(this.a.aI().nextInt(1200) + 1200) + 1200; + this.a.getGoalSit().setSitting(false); + } + + public void d() { + this.a.setSitting(false); + } + + public void e() { + ++this.c; + this.a.getGoalSit().setSitting(false); + if (this.a.e((double) this.f, (double) (this.g + 1), (double) this.h) > 1.0D) { + this.a.setSitting(false); + this.a.getNavigation().a((double) ((float) this.f) + 0.5D, (double) (this.g + 1), (double) ((float) this.h) + 0.5D, this.b); + ++this.d; + } else if (!this.a.isSitting()) { + this.a.setSitting(true); + } else { + --this.d; + } + } + + private boolean f() { + int i = (int) this.a.locY; + double d0 = 2.147483647E9D; + + for (int j = (int) this.a.locX - 8; (double) j < this.a.locX + 8.0D; ++j) { + for (int k = (int) this.a.locZ - 8; (double) k < this.a.locZ + 8.0D; ++k) { + if (this.a(this.a.world, j, i, k) && this.a.world.isEmpty(j, i + 1, k)) { + double d1 = this.a.e((double) j, (double) i, (double) k); + + if (d1 < d0) { + this.f = j; + this.g = i; + this.h = k; + d0 = d1; + } + } + } + } + + return d0 < 2.147483647E9D; + } + + private boolean a(World world, int i, int j, int k) { + Block block = world.getType(i, j, k); + int l = world.getData(i, j, k); + + if (block == Blocks.CHEST) { + TileEntityChest tileentitychest = (TileEntityChest) world.getTileEntity(i, j, k); + + if (tileentitychest.o < 1) { + return true; + } + } else { + if (block == Blocks.BURNING_FURNACE) { + return true; + } + + if (block == Blocks.BED && !BlockBed.b(l)) { + return true; + } + } + + return false; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java new file mode 100644 index 0000000..f7bd8e8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java @@ -0,0 +1,90 @@ +package net.minecraft.server; + +public class PathfinderGoalMakeLove extends PathfinderGoal { + + private EntityVillager b; + private EntityVillager c; + private World d; + private int e; + Village a; + + public PathfinderGoalMakeLove(EntityVillager entityvillager) { + this.b = entityvillager; + this.d = entityvillager.world; + this.a(3); + } + + public boolean a() { + if (this.b.getAge() != 0) { + return false; + } else if (this.b.aI().nextInt(500) != 0) { + return false; + } else { + this.a = this.d.villages.getClosestVillage(MathHelper.floor(this.b.locX), MathHelper.floor(this.b.locY), MathHelper.floor(this.b.locZ), 0); + if (this.a == null) { + return false; + } else if (!this.f()) { + return false; + } else { + Entity entity = this.d.a(EntityVillager.class, this.b.boundingBox.grow(8.0D, 3.0D, 8.0D), (Entity) this.b); + + if (entity == null) { + return false; + } else { + this.c = (EntityVillager) entity; + return this.c.getAge() == 0; + } + } + } + } + + public void c() { + this.e = 300; + this.b.i(true); + } + + public void d() { + this.a = null; + this.c = null; + this.b.i(false); + } + + public boolean b() { + return this.e >= 0 && this.f() && this.b.getAge() == 0; + } + + public void e() { + --this.e; + this.b.getControllerLook().a(this.c, 10.0F, 30.0F); + if (this.b.f(this.c) > 2.25D) { + this.b.getNavigation().a((Entity) this.c, 0.25D); + } else if (this.e == 0 && this.c.ca()) { + this.g(); + } + + if (this.b.aI().nextInt(35) == 0) { + this.d.broadcastEntityEffect(this.b, (byte) 12); + } + } + + private boolean f() { + if (!this.a.i()) { + return false; + } else { + int i = (int) ((double) ((float) this.a.getDoorCount()) * 0.35D); + + return this.a.getPopulationCount() < i; + } + } + + private void g() { + EntityVillager entityvillager = this.b.b((EntityAgeable) this.c); + + this.c.setAge(6000); + this.b.setAge(6000); + entityvillager.setAge(-24000); + entityvillager.setPositionRotation(this.b.locX, this.b.locY, this.b.locZ, 0.0F, 0.0F); + this.d.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + this.d.broadcastEntityEffect(entityvillager, (byte) 12); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMeleeAttack.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMeleeAttack.java new file mode 100644 index 0000000..be541f8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMeleeAttack.java @@ -0,0 +1,103 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + +public class PathfinderGoalMeleeAttack extends PathfinderGoal { + + World a; + EntityCreature b; + int c; + double d; + boolean e; + PathEntity f; + Class g; + private int h; + private double i; + private double j; + private double k; + + public PathfinderGoalMeleeAttack(EntityCreature entitycreature, Class oclass, double d0, boolean flag) { + this(entitycreature, d0, flag); + this.g = oclass; + } + + public PathfinderGoalMeleeAttack(EntityCreature entitycreature, double d0, boolean flag) { + this.b = entitycreature; + this.a = entitycreature.world; + this.d = d0; + this.e = flag; + this.a(3); + } + + public boolean a() { + EntityLiving entityliving = this.b.getGoalTarget(); + + if (entityliving == null) { + return false; + } else if (!entityliving.isAlive()) { + return false; + } else if (this.g != null && !this.g.isAssignableFrom(entityliving.getClass())) { + return false; + } else { + this.f = this.b.getNavigation().a(entityliving); + return this.f != null; + } + } + + public boolean b() { + EntityLiving entityliving = this.b.getGoalTarget(); + + // CraftBukkit start + EntityTargetEvent.TargetReason reason = this.b.getGoalTarget() == null ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; + if (this.b.getGoalTarget() == null || (this.b.getGoalTarget() != null && !this.b.getGoalTarget().isAlive())) { + org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent(b, null, reason); + } + // CraftBukkit end + + return entityliving == null ? false : (!entityliving.isAlive() ? false : (!this.e ? !this.b.getNavigation().g() : this.b.b(MathHelper.floor(entityliving.locX), MathHelper.floor(entityliving.locY), MathHelper.floor(entityliving.locZ)))); + } + + public void c() { + this.b.getNavigation().a(this.f, this.d); + this.h = 0; + } + + public void d() { + this.b.getNavigation().h(); + } + + public void e() { + EntityLiving entityliving = this.b.getGoalTarget(); + + this.b.getControllerLook().a(entityliving, 30.0F, 30.0F); + double d0 = this.b.e(entityliving.locX, entityliving.boundingBox.b, entityliving.locZ); + double d1 = (double) (this.b.width * 2.0F * this.b.width * 2.0F + entityliving.width); + + --this.h; + if ((this.e || this.b.getEntitySenses().canSee(entityliving)) && this.h <= 0 && (this.i == 0.0D && this.j == 0.0D && this.k == 0.0D || entityliving.e(this.i, this.j, this.k) >= 1.0D || this.b.aI().nextFloat() < 0.05F)) { + this.i = entityliving.locX; + this.j = entityliving.boundingBox.b; + this.k = entityliving.locZ; + this.h = 4 + this.b.aI().nextInt(7); + if (d0 > 1024.0D) { + this.h += 10; + } else if (d0 > 256.0D) { + this.h += 5; + } + + if (!this.b.getNavigation().a((Entity) entityliving, this.d)) { + this.h += 15; + } + } + + this.c = Math.max(this.c - 1, 0); + if (d0 <= d1 && this.c <= 20) { + this.c = 20; + if (this.b.be() != null) { + this.b.ba(); + } + + this.b.n(entityliving); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveIndoors.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveIndoors.java new file mode 100644 index 0000000..fdb382d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveIndoors.java @@ -0,0 +1,62 @@ +package net.minecraft.server; + +public class PathfinderGoalMoveIndoors extends PathfinderGoal { + + private EntityCreature a; + private VillageDoor b; + private int c = -1; + private int d = -1; + + public PathfinderGoalMoveIndoors(EntityCreature entitycreature) { + this.a = entitycreature; + this.a(1); + } + + public boolean a() { + int i = MathHelper.floor(this.a.locX); + int j = MathHelper.floor(this.a.locY); + int k = MathHelper.floor(this.a.locZ); + + if ((!this.a.world.w() || this.a.world.Q() || !this.a.world.getBiome(i, k).e()) && !this.a.world.worldProvider.g) { + if (this.a.aI().nextInt(50) != 0) { + return false; + } else if (this.c != -1 && this.a.e((double) this.c, this.a.locY, (double) this.d) < 4.0D) { + return false; + } else { + Village village = this.a.world.villages.getClosestVillage(i, j, k, 14); + + if (village == null) { + return false; + } else { + this.b = village.c(i, j, k); + return this.b != null; + } + } + } else { + return false; + } + } + + public boolean b() { + return !this.a.getNavigation().g(); + } + + public void c() { + this.c = -1; + if (this.a.e((double) this.b.getIndoorsX(), (double) this.b.locY, (double) this.b.getIndoorsZ()) > 256.0D) { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 14, 3, Vec3D.a((double) this.b.getIndoorsX() + 0.5D, (double) this.b.getIndoorsY(), (double) this.b.getIndoorsZ() + 0.5D)); + + if (vec3d != null) { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.MOVEINDOORS, vec3d.a, vec3d.b, vec3d.c, 1.0D); // Poweruser + } + } else { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.MOVEINDOORS, (double) this.b.getIndoorsX() + 0.5D, (double) this.b.getIndoorsY(), (double) this.b.getIndoorsZ() + 0.5D, 1.0D); // Poweruser + } + } + + public void d() { + this.c = this.b.getIndoorsX(); + this.d = this.b.getIndoorsZ(); + this.b = null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveThroughVillage.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveThroughVillage.java new file mode 100644 index 0000000..2d7adbe --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveThroughVillage.java @@ -0,0 +1,121 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class PathfinderGoalMoveThroughVillage extends PathfinderGoal { + + private EntityCreature a; + private double b; + private PathEntity c; + private VillageDoor d; + private boolean e; + private List f = new ArrayList(); + + public PathfinderGoalMoveThroughVillage(EntityCreature entitycreature, double d0, boolean flag) { + this.a = entitycreature; + this.b = d0; + this.e = flag; + this.a(1); + } + + public boolean a() { + this.f(); + if (this.e && this.a.world.w()) { + return false; + } else { + Village village = this.a.world.villages.getClosestVillage(MathHelper.floor(this.a.locX), MathHelper.floor(this.a.locY), MathHelper.floor(this.a.locZ), 0); + + if (village == null) { + return false; + } else { + this.d = this.a(village); + if (this.d == null) { + return false; + } else { + boolean flag = this.a.getNavigation().c(); + + this.a.getNavigation().b(false); + this.c = this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.MOVETHROUGHVILLAGE, (double) this.d.locX, (double) this.d.locY, (double) this.d.locZ); // Poweruser + this.a.getNavigation().b(flag); + if (this.c != null) { + return true; + } else { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 10, 7, Vec3D.a((double) this.d.locX, (double) this.d.locY, (double) this.d.locZ)); + + if (vec3d == null) { + return false; + } else { + this.a.getNavigation().b(false); + this.c = this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.MOVETHROUGHVILLAGE, vec3d.a, vec3d.b, vec3d.c); // Poweruser + this.a.getNavigation().b(flag); + return this.c != null; + } + } + } + } + } + } + + public boolean b() { + if (this.a.getNavigation().g()) { + return false; + } else { + float f = this.a.width + 4.0F; + + return this.a.e((double) this.d.locX, (double) this.d.locY, (double) this.d.locZ) > (double) (f * f); + } + } + + public void c() { + this.a.getNavigation().a(this.c, this.b); + } + + public void d() { + if (this.a.getNavigation().g() || this.a.e((double) this.d.locX, (double) this.d.locY, (double) this.d.locZ) < 16.0D) { + this.f.add(this.d); + } + } + + private VillageDoor a(Village village) { + VillageDoor villagedoor = null; + int i = Integer.MAX_VALUE; + List list = village.getDoors(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor1 = (VillageDoor) iterator.next(); + int j = villagedoor1.b(MathHelper.floor(this.a.locX), MathHelper.floor(this.a.locY), MathHelper.floor(this.a.locZ)); + + if (j < i && !this.a(villagedoor1)) { + villagedoor = villagedoor1; + i = j; + } + } + + return villagedoor; + } + + private boolean a(VillageDoor villagedoor) { + Iterator iterator = this.f.iterator(); + + VillageDoor villagedoor1; + + do { + if (!iterator.hasNext()) { + return false; + } + + villagedoor1 = (VillageDoor) iterator.next(); + } while (villagedoor.locX != villagedoor1.locX || villagedoor.locY != villagedoor1.locY || villagedoor.locZ != villagedoor1.locZ); + + return true; + } + + private void f() { + if (this.f.size() > 15) { + this.f.remove(0); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveTowardsRestriction.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveTowardsRestriction.java new file mode 100644 index 0000000..a8a0c38 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveTowardsRestriction.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +public class PathfinderGoalMoveTowardsRestriction extends PathfinderGoal { + + private EntityCreature a; + private double b; + private double c; + private double d; + private double e; + + public PathfinderGoalMoveTowardsRestriction(EntityCreature entitycreature, double d0) { + this.a = entitycreature; + this.e = d0; + this.a(1); + } + + public boolean a() { + if (this.a.bU()) { + return false; + } else { + ChunkCoordinates chunkcoordinates = this.a.bV(); + Vec3D vec3d = RandomPositionGenerator.a(this.a, 16, 7, Vec3D.a((double) chunkcoordinates.x, (double) chunkcoordinates.y, (double) chunkcoordinates.z)); + + if (vec3d == null) { + return false; + } else { + this.b = vec3d.a; + this.c = vec3d.b; + this.d = vec3d.c; + return true; + } + } + } + + public boolean b() { + return !this.a.getNavigation().g(); + } + + public void c() { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.MOVETOWARDSRESTRICTION, this.b, this.c, this.d, this.e); // Poweruser + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveTowardsTarget.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveTowardsTarget.java new file mode 100644 index 0000000..60ffa0c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalMoveTowardsTarget.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +public class PathfinderGoalMoveTowardsTarget extends PathfinderGoal { + + private EntityCreature a; + private EntityLiving b; + private double c; + private double d; + private double e; + private double f; + private float g; + + public PathfinderGoalMoveTowardsTarget(EntityCreature entitycreature, double d0, float f) { + this.a = entitycreature; + this.f = d0; + this.g = f; + this.a(1); + } + + public boolean a() { + this.b = this.a.getGoalTarget(); + if (this.b == null) { + return false; + } else if (this.b.f(this.a) > (double) (this.g * this.g)) { + return false; + } else { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 16, 7, Vec3D.a(this.b.locX, this.b.locY, this.b.locZ)); + + if (vec3d == null) { + return false; + } else { + this.c = vec3d.a; + this.d = vec3d.b; + this.e = vec3d.c; + return true; + } + } + } + + public boolean b() { + return !this.a.getNavigation().g() && this.b.isAlive() && this.b.f(this.a) < (double) (this.g * this.g); + } + + public void d() { + this.b = null; + } + + public void c() { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.MOVETOWARDSTARGET, this.c, this.d, this.e, this.f); // Poweruser + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java new file mode 100644 index 0000000..30a05c6 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java @@ -0,0 +1,54 @@ +package net.minecraft.server; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.List; + +public class PathfinderGoalNearestAttackableTarget extends PathfinderGoalTarget { + + private final Class a; + private final int b; + private final DistanceComparator e; + private final IEntitySelector f; + private WeakReference g; // Spigot Update - 20140921a + + public PathfinderGoalNearestAttackableTarget(EntityCreature entitycreature, Class oclass, int i, boolean flag) { + this(entitycreature, oclass, i, flag, false); + } + + public PathfinderGoalNearestAttackableTarget(EntityCreature entitycreature, Class oclass, int i, boolean flag, boolean flag1) { + this(entitycreature, oclass, i, flag, flag1, (IEntitySelector) null); + } + + public PathfinderGoalNearestAttackableTarget(EntityCreature entitycreature, Class oclass, int i, boolean flag, boolean flag1, IEntitySelector ientityselector) { + super(entitycreature, flag, flag1); + this.g = new WeakReference(null); + this.a = oclass; + this.b = i; + this.e = new DistanceComparator(entitycreature); + this.a(1); + this.f = new EntitySelectorNearestAttackableTarget(this, ientityselector); + } + + public boolean a() { + if (this.b > 0 && this.c.aI().nextInt(this.b) != 0) { + return false; + } else { + double d0 = this.f(); + List list = this.c.world.a(this.a, this.c.boundingBox.grow(d0, 4.0D, d0), this.f); + + Collections.sort(list, this.e); + if (list.isEmpty()) { + return false; + } else { + this.g = new WeakReference((EntityLiving) list.get(0)); + return true; + } + } + } + + public void c() { + this.c.setGoalTarget(this.g.get()); + super.c(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalOcelotAttack.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalOcelotAttack.java new file mode 100644 index 0000000..0c08df3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalOcelotAttack.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + +public class PathfinderGoalOcelotAttack extends PathfinderGoal { + + World a; + EntityInsentient b; + EntityLiving c; + int d; + + public PathfinderGoalOcelotAttack(EntityInsentient entityinsentient) { + this.b = entityinsentient; + this.a = entityinsentient.world; + this.a(3); + } + + public boolean a() { + EntityLiving entityliving = this.b.getGoalTarget(); + + if (entityliving == null) { + return false; + } else { + this.c = entityliving; + return true; + } + } + + public boolean b() { + return !this.c.isAlive() ? false : (this.b.f(this.c) > 225.0D ? false : !this.b.getNavigation().g() || this.a()); + } + + public void d() { + // CraftBukkit start + EntityTargetEvent.TargetReason reason = this.c.isAlive() ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; + org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent(this.c, null, reason); + // CraftBukkit end + this.c = null; + this.b.getNavigation().h(); + } + + public void e() { + this.b.getControllerLook().a(this.c, 30.0F, 30.0F); + double d0 = (double) (this.b.width * 2.0F * this.b.width * 2.0F); + double d1 = this.b.e(this.c.locX, this.c.boundingBox.b, this.c.locZ); + double d2 = 0.8D; + + if (d1 > d0 && d1 < 16.0D) { + d2 = 1.33D; + } else if (d1 < 225.0D) { + d2 = 0.6D; + } + + this.b.getNavigation().a((Entity) this.c, d2); + this.d = Math.max(this.d - 1, 0); + if (d1 <= d0) { + if (this.d <= 0) { + this.d = 20; + this.b.n(this.c); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalPanic.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalPanic.java new file mode 100644 index 0000000..6ee0c3a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalPanic.java @@ -0,0 +1,47 @@ +package net.minecraft.server; + +public class PathfinderGoalPanic extends PathfinderGoal { + + private EntityCreature a; + private double b; + private double c; + private double d; + private double e; + + public PathfinderGoalPanic(EntityCreature entitycreature, double d0) { + this.a = entitycreature; + this.b = d0; + this.a(1); + } + + public boolean a() { + if (this.a.getLastDamager() == null && !this.a.isBurning()) { + return false; + } else { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 5, 4); + + if (vec3d == null) { + return false; + } else { + this.c = vec3d.a; + this.d = vec3d.b; + this.e = vec3d.c; + return true; + } + } + } + + public void c() { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.PANIC, this.c, this.d, this.e, this.b); // Poweruser + } + + public boolean b() { + // CraftBukkit start - introduce a temporary timeout hack until this is fixed properly + if ((this.a.ticksLived - this.a.aK()) > 100) { + this.a.b((EntityLiving) null); + return false; + } + // CraftBukkit end + return !this.a.getNavigation().g(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalPlay.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalPlay.java new file mode 100644 index 0000000..c9c8f23 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalPlay.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +public class PathfinderGoalPlay extends PathfinderGoal { + + private EntityVillager a; + private EntityLiving b; + private double c; + private int d; + + public PathfinderGoalPlay(EntityVillager entityvillager, double d0) { + this.a = entityvillager; + this.c = d0; + this.a(1); + } + + public boolean a() { + if (this.a.getAge() >= 0) { + return false; + } else if (this.a.aI().nextInt(400) != 0) { + return false; + } else { + List list = this.a.world.a(EntityVillager.class, this.a.boundingBox.grow(6.0D, 3.0D, 6.0D)); + double d0 = Double.MAX_VALUE; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityVillager entityvillager = (EntityVillager) iterator.next(); + + if (entityvillager != this.a && !entityvillager.cb() && entityvillager.getAge() < 0) { + double d1 = entityvillager.f(this.a); + + if (d1 <= d0) { + d0 = d1; + this.b = entityvillager; + } + } + } + + if (this.b == null) { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 16, 3); + + if (vec3d == null) { + return false; + } + } + + return true; + } + } + + public boolean b() { + return this.d > 0; + } + + public void c() { + if (this.b != null) { + this.a.j(true); + } + + this.d = 1000; + } + + public void d() { + this.a.j(false); + this.b = null; + } + + public void e() { + --this.d; + if (this.b != null) { + if (this.a.f(this.b) > 4.0D) { + this.a.getNavigation().a((Entity) this.b, this.c); + } + } else if (this.a.getNavigation().g()) { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 16, 3); + + if (vec3d == null) { + return; + } + + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.PLAY, vec3d.a, vec3d.b, vec3d.c, this.c); // Poweruser + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalRandomStroll.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalRandomStroll.java new file mode 100644 index 0000000..3c07979 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalRandomStroll.java @@ -0,0 +1,46 @@ +package net.minecraft.server; + +public class PathfinderGoalRandomStroll extends PathfinderGoal { + + private EntityCreature a; + private double b; + private double c; + private double d; + private double e; + + public PathfinderGoalRandomStroll(EntityCreature entitycreature, double d0) { + this.a = entitycreature; + this.e = d0; + this.a(1); + } + + public boolean a() { + // MineHQ start - disable RandomStroll AI + //if (this.a.aN() >= 100) { + // return false; + //} else if (this.a.aI().nextInt(120) != 0) { + // return false; + //} else { + // Vec3D vec3d = RandomPositionGenerator.a(this.a, 10, 7); + // + // if (vec3d == null) { + // return false; + // } else { + // this.b = vec3d.a; + // this.c = vec3d.b; + // this.d = vec3d.c; + // return true; + // } + //} + return false; + // MineHQ end + } + + public boolean b() { + return !this.a.getNavigation().g(); + } + + public void c() { + this.a.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.RANDOMSTROLL, this.b, this.c, this.d, this.e); // Poweruser + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSelector.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSelector.java new file mode 100644 index 0000000..33aded0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSelector.java @@ -0,0 +1,153 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.bukkit.craftbukkit.util.UnsafeList; // CraftBukkit + +public class PathfinderGoalSelector { + + private static final Logger a = LogManager.getLogger(); + // CraftBukkit start - ArrayList -> UnsafeList + private List b = new UnsafeList(); + private List c = new UnsafeList(); + // CraftBukkit end + private final MethodProfiler d; + private int e; + private int f = 3; + + public PathfinderGoalSelector(MethodProfiler methodprofiler) { + this.d = methodprofiler; + } + + public void a(int i, PathfinderGoal pathfindergoal) { + this.b.add(new PathfinderGoalSelectorItem(this, i, pathfindergoal)); + } + + public void a(PathfinderGoal pathfindergoal) { + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + PathfinderGoalSelectorItem pathfindergoalselectoritem = (PathfinderGoalSelectorItem) iterator.next(); + PathfinderGoal pathfindergoal1 = pathfindergoalselectoritem.a; + + if (pathfindergoal1 == pathfindergoal) { + if (this.c.contains(pathfindergoalselectoritem)) { + pathfindergoal1.d(); + this.c.remove(pathfindergoalselectoritem); + } + + iterator.remove(); + } + } + } + + public void a() { + // ArrayList arraylist = new ArrayList(); // CraftBukkit - remove usage + Iterator iterator; + PathfinderGoalSelectorItem pathfindergoalselectoritem; + + if (this.e++ % this.f == 0) { + iterator = this.b.iterator(); + + while (iterator.hasNext()) { + pathfindergoalselectoritem = (PathfinderGoalSelectorItem) iterator.next(); + boolean flag = this.c.contains(pathfindergoalselectoritem); + + if (flag) { + if (this.b(pathfindergoalselectoritem) && this.a(pathfindergoalselectoritem)) { + continue; + } + + pathfindergoalselectoritem.a.d(); + this.c.remove(pathfindergoalselectoritem); + } + + if (this.b(pathfindergoalselectoritem) && pathfindergoalselectoritem.a.a()) { + // CraftBukkit start - call method now instead of queueing + // arraylist.add(pathfindergoalselectoritem); + pathfindergoalselectoritem.a.c(); + // CraftBukkit end + this.c.add(pathfindergoalselectoritem); + } + } + } else { + iterator = this.c.iterator(); + + while (iterator.hasNext()) { + pathfindergoalselectoritem = (PathfinderGoalSelectorItem) iterator.next(); + if (!pathfindergoalselectoritem.a.b()) { + pathfindergoalselectoritem.a.d(); + iterator.remove(); + } + } + } + + this.d.a("goalStart"); + // CraftBukkit start - removed usage of arraylist + /*iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + pathfindergoalselectoritem = (PathfinderGoalSelectorItem) iterator.next(); + this.d.a(pathfindergoalselectoritem.a.getClass().getSimpleName()); + pathfindergoalselectoritem.a.c(); + this.d.b(); + }*/ + // CraftBukkit end + + this.d.b(); + this.d.a("goalTick"); + iterator = this.c.iterator(); + + while (iterator.hasNext()) { + pathfindergoalselectoritem = (PathfinderGoalSelectorItem) iterator.next(); + pathfindergoalselectoritem.a.e(); + } + + this.d.b(); + } + + private boolean a(PathfinderGoalSelectorItem pathfindergoalselectoritem) { + this.d.a("canContinue"); + boolean flag = pathfindergoalselectoritem.a.b(); + + this.d.b(); + return flag; + } + + private boolean b(PathfinderGoalSelectorItem pathfindergoalselectoritem) { + this.d.a("canUse"); + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + PathfinderGoalSelectorItem pathfindergoalselectoritem1 = (PathfinderGoalSelectorItem) iterator.next(); + + if (pathfindergoalselectoritem1 != pathfindergoalselectoritem) { + if (pathfindergoalselectoritem.b >= pathfindergoalselectoritem1.b) { + // CraftBukkit - switch order + if (!this.a(pathfindergoalselectoritem, pathfindergoalselectoritem1) && this.c.contains(pathfindergoalselectoritem1)) { + this.d.b(); + ((UnsafeList.Itr) iterator).valid = false; // CraftBukkit - mark iterator for reuse + return false; + } + // CraftBukkit - switch order + } else if (!pathfindergoalselectoritem1.a.i() && this.c.contains(pathfindergoalselectoritem1)) { + this.d.b(); + ((UnsafeList.Itr) iterator).valid = false; // CraftBukkit - mark iterator for reuse + return false; + } + } + } + + this.d.b(); + return true; + } + + private boolean a(PathfinderGoalSelectorItem pathfindergoalselectoritem, PathfinderGoalSelectorItem pathfindergoalselectoritem1) { + return (pathfindergoalselectoritem.a.j() & pathfindergoalselectoritem1.a.j()) == 0; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSit.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSit.java new file mode 100644 index 0000000..60371be --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSit.java @@ -0,0 +1,39 @@ +package net.minecraft.server; + +public class PathfinderGoalSit extends PathfinderGoal { + + private EntityTameableAnimal entity; + private boolean willSit; + + public PathfinderGoalSit(EntityTameableAnimal entitytameableanimal) { + this.entity = entitytameableanimal; + this.a(5); + } + + public boolean a() { + if (!this.entity.isTamed()) { + return this.willSit && this.entity.getGoalTarget() == null; // CraftBukkit - Allow sitting for wild animals + } else if (this.entity.M()) { + return false; + } else if (!this.entity.onGround) { + return false; + } else { + EntityLiving entityliving = this.entity.getOwner(); + + return entityliving == null ? true : (this.entity.f(entityliving) < 144.0D && entityliving.getLastDamager() != null ? false : this.willSit); + } + } + + public void c() { + this.entity.getNavigation().h(); + this.entity.setSitting(true); + } + + public void d() { + this.entity.setSitting(false); + } + + public void setSitting(boolean flag) { + this.willSit = flag; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSwell.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSwell.java new file mode 100644 index 0000000..9a31d82 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalSwell.java @@ -0,0 +1,36 @@ +package net.minecraft.server; + +public class PathfinderGoalSwell extends PathfinderGoal { + + EntityCreeper a; + + public PathfinderGoalSwell(EntityCreeper entitycreeper) { + this.a = entitycreeper; + this.a(1); + } + + public boolean a() { + EntityLiving entityliving = this.a.getGoalTarget(); + + return this.a.cb() > 0 || entityliving != null && this.a.f(entityliving) < 9.0D; + } + + public void c() { + this.a.getNavigation().h(); + } + + public void d() {} + + public void e() { + EntityLiving b = this.a.getGoalTarget(); // Spigot Update - 20140921a + if (b == null) { + this.a.a(-1); + } else if (this.a.f(b) > 49.0D) { + this.a.a(-1); + } else if (!this.a.getEntitySenses().canSee(b)) { + this.a.a(-1); + } else { + this.a.a(1); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalTame.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalTame.java new file mode 100644 index 0000000..bfed1dd --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalTame.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +public class PathfinderGoalTame extends PathfinderGoal { + + private EntityHorse entity; + private double b; + private double c; + private double d; + private double e; + + public PathfinderGoalTame(EntityHorse entityhorse, double d0) { + this.entity = entityhorse; + this.b = d0; + this.a(1); + } + + public boolean a() { + if (!this.entity.isTame() && this.entity.passenger != null) { + Vec3D vec3d = RandomPositionGenerator.a(this.entity, 5, 4); + + if (vec3d == null) { + return false; + } else { + this.c = vec3d.a; + this.d = vec3d.b; + this.e = vec3d.c; + return true; + } + } else { + return false; + } + } + + public void c() { + this.entity.getNavigation().a(net.valorhcf.pathsearch.PositionPathSearchType.TAME, this.c, this.d, this.e, this.b); // Poweruser + } + + public boolean b() { + return !this.entity.getNavigation().g() && this.entity.passenger != null; + } + + public void e() { + if (this.entity.aI().nextInt(50) == 0) { + if (this.entity.passenger instanceof EntityHuman) { + int i = this.entity.getTemper(); + int j = this.entity.getMaxDomestication(); + + // CraftBukkit - fire EntityTameEvent + if (j > 0 && this.entity.aI().nextInt(j) < i && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.entity, (EntityHuman) this.entity.passenger).isCancelled() && this.entity.passenger instanceof EntityHuman) { + this.entity.h((EntityHuman) this.entity.passenger); + this.entity.world.broadcastEntityEffect(this.entity, (byte) 7); + return; + } + + this.entity.v(5); + } + + // CraftBukkit start - Handle dismounting to account for VehicleExitEvent being fired. + if (this.entity.passenger != null) { + this.entity.passenger.mount((Entity) null); + // If the entity still has a passenger, then a plugin cancelled the event. + if (this.entity.passenger != null) { + return; + } + } + // this.entity.passenger = null; + // CraftBukkit end + this.entity.cJ(); + this.entity.world.broadcastEntityEffect(this.entity, (byte) 6); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalTarget.java b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalTarget.java new file mode 100644 index 0000000..761fe6d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PathfinderGoalTarget.java @@ -0,0 +1,165 @@ +package net.minecraft.server; + +import net.minecraft.util.org.apache.commons.lang3.StringUtils; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.event.entity.EntityTargetEvent; +// CraftBukkit end + +public abstract class PathfinderGoalTarget extends PathfinderGoal { + + protected EntityCreature c; + protected boolean d; + private boolean a; + private int b; + private int e; + private int f; + + public PathfinderGoalTarget(EntityCreature entitycreature, boolean flag) { + this(entitycreature, flag, false); + } + + public PathfinderGoalTarget(EntityCreature entitycreature, boolean flag, boolean flag1) { + this.c = entitycreature; + this.d = flag; + this.a = flag1; + } + + public boolean b() { + EntityLiving entityliving = this.c.getGoalTarget(); + + if (entityliving == null) { + return false; + } else if (!entityliving.isAlive()) { + return false; + } else { + double d0 = this.f(); + + if (this.c.f(entityliving) > d0 * d0) { + return false; + } else { + if (this.d) { + if (this.c.getEntitySenses().canSee(entityliving)) { + this.f = 0; + } else if (++this.f > 60) { + return false; + } + } + + return !(entityliving instanceof EntityPlayer) || !((EntityPlayer) entityliving).playerInteractManager.isCreative(); + } + } + } + + protected double f() { + AttributeInstance attributeinstance = this.c.getAttributeInstance(GenericAttributes.b); + + return attributeinstance == null ? 16.0D : attributeinstance.getValue(); + } + + public void c() { + this.b = 0; + this.e = 0; + this.f = 0; + } + + public void d() { + this.c.setGoalTarget((EntityLiving) null); + } + + protected boolean a(EntityLiving entityliving, boolean flag) { + if (entityliving == null) { + return false; + } else if (entityliving == this.c) { + return false; + } else if (!entityliving.isAlive()) { + return false; + } else if (!this.c.a(entityliving.getClass())) { + return false; + } else { + if (this.c instanceof EntityOwnable && StringUtils.isNotEmpty(((EntityOwnable) this.c).getOwnerUUID())) { + if (entityliving instanceof EntityOwnable && ((EntityOwnable) this.c).getOwnerUUID().equals(((EntityOwnable) entityliving).getOwnerUUID())) { + return false; + } + + if (entityliving == ((EntityOwnable) this.c).getOwner()) { + return false; + } + } else if (entityliving instanceof EntityHuman && !flag && ((EntityHuman) entityliving).abilities.isInvulnerable) { + return false; + } + + if (!this.c.b(MathHelper.floor(entityliving.locX), MathHelper.floor(entityliving.locY), MathHelper.floor(entityliving.locZ))) { + return false; + } else if (this.d && !this.c.getEntitySenses().canSee(entityliving)) { + return false; + } else { + if (this.a) { + if (--this.e <= 0) { + this.b = 0; + } + + if (this.b == 0) { + this.b = this.a(entityliving) ? 1 : 2; + } + + if (this.b == 2) { + return false; + } + } + + // CraftBukkit start - Check all the different target goals for the reason, default to RANDOM_TARGET + EntityTargetEvent.TargetReason reason = EntityTargetEvent.TargetReason.RANDOM_TARGET; + + if (this instanceof PathfinderGoalDefendVillage) { + reason = EntityTargetEvent.TargetReason.DEFEND_VILLAGE; + } else if (this instanceof PathfinderGoalHurtByTarget) { + reason = EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY; + } else if (this instanceof PathfinderGoalNearestAttackableTarget) { + if (entityliving instanceof EntityHuman) { + reason = EntityTargetEvent.TargetReason.CLOSEST_PLAYER; + } + } else if (this instanceof PathfinderGoalOwnerHurtByTarget) { + reason = EntityTargetEvent.TargetReason.TARGET_ATTACKED_OWNER; + } else if (this instanceof PathfinderGoalOwnerHurtTarget) { + reason = EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET; + } + + org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this.c, entityliving, reason); + if (event.isCancelled() || event.getTarget() == null) { + this.c.setGoalTarget(null); + return false; + } else if (entityliving.getBukkitEntity() != event.getTarget()) { + this.c.setGoalTarget((EntityLiving) ((CraftEntity) event.getTarget()).getHandle()); + } + if (this.c instanceof EntityCreature) { + ((EntityCreature) this.c).target = ((CraftEntity) event.getTarget()).getHandle(); + } + // CraftBukkit end + + return true; + } + } + } + + private boolean a(EntityLiving entityliving) { + this.e = 10 + this.c.aI().nextInt(5); + PathEntity pathentity = this.c.getNavigation().a(entityliving); + + if (pathentity == null) { + return false; + } else { + PathPoint pathpoint = pathentity.c(); + + if (pathpoint == null) { + return false; + } else { + int i = pathpoint.a - MathHelper.floor(entityliving.locX); + int j = pathpoint.c - MathHelper.floor(entityliving.locZ); + + return (double) (i * i + j * j) <= 2.25D; + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PersistentCollection.java b/vspigot-server/src/main/java/net/minecraft/server/PersistentCollection.java new file mode 100644 index 0000000..ec79285 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PersistentCollection.java @@ -0,0 +1,190 @@ +package net.minecraft.server; + +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.valorhcf.ThreadingManager; // Poweruser + +public class PersistentCollection { + + private IDataManager a; + private Map b = new HashMap(); + public List c = new ArrayList(); // Spigot + private Map d = new HashMap(); + + public PersistentCollection(IDataManager idatamanager) { + this.a = idatamanager; + this.b(); + } + + public PersistentBase get(Class oclass, String s) { + PersistentBase persistentbase = (PersistentBase) this.b.get(s); + + if (persistentbase != null) { + return persistentbase; + } else { + if (this.a != null) { + try { + File file1 = this.a.getDataFile(s); + + if (file1 != null && file1.exists()) { + try { + persistentbase = (PersistentBase) oclass.getConstructor(new Class[] { String.class}).newInstance(new Object[] { s}); + } catch (Exception exception) { + throw new RuntimeException("Failed to instantiate " + oclass.toString(), exception); + } + + FileInputStream fileinputstream = new FileInputStream(file1); + NBTTagCompound nbttagcompound = NBTCompressedStreamTools.a((InputStream) fileinputstream); + + fileinputstream.close(); + persistentbase.a(nbttagcompound.getCompound("data")); + } + } catch (Exception exception1) { + exception1.printStackTrace(); + } + } + + if (persistentbase != null) { + this.b.put(s, persistentbase); + this.c.add(persistentbase); + } + + return persistentbase; + } + } + + public void a(String s, PersistentBase persistentbase) { + if (persistentbase == null) { + throw new RuntimeException("Can\'t set null data"); + } else { + if (this.b.containsKey(s)) { + this.c.remove(this.b.remove(s)); + } + + this.b.put(s, persistentbase); + this.c.add(persistentbase); + } + } + + public void a() { + for (int i = 0; i < this.c.size(); ++i) { + PersistentBase persistentbase = (PersistentBase) this.c.get(i); + + if (persistentbase.d()) { + this.a(persistentbase); + persistentbase.a(false); + } + } + } + + private void a(PersistentBase persistentbase) { + if (this.a != null) { + try { + File file1 = this.a.getDataFile(persistentbase.id); + + if (file1 != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + persistentbase.b(nbttagcompound); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.set("data", nbttagcompound); + /* Poweruser start + FileOutputStream fileoutputstream = new FileOutputStream(file1); + + NBTCompressedStreamTools.a(nbttagcompound1, (OutputStream) fileoutputstream); + fileoutputstream.close(); + */ + ThreadingManager.saveNBTFileStatic((NBTTagCompound) nbttagcompound1.clone(), file1); + // Poweruser end + } + } catch (Exception exception) { + exception.printStackTrace(); + } + } + } + + private void b() { + try { + this.d.clear(); + if (this.a == null) { + return; + } + + File file1 = this.a.getDataFile("idcounts"); + + if (file1 != null && file1.exists()) { + DataInputStream datainputstream = new DataInputStream(new FileInputStream(file1)); + NBTTagCompound nbttagcompound = NBTCompressedStreamTools.a(datainputstream); + + datainputstream.close(); + Iterator iterator = nbttagcompound.c().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + NBTBase nbtbase = nbttagcompound.get(s); + + if (nbtbase instanceof NBTTagShort) { + NBTTagShort nbttagshort = (NBTTagShort) nbtbase; + short short1 = nbttagshort.e(); + + this.d.put(s, Short.valueOf(short1)); + } + } + } + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + public int a(String s) { + Short oshort = (Short) this.d.get(s); + + if (oshort == null) { + oshort = Short.valueOf((short) 0); + } else { + oshort = Short.valueOf((short) (oshort.shortValue() + 1)); + } + + this.d.put(s, oshort); + if (this.a == null) { + return oshort.shortValue(); + } else { + try { + File file1 = this.a.getDataFile("idcounts"); + + if (file1 != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + Iterator iterator = this.d.keySet().iterator(); + + while (iterator.hasNext()) { + String s1 = (String) iterator.next(); + short short1 = ((Short) this.d.get(s1)).shortValue(); + + nbttagcompound.setShort(s1, short1); + } + + DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(file1)); + + NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream); + dataoutputstream.close(); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + + return oshort.shortValue(); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerAbilities.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerAbilities.java new file mode 100644 index 0000000..88718ce --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerAbilities.java @@ -0,0 +1,54 @@ +package net.minecraft.server; + +public class PlayerAbilities { + + public boolean isInvulnerable; + public boolean isFlying; + public boolean canFly; + public boolean canInstantlyBuild; + public boolean mayBuild = true; + public float flySpeed = 0.05F; // CraftBukkit private -> public + public float walkSpeed = 0.1F; // CraftBukkit private -> public + + public PlayerAbilities() {} + + public void a(NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setBoolean("invulnerable", this.isInvulnerable); + nbttagcompound1.setBoolean("flying", this.isFlying); + nbttagcompound1.setBoolean("mayfly", this.canFly); + nbttagcompound1.setBoolean("instabuild", this.canInstantlyBuild); + nbttagcompound1.setBoolean("mayBuild", this.mayBuild); + nbttagcompound1.setFloat("flySpeed", this.flySpeed); + nbttagcompound1.setFloat("walkSpeed", this.walkSpeed); + nbttagcompound.set("abilities", nbttagcompound1); + } + + public void b(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("abilities", 10)) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("abilities"); + + this.isInvulnerable = nbttagcompound1.getBoolean("invulnerable"); + this.isFlying = nbttagcompound1.getBoolean("flying"); + this.canFly = nbttagcompound1.getBoolean("mayfly"); + this.canInstantlyBuild = nbttagcompound1.getBoolean("instabuild"); + if (nbttagcompound1.hasKeyOfType("flySpeed", 99)) { + this.flySpeed = nbttagcompound1.getFloat("flySpeed"); + this.walkSpeed = nbttagcompound1.getFloat("walkSpeed"); + } + + if (nbttagcompound1.hasKeyOfType("mayBuild", 1)) { + this.mayBuild = nbttagcompound1.getBoolean("mayBuild"); + } + } + } + + public float a() { + return this.flySpeed; + } + + public float b() { + return this.walkSpeed; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerChunk.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerChunk.java new file mode 100644 index 0000000..fa704d8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerChunk.java @@ -0,0 +1,283 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; +import java.util.HashMap; +import java.util.Set; +// CraftBukkit end + +class PlayerChunk { + + private final List b; + private final ChunkCoordIntPair location; + private short[] dirtyBlocks; + private int dirtyCount; + private int f; + private long g; + final PlayerChunkMap playerChunkMap; + // CraftBukkit start - add fields + private final HashMap players = new HashMap(); + private boolean loaded = false; + private Runnable loadedRunnable = new Runnable() { + public void run() { + PlayerChunk.this.loaded = true; + } + }; + // CraftBukkit end + + public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) { + this.playerChunkMap = playerchunkmap; + this.b = new ArrayList(); + this.dirtyBlocks = new short[64]; + this.location = new ChunkCoordIntPair(i, j); + playerchunkmap.a().chunkProviderServer.getChunkAt(i, j, this.loadedRunnable); // CraftBukkit + } + + public void a(final EntityPlayer entityplayer) { // CraftBukkit - added final to argument + if (this.b.contains(entityplayer)) { + PlayerChunkMap.c().debug("Failed to add player. {} already is in chunk {}, {}", new Object[] { entityplayer, Integer.valueOf(this.location.x), Integer.valueOf(this.location.z)}); + } else { + if (this.b.isEmpty()) { + this.g = PlayerChunkMap.a(this.playerChunkMap).getTime(); + } + + this.b.add(entityplayer); + // CraftBukkit start - use async chunk io + Runnable playerRunnable; + if (this.loaded) { + playerRunnable = null; + entityplayer.chunkCoordIntPairQueue.add(this.location); + } else { + playerRunnable = new Runnable() { + public void run() { + entityplayer.chunkCoordIntPairQueue.add(PlayerChunk.this.location); + } + }; + this.playerChunkMap.a().chunkProviderServer.getChunkAt(this.location.x, this.location.z, playerRunnable); + } + + this.players.put(entityplayer, playerRunnable); + // CraftBukkit end + } + } + + public void b(EntityPlayer entityplayer) { + if (this.b.contains(entityplayer)) { + // CraftBukkit start - If we haven't loaded yet don't load the chunk just so we can clean it up + if (!this.loaded) { + ChunkIOExecutor.dropQueuedChunkLoad(this.playerChunkMap.a(), this.location.x, this.location.z, this.players.get(entityplayer)); + this.b.remove(entityplayer); + this.players.remove(entityplayer); + + if (this.b.isEmpty()) { + ChunkIOExecutor.dropQueuedChunkLoad(this.playerChunkMap.a(), this.location.x, this.location.z, this.loadedRunnable); + long i = (long) this.location.x + 2147483647L | (long) this.location.z + 2147483647L << 32; + PlayerChunkMap.b(this.playerChunkMap).remove(i); + // PlayerChunkMap.c(this.playerChunkMap).remove(this); Kohi + } + + return; + } + // CraftBukkit end + + Chunk chunk = PlayerChunkMap.a(this.playerChunkMap).getChunkAt(this.location.x, this.location.z); + + if (chunk.isReady()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(chunk, true, 0, entityplayer.playerConnection.networkManager.getVersion())); // Spigot - protocol patch + } + + this.players.remove(entityplayer); // CraftBukkit + this.b.remove(entityplayer); + entityplayer.chunkCoordIntPairQueue.remove(this.location); + if (this.b.isEmpty()) { + long i = (long) this.location.x + 2147483647L | (long) this.location.z + 2147483647L << 32; + + this.a(chunk); + PlayerChunkMap.b(this.playerChunkMap).remove(i); + // PlayerChunkMap.c(this.playerChunkMap).remove(this); Kohi + if (this.dirtyCount > 0) { + PlayerChunkMap.d(this.playerChunkMap).remove(this); + } + + this.playerChunkMap.a().chunkProviderServer.queueUnload(this.location.x, this.location.z); + } + } + } + + public void a() { + this.a(PlayerChunkMap.a(this.playerChunkMap).getChunkAt(this.location.x, this.location.z)); + } + + private void a(Chunk chunk) { + chunk.s += PlayerChunkMap.a(this.playerChunkMap).getTime() - this.g; + this.g = PlayerChunkMap.a(this.playerChunkMap).getTime(); + } + + public void a(int i, int j, int k) { + if (this.dirtyCount == 0) { + PlayerChunkMap.d(this.playerChunkMap).add(this); + } + + this.f |= 1 << (j >> 4); + if (this.dirtyCount < 64) { + short short1 = (short) (i << 12 | k << 8 | j); + + for (int l = 0; l < this.dirtyCount; ++l) { + if (this.dirtyBlocks[l] == short1) { + return; + } + } + + this.dirtyBlocks[this.dirtyCount++] = short1; + } + } + + public void sendAll(Packet packet) { + for (int i = 0; i < this.b.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) this.b.get(i); + + if (!entityplayer.chunkCoordIntPairQueue.contains(this.location)) { + entityplayer.playerConnection.sendPacket(packet); + } + } + } + + public void b() { + if (this.dirtyCount != 0) { + int i; + int j; + int k; + + // MineHQ start - if we send any block changes to clients on the edge of their render, add a "padding" + // chunk to stop them freezing + if (this.dirtyCount < 64) { + Set affectedChunks = null; + for (i = 0; i < this.dirtyCount; i++) { + int x = this.dirtyBlocks[i] >> 12 & 15; + int z = this.dirtyBlocks[i] >> 8 & 15; + if (x == 0) { + if (affectedChunks == null) affectedChunks = new HashSet(); + affectedChunks.add(new ChunkCoordIntPair(this.location.x - 1, this.location.z)); + } else if (x == 15) { + if (affectedChunks == null) affectedChunks = new HashSet(); + affectedChunks.add(new ChunkCoordIntPair(this.location.x + 1, this.location.z)); + } + if (z == 0) { + if (affectedChunks == null) affectedChunks = new HashSet(); + affectedChunks.add(new ChunkCoordIntPair(this.location.x, this.location.z - 1)); + } else if (z == 15) { + if (affectedChunks == null) affectedChunks = new HashSet(); + affectedChunks.add(new ChunkCoordIntPair(this.location.x, this.location.z + 1)); + } + } + if (affectedChunks != null) { + for (ChunkCoordIntPair chunk : affectedChunks) { + for (Object o : this.b) { + EntityPlayer player = (EntityPlayer) o; + // not applicable to 1.8 clients + if (player.playerConnection.networkManager.getVersion() > 5) { + continue; + } + if (player.chunkCoordIntPairQueue.contains(chunk) || player.paddingChunks.contains(chunk)) { + continue; + } + if (!this.playerChunkMap.a(player, chunk.x, chunk.z)) { + player.paddingChunks.add(chunk); + player.playerConnection.sendPacket(PacketPlayOutMapChunkBulk.paddingChunk(this.playerChunkMap.a(), chunk.x, chunk.z)); + } + } + } + } + } + // MineHQ end + + if (this.dirtyCount == 1) { + i = this.location.x * 16 + (this.dirtyBlocks[0] >> 12 & 15); + j = this.dirtyBlocks[0] & 255; + k = this.location.z * 16 + (this.dirtyBlocks[0] >> 8 & 15); + this.sendAll(new PacketPlayOutBlockChange(i, j, k, PlayerChunkMap.a(this.playerChunkMap))); + if (PlayerChunkMap.a(this.playerChunkMap).getType(i, j, k).isTileEntity()) { + this.sendTileEntity(PlayerChunkMap.a(this.playerChunkMap).getTileEntity(i, j, k)); + } + } else { + int l; + + if (this.dirtyCount == 64) { + i = this.location.x * 16; + j = this.location.z * 16; + // Spigot start - protocol patch + //this.sendAll(new PacketPlayOutMapChunk(PlayerChunkMap.a(this.playerChunkMap).getChunkAt(this.location.x, this.location.z), (this.f == 0xFFFF), this.f)); // CraftBukkit - send everything (including biome) if all sections flagged + + Chunk chunk = PlayerChunkMap.a( this.playerChunkMap ).getChunkAt( this.location.x, this.location.z ); + for (int idx = 0; idx < this.b.size(); ++idx) { + EntityPlayer entityplayer = (EntityPlayer) this.b.get(idx); + + if (!entityplayer.chunkCoordIntPairQueue.contains(this.location)) { + entityplayer.playerConnection.sendPacket( + new PacketPlayOutMapChunk( chunk, (this.f == 0xFFFF), this.f, entityplayer.playerConnection.networkManager.getVersion()) + ); + } + } + + // Spigot end - protocol patch + for (k = 0; k < 16; ++k) { + if ((this.f & 1 << k) != 0) { + l = k << 4; + List list = PlayerChunkMap.a(this.playerChunkMap).getTileEntities(i, l, j, i + 16, l + 16, j + 16); + + for (int i1 = 0; i1 < list.size(); ++i1) { + this.sendTileEntity((TileEntity) list.get(i1)); + } + } + } + } else { + this.sendAll(new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, PlayerChunkMap.a(this.playerChunkMap).getChunkAt(this.location.x, this.location.z))); + + for (i = 0; i < this.dirtyCount; ++i) { + j = this.location.x * 16 + (this.dirtyBlocks[i] >> 12 & 15); + k = this.dirtyBlocks[i] & 255; + l = this.location.z * 16 + (this.dirtyBlocks[i] >> 8 & 15); + if (PlayerChunkMap.a(this.playerChunkMap).getType(j, k, l).isTileEntity()) { + this.sendTileEntity(PlayerChunkMap.a(this.playerChunkMap).getTileEntity(j, k, l)); + } + } + } + } + + this.dirtyCount = 0; + this.f = 0; + } + } + + private void sendTileEntity(TileEntity tileentity) { + if (tileentity != null) { + Packet packet = tileentity.getUpdatePacket(); + + if (packet != null) { + this.sendAll(packet); + } + } + } + + static ChunkCoordIntPair a(PlayerChunk playerchunk) { + return playerchunk.location; + } + + static List b(PlayerChunk playerchunk) { + return playerchunk.b; + } + + // MineHQ start - chunk snapshot api + public void resend() { + if (this.dirtyCount == 0) { + PlayerChunkMap.d(this.playerChunkMap).add(this); + } + this.dirtyCount = 64; + this.f = 0xFFFF; + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerChunkMap.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerChunkMap.java new file mode 100644 index 0000000..d11c1a4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +1,425 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import java.util.Collections; +import java.util.Queue; +import java.util.LinkedList; +// CraftBukkit end + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PlayerChunkMap { + + private static final Logger a = LogManager.getLogger(); + private final WorldServer world; + private final List managedPlayers = new ArrayList(); + private final LongHashMap d = new LongHashMap(); + private final List e = new ArrayList(); // Kohi - use arraylist as in vanilla + // private final Queue f = new java.util.concurrent.ConcurrentLinkedQueue(); // Kohi - this is pointless + private int g; + private long h; + private final int[][] i = new int[][] { { 1, 0}, { 0, 1}, { -1, 0}, { 0, -1}}; + private boolean wasNotEmpty; // CraftBukkit - add field + + public PlayerChunkMap(WorldServer worldserver, int viewDistance /* Spigot */) { + this.world = worldserver; + this.a(viewDistance); // Spigot + } + + public WorldServer a() { + return this.world; + } + + public void flush() { + long i = this.world.getTime(); + int j; + + /* Kohi - removed PlayerChunkMap.f + if (i - this.h > 8000L) { + this.h = i; + + // CraftBukkit start - Use iterator + java.util.Iterator iterator = this.f.iterator(); + while (iterator.hasNext()) { + playerchunk = (PlayerChunk) iterator.next(); + playerchunk.b(); + playerchunk.a(); + } + } else { + java.util.Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + playerchunk = (PlayerChunk) iterator.next(); + playerchunk.b(); + iterator.remove(); + // CraftBukkit end + } + } + */ + + // Kohi - we changed this back to arraylist + for (Object o : this.e) { + PlayerChunk playerchunk = (PlayerChunk) o; + playerchunk.b(); + } + + this.e.clear(); + // MineHQ start - chunk GC handles this + /* + if (this.managedPlayers.isEmpty()) { + if (!wasNotEmpty) return; // CraftBukkit - Only do unload when we go from non-empty to empty + WorldProvider worldprovider = this.world.worldProvider; + + if (!worldprovider.e()) { + this.world.chunkProviderServer.b(); + } + // CraftBukkit start + wasNotEmpty = false; + } else { + wasNotEmpty = true; + } + // CraftBukkit end + */ + // MineHQ end + + } + + public boolean a(int i, int j) { + long k = (long) i + 2147483647L | (long) j + 2147483647L << 32; + + return this.d.getEntry(k) != null; + } + + private PlayerChunk a(int i, int j, boolean flag) { + long k = (long) i + 2147483647L | (long) j + 2147483647L << 32; + PlayerChunk playerchunk = (PlayerChunk) this.d.getEntry(k); + + if (playerchunk == null && flag) { + playerchunk = new PlayerChunk(this, i, j); + this.d.put(k, playerchunk); + // this.f.add(playerchunk); Kohi + } + + return playerchunk; + } + // CraftBukkit start - add method + public final boolean isChunkInUse(int x, int z) { + PlayerChunk pi = a(x, z, false); + if (pi != null) { + return (PlayerChunk.b(pi).size() > 0); + } + return false; + } + // CraftBukkit end + + public void flagDirty(int i, int j, int k) { + org.spigotmc.AsyncCatcher.catchOp("PlayerChunkMap.flagDirty"); + int l = i >> 4; + int i1 = k >> 4; + PlayerChunk playerchunk = this.a(l, i1, false); + + if (playerchunk != null) { + playerchunk.a(i & 15, j, k & 15); + } + } + + public void addPlayer(EntityPlayer entityplayer) { + // Poweruser start + int i = MathHelper.floor(entityplayer.locX) >> 4; + int j = MathHelper.floor(entityplayer.locZ) >> 4; + // Poweruser end + + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; + + // CraftBukkit start - Load nearby chunks first + List chunkList = new LinkedList(); + for (int k = i - this.g; k <= i + this.g; ++k) { + for (int l = j - this.g; l <= j + this.g; ++l) { + chunkList.add(new ChunkCoordIntPair(k, l)); + } + } + + Collections.sort(chunkList, new ChunkCoordComparator(entityplayer)); + for (ChunkCoordIntPair pair : chunkList) { + this.a(pair.x, pair.z, true).a(entityplayer); + } + // CraftBukkit end + + this.managedPlayers.add(entityplayer); + this.b(entityplayer); + } + + public void b(EntityPlayer entityplayer) { + ArrayList arraylist = new ArrayList(entityplayer.chunkCoordIntPairQueue); + int i = 0; + int j = this.g; + // Poweruser start + int k = MathHelper.floor(entityplayer.locX) >> 4; + int l = MathHelper.floor(entityplayer.locZ) >> 4; + // Poweruser end + int i1 = 0; + int j1 = 0; + ChunkCoordIntPair chunkcoordintpair = PlayerChunk.a(this.a(k, l, true)); + + entityplayer.chunkCoordIntPairQueue.clear(); + if (arraylist.contains(chunkcoordintpair)) { + entityplayer.chunkCoordIntPairQueue.add(chunkcoordintpair); + } + + int k1; + + for (k1 = 1; k1 <= j * 2; ++k1) { + for (int l1 = 0; l1 < 2; ++l1) { + int[] aint = this.i[i++ % 4]; + + for (int i2 = 0; i2 < k1; ++i2) { + i1 += aint[0]; + j1 += aint[1]; + chunkcoordintpair = PlayerChunk.a(this.a(k + i1, l + j1, true)); + if (arraylist.contains(chunkcoordintpair)) { + entityplayer.chunkCoordIntPairQueue.add(chunkcoordintpair); + } + } + } + } + + i %= 4; + + for (k1 = 0; k1 < j * 2; ++k1) { + i1 += this.i[i][0]; + j1 += this.i[i][1]; + chunkcoordintpair = PlayerChunk.a(this.a(k + i1, l + j1, true)); + if (arraylist.contains(chunkcoordintpair)) { + entityplayer.chunkCoordIntPairQueue.add(chunkcoordintpair); + } + } + } + + public void removePlayer(EntityPlayer entityplayer) { + // Poweruser start + int i = MathHelper.floor(entityplayer.d) >> 4; + int j = MathHelper.floor(entityplayer.e) >> 4; + // Poweruser end + + for (int k = i - this.g; k <= i + this.g; ++k) { + for (int l = j - this.g; l <= j + this.g; ++l) { + PlayerChunk playerchunk = this.a(k, l, false); + + if (playerchunk != null) { + playerchunk.b(entityplayer); + } + } + } + entityplayer.paddingChunks.clear(); // MineHQ + + this.managedPlayers.remove(entityplayer); + } + + private boolean a(int i, int j, int k, int l, int i1) { + int j1 = i - k; + int k1 = j - l; + + return j1 >= -i1 && j1 <= i1 ? k1 >= -i1 && k1 <= i1 : false; + } + + public void movePlayer(EntityPlayer entityplayer) { + // Poweruser start + int i = MathHelper.floor(entityplayer.locX) >> 4; + int j = MathHelper.floor(entityplayer.locZ) >> 4; + // Poweruser end + double d0 = entityplayer.d - entityplayer.locX; + double d1 = entityplayer.e - entityplayer.locZ; + double d2 = d0 * d0 + d1 * d1; + + if (d2 >= 64.0D) { + // Poweruser start + int k = MathHelper.floor(entityplayer.d) >> 4; + int l = MathHelper.floor(entityplayer.e) >> 4; + // Poweruser end + int i1 = this.g; + int j1 = i - k; + int k1 = j - l; + List chunksToLoad = new LinkedList(); // CraftBukkit + + if (j1 != 0 || k1 != 0) { + // MineHQ start - unload padding chunks when players move away from them + Iterator iter = entityplayer.paddingChunks.iterator(); + while (iter.hasNext()) { + ChunkCoordIntPair chunk = iter.next(); + int xDist = chunk.x - k; + int zDist = chunk.z - l; + if (xDist > i1 || zDist > i1 || xDist < -i1 || zDist < -i1) { + entityplayer.playerConnection.sendPacket(PacketPlayOutMapChunk.unload(chunk.x, chunk.z)); + iter.remove(); + } + } + // MineHQ end + for (int l1 = i - i1; l1 <= i + i1; ++l1) { + for (int i2 = j - i1; i2 <= j + i1; ++i2) { + if (!this.a(l1, i2, k, l, i1)) { + chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); // CraftBukkit + } + + if (!this.a(l1 - j1, i2 - k1, i, j, i1)) { + PlayerChunk playerchunk = this.a(l1 - j1, i2 - k1, false); + + if (playerchunk != null) { + playerchunk.b(entityplayer); + } + } + } + } + + this.b(entityplayer); + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; + + // CraftBukkit start - send nearest chunks first + Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer)); + for (ChunkCoordIntPair pair : chunksToLoad) { + this.a(pair.x, pair.z, true).a(entityplayer); + } + + if (j1 > 1 || j1 < -1 || k1 > 1 || k1 < -1) { // Spigot - missed diff + Collections.sort(entityplayer.chunkCoordIntPairQueue, new ChunkCoordComparator(entityplayer)); + } + // CraftBukkit end + } + } + } + + public boolean a(EntityPlayer entityplayer, int i, int j) { + PlayerChunk playerchunk = this.a(i, j, false); + + return playerchunk != null && PlayerChunk.b(playerchunk).contains(entityplayer) && !entityplayer.chunkCoordIntPairQueue.contains(PlayerChunk.a(playerchunk)); + } + + public void a(int i) { + i = MathHelper.a(i, 3, 20); + if (i != this.g) { + int j = i - this.g; + Iterator iterator = this.managedPlayers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + int k = MathHelper.floor(entityplayer.locX) >> 4; + int l = MathHelper.floor(entityplayer.locZ) >> 4; + int i1; + int j1; + + if (j > 0) { + for (i1 = k - i; i1 <= k + i; ++i1) { + for (j1 = l - i; j1 <= l + i; ++j1) { + PlayerChunk playerchunk = this.a(i1, j1, true); + + if (!PlayerChunk.b(playerchunk).contains(entityplayer)) { + playerchunk.a(entityplayer); + } + } + } + } else { + for (i1 = k - this.g; i1 <= k + this.g; ++i1) { + for (j1 = l - this.g; j1 <= l + this.g; ++j1) { + if (!this.a(i1, j1, k, l, i)) { + this.a(i1, j1, true).b(entityplayer); + } + } + } + } + } + + this.g = i; + } + } + + public static int getFurthestViewableBlock(int i) { + return i * 16 - 16; + } + + static Logger c() { + return a; + } + + static WorldServer a(PlayerChunkMap playerchunkmap) { + return playerchunkmap.world; + } + + static LongHashMap b(PlayerChunkMap playerchunkmap) { + return playerchunkmap.d; + } + + /* Kohi + static Queue c(PlayerChunkMap playermanager) { // CraftBukkit List -> Queue + return playermanager.f; + } + */ + + static List d(PlayerChunkMap playermanager) { // Kohi - List + return playermanager.e; + } + + // CraftBukkit start - Sorter to load nearby chunks first + private static class ChunkCoordComparator implements java.util.Comparator { + private int x; + private int z; + + public ChunkCoordComparator (EntityPlayer entityplayer) { + // Poweruser start + x = MathHelper.floor(entityplayer.locX) >> 4; + z = MathHelper.floor(entityplayer.locZ) >> 4; + // Poweruser end + } + + public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) { + if (a.equals(b)) { + return 0; + } + + // Subtract current position to set center point + int ax = a.x - this.x; + int az = a.z - this.z; + int bx = b.x - this.x; + int bz = b.z - this.z; + + int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz)); + if (result != 0) { + return result; + } + + if (ax < 0) { + if (bx < 0) { + return bz - az; + } else { + return -1; + } + } else { + if (bx < 0) { + return 1; + } else { + return az - bz; + } + } + } + } + // CraftBukkit end + + public int getWorldViewDistance() { + return this.g; + } + + // MineHQ start - chunk snapshot api + public void resend(int chunkX, int chunkZ) { + PlayerChunk playerchunk = this.a(chunkX, chunkZ, false); + + if (playerchunk != null) { + playerchunk.resend(); + } + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerConnection.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerConnection.java new file mode 100644 index 0000000..ac3fdaa --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +1,2272 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.google.common.collect.Lists; +import net.minecraft.util.io.netty.buffer.Unpooled; +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; +import net.minecraft.util.org.apache.commons.lang3.StringUtils; +import net.valorhcf.ValorSpigot; +import net.valorhcf.handler.MovementHandler; +import net.valorhcf.handler.PacketHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.SpigotTimings; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.LazyPlayerSet; +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.inventory.*; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.event.player.*; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.util.NumberConversions; +import org.github.paperspigot.PaperSpigotConfig; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +// CraftBukkit start +// CraftBukkit end +// Guardian start +// Guardian end + +public class PlayerConnection implements PacketPlayInListener { + + private static final Logger c = LogManager.getLogger(); + public final NetworkManager networkManager; + private final MinecraftServer minecraftServer; + public EntityPlayer player; + private int e; + private int f; + private boolean g; + private int h; + private long i; + private static Random j = new Random(); + private long k; + private volatile int chatThrottle; + private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle"); // CraftBukkit - multithreaded field + private int x; + private IntHashMap n = new IntHashMap(); + private double y; + private double z; + private double q; + public boolean checkMovement = true; // CraftBukkit - private -> public + private boolean processedDisconnect; // CraftBukkit - added + + // Guardian start + public int packetsNotReceived = 0; + public long lastKeepAlivePacketReceived = -1; + + public LinkedList lastPacketsQueue = new LinkedList<>(); + + public Set keepAlives = new HashSet<>(); + + public long lastMotionTick = 0L; + public int antiKBViolations = 0; + + public long lastKAPacketTick = MinecraftServer.currentTick; + public long lastKAMovementPacket = MinecraftServer.currentTick; + public long lastNotificationTick = MinecraftServer.currentTick; + public long lastAttackPlayerTime = 0L; + + public boolean isDigging = false; + public int digHorizontalMovement = 0; + + public final List velocitiesSent = new ArrayList(); + public final List velocitySentTimes = new ArrayList(); + public long positionSentTime = System.currentTimeMillis(); + + private long lastKickedForFly; + + + private double nextExpectedYDelta = 0.0D; + public int lastVelocitySentTick = Integer.MIN_VALUE; + + private int gGoodTicks = 0; + + long lastDismounted; + + private int flyModuleGAmount; + private int fastFallModuleGAmount; + // Guardian end + + // AGC start + private int legalMovements = 0; + private int illegalMovements = 0; + private int suspiciousHeightMovements = 0; + // AGC end + + // Alfie start + private static Set glitchBlocks = Sets.newHashSet(Block.getById(13), + Block.getById(94), + Block.getById(145), + Block.getById(54), + Block.getById(146), + Block.getById(44), + Block.getById(154), + Block.getById(88), + Block.getById(78), + Block.getById(126) + ); + // Alfie end + + public PlayerConnection(MinecraftServer minecraftserver, NetworkManager networkmanager, EntityPlayer entityplayer) { + this.minecraftServer = minecraftserver; + this.networkManager = networkmanager; + networkmanager.a((PacketListener) this); + this.player = entityplayer; + entityplayer.playerConnection = this; + + // CraftBukkit start - add fields and methods + this.server = minecraftserver.server; + } + + private final org.bukkit.craftbukkit.CraftServer server; + private int lastTick = MinecraftServer.currentTick; + private int lastDropTick = MinecraftServer.currentTick; + private int dropCount = 0; + private static final int SURVIVAL_PLACE_DISTANCE_SQUARED = 6 * 6; + private static final int CREATIVE_PLACE_DISTANCE_SQUARED = 7 * 7; + + // Get position of last block hit for BlockDamageLevel.STOPPED + private double lastPosX = Double.MAX_VALUE; + private double lastPosY = Double.MAX_VALUE; + private double lastPosZ = Double.MAX_VALUE; + private float lastPitch = Float.MAX_VALUE; + private float lastYaw = Float.MAX_VALUE; + private boolean justTeleported = false; + private boolean hasMoved; // Spigot + + // For the PacketPlayOutBlockPlace hack :( + Long lastPacket; + + // Store the last block right clicked and what type it was + private Item lastMaterial; + + public CraftPlayer getPlayer() { + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); + } + + private final static HashSet invalidItems = new HashSet(java.util.Arrays.asList(8, 9, 10, 11, 26, 34, 36, 43, 51, 52, 55, 59, 60, 62, 63, 64, 68, 71, 74, 75, 83, 90, 92, 93, 94, 104, 105, 115, 117, 118, 119, 125, 127, 132, 140, 141, 142, 144)); // TODO: Check after every update. + // CraftBukkit end + + public void a() { + this.g = false; + ++this.e; + this.minecraftServer.methodProfiler.a("keepAlive"); + if ((long) this.e - this.k > 10L) { // Guardian: 40L -> 10L + this.k = (long) this.e; + this.i = this.d(); + this.h = (int) this.i; + + // Guardian start + packetsNotReceived++; + keepAlives.add(h); + + if (keepAlives.size() > 240 && Bukkit.shouldGuardianAct()) { + disconnect("Disconnected due to lag"); + return; + } + // Guardian end + + this.sendPacket(new PacketPlayOutKeepAlive(this.h)); + } + + // Guardian start + if (packetsNotReceived >= 240 && Bukkit.shouldGuardianAct()) { + disconnect("Disconnected due to lag"); + return; + } + // Guardian end + + // CraftBukkit start + for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ; + /* Use thread-safe field access instead + if (this.chatThrottle > 0) { + --this.chatThrottle; + } + */ + // CraftBukkit end + + if (this.x > 0) { + --this.x; + } + + if (this.player.x() > 0L && this.minecraftServer.getIdleTimeout() > 0 && MinecraftServer.ar() - this.player.x() > (long) (this.minecraftServer.getIdleTimeout() * 1000 * 60)) { + this.disconnect("You have been idle for too long!"); + } + } + + public NetworkManager b() { + return this.networkManager; + } + + public void disconnect(String s) { + // CraftBukkit start - fire PlayerKickEvent + String leaveMessage = EnumChatFormat.YELLOW + this.player.getName() + " left the game."; + + PlayerKickEvent event = new PlayerKickEvent(this.server.getPlayer(this.player), s, leaveMessage); + + if (this.server.getServer().isRunning()) { + this.server.getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + // Do not kick the player + return; + } + // Send the possibly modified leave message + s = event.getReason(); + // CraftBukkit end + ChatComponentText chatcomponenttext = new ChatComponentText(s); + + this.networkManager.handle(new PacketPlayOutKickDisconnect(chatcomponenttext), new GenericFutureListener[]{new PlayerConnectionFuture(this, chatcomponenttext)}); + this.a(chatcomponenttext); // CraftBukkit - Process quit immediately + this.networkManager.g(); + } + + public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) { + this.player.a(packetplayinsteervehicle.c(), packetplayinsteervehicle.d(), packetplayinsteervehicle.e(), packetplayinsteervehicle.f()); + } + + public void a(PacketPlayInFlying packetplayinflying) { + // CraftBukkit start - Check for NaN + if (Double.isNaN(packetplayinflying.x) || Double.isNaN(packetplayinflying.y) || Double.isNaN(packetplayinflying.z) || Double.isNaN(packetplayinflying.stance)) { + c.warn(player.getName() + " was caught trying to crash the server with an invalid position."); + getPlayer().kickPlayer("NaN in position (Hacking?)"); //Spigot "Nope" -> Descriptive reason + return; + } + // CraftBukkit end + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + + this.g = true; + if (!this.player.viewingCredits) { + double d0; + + if (!this.checkMovement) { + d0 = packetplayinflying.d() - this.z; + if (packetplayinflying.c() == this.y && d0 * d0 < 0.01D && packetplayinflying.e() == this.q) { + this.checkMovement = true; + } + } + + this.lastKAMovementPacket = MinecraftServer.currentTick; // Guardian + + // CraftBukkit start - fire PlayerMoveEvent + Player player = this.getPlayer(); + // Spigot Start + if (!hasMoved) { + Location curPos = player.getLocation(); + lastPosX = curPos.getX(); + lastPosY = curPos.getY(); + lastPosZ = curPos.getZ(); + lastYaw = curPos.getYaw(); + lastPitch = curPos.getPitch(); + hasMoved = true; + } + // Spigot End + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location. + Location to = player.getLocation().clone(); // Start off the To location as the Players current location. + + // If the packet contains movement information then we update the To location with the correct XYZ. + if (packetplayinflying.hasPos && !(packetplayinflying.hasPos && packetplayinflying.y == -999.0D && packetplayinflying.stance == -999.0D)) { + to.setX(packetplayinflying.x); + to.setY(packetplayinflying.y); + to.setZ(packetplayinflying.z); + } + + // If the packet contains look information then we update the To location with the correct Yaw & Pitch. + if (packetplayinflying.hasLook) { + to.setYaw(packetplayinflying.yaw); + to.setPitch(packetplayinflying.pitch); + } + + // Prevent 40 event-calls for less than a single pixel of movement >.> + double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2); + float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch()); + + if (packetplayinflying.hasPos && delta > 0.0D && this.checkMovement && !this.player.dead) { + for (MovementHandler handler : ValorSpigot.INSTANCE.getMovementHandlers()) { + try { + handler.handleUpdateLocation(player, to, from, packetplayinflying); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + if (packetplayinflying.hasLook && deltaAngle > 0.0F && this.checkMovement && !this.player.dead) { + for (MovementHandler handler : ValorSpigot.INSTANCE.getMovementHandlers()) { + try { + handler.handleUpdateRotation(player, to, from, packetplayinflying); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + // Guardian start + float f4 = 0.0625F; + AxisAlignedBB axisalignedbb = this.player.boundingBox.clone().grow(f4, f4, f4).a(0.0D, -0.55D, 0.0D); + // Guardian end + + if ((delta > 1f / 256 || deltaAngle > 10f) && (this.checkMovement && !this.player.dead)) { + this.lastPosX = to.getX(); + this.lastPosY = to.getY(); + this.lastPosZ = to.getZ(); + this.lastYaw = to.getYaw(); + this.lastPitch = to.getPitch(); + + if (((this.isDigging) && (to.getX() != this.player.locX)) || (to.getZ() != this.player.locZ)) { + this.digHorizontalMovement += 1; + } + + // Skip the first time we do this + if (true) { // Spigot - don't skip any move events + PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); + this.server.getPluginManager().callEvent(event); + + // If the event is cancelled we move the player back to their old location. + if (event.isCancelled()) { + this.player.playerConnection.sendPacket(new PacketPlayOutPosition(from.getX(), from.getY() + 1.6200000047683716D, from.getZ(), from.getYaw(), from.getPitch(), false)); + return; + } + + /* If a Plugin has changed the To destination then we teleport the Player + there to avoid any 'Moved wrongly' or 'Moved too quickly' errors. + We only do this if the Event was not cancelled. */ + if (!to.equals(event.getTo()) && !event.isCancelled()) { + this.player.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.UNKNOWN); + return; + } + + /* Check to see if the Players Location has some how changed during the call of the event. + This can happen due to a plugin teleporting the player instead of using .setTo() */ + if (!from.equals(this.getPlayer().getLocation()) && this.justTeleported) { + this.justTeleported = false; + return; + } + } + } + + if (this.checkMovement && !this.player.dead) { + // CraftBukkit end + double d1; + double d2; + double d3; + + if (this.player.vehicle != null) { + float f = this.player.yaw; + float f1 = this.player.pitch; + + this.player.vehicle.ac(); + d1 = this.player.locX; + d2 = this.player.locY; + d3 = this.player.locZ; + if (packetplayinflying.k()) { + f = packetplayinflying.g(); + f1 = packetplayinflying.h(); + } + + this.player.onGround = packetplayinflying.i(); + this.player.i(); + this.player.V = 0.0F; + this.player.setLocation(d1, d2, d3, f, f1); + if (this.player.vehicle != null) { + this.player.vehicle.ac(); + } + + this.minecraftServer.getPlayerList().d(this.player); + if (this.checkMovement) { + this.y = this.player.locX; + this.z = this.player.locY; + this.q = this.player.locZ; + } + + worldserver.playerJoinedWorld(this.player); + return; + } + + if (this.player.isSleeping()) { + this.player.i(); + this.player.setLocation(this.y, this.z, this.q, this.player.yaw, this.player.pitch); + worldserver.playerJoinedWorld(this.player); + return; + } + + d0 = this.player.locY; + this.y = this.player.locX; + this.z = this.player.locY; + this.q = this.player.locZ; + d1 = this.player.locX; + d2 = this.player.locY; + d3 = this.player.locZ; + float f2 = this.player.yaw; + float f3 = this.player.pitch; + + boolean onGround = this.player.onGround; // Guardian + + if (packetplayinflying.j() && packetplayinflying.d() == -999.0D && packetplayinflying.f() == -999.0D) { + packetplayinflying.a(false); + } + + // has pos + if (packetplayinflying.j()) { + d1 = packetplayinflying.c(); + d2 = packetplayinflying.d(); + d3 = packetplayinflying.e(); + double d4 = packetplayinflying.f() - packetplayinflying.d(); + if (!this.player.isSleeping() && (d4 > 1.65D || d4 < 0.1D)) { + this.disconnect("Illegal stance"); + c.warn(this.player.getName() + " had an illegal stance: " + d4); + return; + } + + if (Math.abs(packetplayinflying.c()) > 3.2E7D || Math.abs(packetplayinflying.e()) > 3.2E7D) { + this.disconnect("Illegal position"); + return; + } + } + + if (packetplayinflying.k()) { + f2 = packetplayinflying.g(); + f3 = packetplayinflying.h(); + } + + this.player.i(); + this.player.V = 0.0F; + this.player.setLocation(this.y, this.z, this.q, f2, f3); + if (!this.checkMovement) { + return; + } + + // Guardian start: Rename for readability + double xDelta = d1 - this.player.locX; + double yDelta = d2 - this.player.locY; + double zDelta = d3 - this.player.locZ; + // Guardian end + // CraftBukkit start - min to max + double d7 = Math.max(Math.abs(xDelta), Math.abs(this.player.motX)); + double d8 = Math.max(Math.abs(yDelta), Math.abs(this.player.motY)); + double d9 = Math.max(Math.abs(zDelta), Math.abs(this.player.motZ)); + // CraftBukkit end + double d10 = d7 * d7 + d8 * d8 + d9 * d9; + + // Spigot: make "moved too quickly" limit configurable + // Poweruser start + if (d10 > org.spigotmc.SpigotConfig.movedTooQuicklyThreshold && (!this.minecraftServer.N() || !this.minecraftServer.M().equals(this.player.getName()))) { // CraftBukkit - Added this.checkMovement condition to solve this check being triggered by teleports + c.warn(this.player.getName() + " moved too quickly! Distance: " + Math.sqrt(d10)); + + // Poweruser end + this.a(this.y, this.z, this.q, this.player.yaw, this.player.pitch); + return; + } + + SpigotTimings.connectionTimer_PacketFlying_move.startTiming(); // Poweruser + + boolean flag = worldserver.getCubes(this.player, this.player.boundingBox.clone().shrink((double) f4, (double) f4, (double) f4)).isEmpty(); + + if (this.player.onGround && !packetplayinflying.i() && yDelta > 0.0D) { + this.player.bj(); + } + + this.player.move(xDelta, yDelta, zDelta); + this.player.onGround = packetplayinflying.i(); + this.player.checkMovement(xDelta, yDelta, zDelta); + double d11 = yDelta; + + xDelta = d1 - this.player.locX; + yDelta = d2 - this.player.locY; + if (yDelta > -0.5D || yDelta < 0.5D) { + yDelta = 0.0D; + } + + zDelta = d3 - this.player.locZ; + d10 = xDelta * xDelta + yDelta * yDelta + zDelta * zDelta; + boolean flag1 = false; + + // Spigot: make "moved wrongly" limit configurable + // Poweruser start + double positionOffset = d10; + + if (this.player.playerInteractManager.isCreative()) { + positionOffset *= 2.0D; + } + + if (positionOffset > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping()) { + c.warn(this.player.getName() + " moved wrongly!"); + flag1 = true; + } + // Poweruser end + + // Poweruser start + double calculatedX = this.player.locX; + double calculatedY = this.player.locY; + double calculatedZ = this.player.locZ; + this.player.setLocation(d1, d2, d3, f2, f3); + boolean flag2 = worldserver.getCubes(this.player, this.player.boundingBox.clone().shrink((double) f4, (double) f4, (double) f4)).isEmpty(); + boolean rayTraceCollision = delta > 0.3 && worldserver.rayTrace(Vec3D.a(this.y, this.z + 1.0, this.q), Vec3D.a(d1, d2 + 1.0, d3), false, true, false) != null; + + this.player.setLocation(calculatedX, calculatedY, calculatedZ, f2, f3); + + SpigotTimings.connectionTimer_PacketFlying_move.stopTiming(); // Poweruser + // Poweruser end + + if (this.player.vehicle != null) { + networkManager.lastVehicleTime = networkManager.currentTime; + } + + // joeleoli start + boolean phasing = !this.player.isSleeping() && flag && !flag2; + + // Poweruser start + if (!phasing || !this.player.allowServerSidePhase) { + if (flag1 || phasing || rayTraceCollision) { + if (!rayTraceCollision && !flag && e % 3 != 0) { + this.player.setPosition(this.y, this.z, this.q); + } else { + this.a(this.y, this.z, this.q, f2, f3); + } + + return; + } + } + // Poweruser end + // joeleoli end + + if (!this.minecraftServer.getAllowFlight() && !this.player.abilities.canFly && !worldserver.c(axisalignedbb)) { // CraftBukkit - check abilities instead of creative mode + if (d11 >= -0.03125D) { + ++this.f; + if (this.f > 80) { + if (Bukkit.shouldGuardianAct() && lastKickedForFly + TimeUnit.MINUTES.toMillis(1) < networkManager.currentTime) { // Guardian - Only kick them if we are currently doing checks, add a delay between kicks + lastKickedForFly = networkManager.currentTime; + + if (!player.isOp()) { // Guardian: Only kick if they're not opped + this.disconnect("Flying is not enabled on this server"); + c.warn(this.player.getName() + " was kicked for flying!"); + } + + // Guardian start + String message = String.format("%s was caught flying (Module V) at %.1f %.1f %.1f", this.player.getName(), this.player.locX, this.player.locY, this.player.locZ); + + Bukkit.getPluginManager().callEvent( + new GuardianEvent(player, GuardianEvent.Cheat.FLY_HACKS, "V", GuardianEvent.DisplayLevel.HIGH, message, new Location(player.getWorld(), this.player.locX, this.player.locY, this.player.locZ)) + ); + // Guardian end + return; + } + } + } + } else { + this.f = 0; + } + + this.player.onGround = packetplayinflying.i(); + SpigotTimings.connectionTimer_PacketFlying_playerChunks.startTiming(); // Poweruser + this.minecraftServer.getPlayerList().d(this.player); + SpigotTimings.connectionTimer_PacketFlying_playerChunks.stopTiming(); // Poweruser + this.player.b(this.player.locY - d0, packetplayinflying.i()); + } else if (this.e % 20 == 0) { + this.a(this.y, this.z, this.q, this.player.yaw, this.player.pitch); + } + } + } + + private AxisAlignedBB getBoundingBoxRounded() { + AxisAlignedBB bb = this.player.boundingBox.clone(); + bb.a = (Math.round(bb.a * 1000000.0D) / 1000000.0D); + bb.b = (Math.round(bb.b * 1000000.0D) / 1000000.0D); + bb.c = (Math.round(bb.c * 1000000.0D) / 1000000.0D); + bb.d = (Math.round(bb.d * 1000000.0D) / 1000000.0D); + bb.e = (Math.round(bb.e * 1000000.0D) / 1000000.0D); + bb.f = (Math.round(bb.f * 1000000.0D) / 1000000.0D); + return bb; + } + + public void a(double d0, double d1, double d2, float f, float f1) { + // CraftBukkit start - Delegate to teleport(Location) + Player player = this.getPlayer(); + Location from = player.getLocation(); + Location to = new Location(this.getPlayer().getWorld(), d0, d1, d2, f, f1); + PlayerTeleportEvent event = new PlayerTeleportEvent(player, from, to, PlayerTeleportEvent.TeleportCause.UNKNOWN); + this.server.getPluginManager().callEvent(event); + + from = event.getFrom(); + to = event.isCancelled() ? from : event.getTo(); + + this.teleport(to); + } + + public void teleport(Location dest) { + double d0, d1, d2; + float f, f1; + + d0 = dest.getX(); + d1 = dest.getY(); + d2 = dest.getZ(); + f = dest.getYaw(); + f1 = dest.getPitch(); + + // TODO: make sure this is the best way to address this. + if (Float.isNaN(f)) { + f = 0; + } + + if (Float.isNaN(f1)) { + f1 = 0; + } + + this.lastPosX = d0; + this.lastPosY = d1; + this.lastPosZ = d2; + this.lastYaw = f; + this.lastPitch = f1; + this.justTeleported = true; + // CraftBukkit end + + this.checkMovement = false; + this.y = d0; + this.z = d1; + this.q = d2; + this.player.setLocation(d0, d1, d2, f, f1); + this.player.playerConnection.sendPacket(new PacketPlayOutPosition(d0, d1 + 1.6200000047683716D, d2, f, f1, false)); + } + + public void a(PacketPlayInBlockDig packetplayinblockdig) { + if (this.player.dead) return; // CraftBukkit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + + // Guardian start + if (!this.player.abilities.canInstantlyBuild) { + if (packetplayinblockdig.g() == 0) { + this.isDigging = true; + this.digHorizontalMovement = 0; + } else if ((packetplayinblockdig.g() == 1) || (packetplayinblockdig.g() == 2)) { + this.isDigging = false; + } + } + // Guardian end + + this.player.v(); + if (packetplayinblockdig.g() == 4) { + // CraftBukkit start - limit how quickly items can be dropped + // If the ticks aren't the same then the count starts from 0 and we update the lastDropTick. + if (this.lastDropTick != MinecraftServer.currentTick) { + this.dropCount = 0; + this.lastDropTick = MinecraftServer.currentTick; + } else { + // Else we increment the drop count and check the amount. + this.dropCount++; + if (this.dropCount >= 20) { + PlayerConnection.c.warn(this.player.getName() + " dropped their items too quickly!"); + this.disconnect("You dropped your items too quickly (Hacking?)"); + return; + } + } + // CraftBukkit end + this.player.a(false); + } else if (packetplayinblockdig.g() == 3) { + this.player.a(true); + } else if (packetplayinblockdig.g() == 5) { + this.player.bA(); + } else { + boolean flag = false; + + if (packetplayinblockdig.g() == 0) { + flag = true; + } + + if (packetplayinblockdig.g() == 1) { + flag = true; + } + + if (packetplayinblockdig.g() == 2) { + flag = true; + } + + int i = packetplayinblockdig.c(); + int j = packetplayinblockdig.d(); + int k = packetplayinblockdig.e(); + + if (flag) { + double d0 = this.player.locX - ((double) i + 0.5D); + double d1 = this.player.locY - ((double) j + 0.5D) + 1.5D; + double d2 = this.player.locZ - ((double) k + 0.5D); + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d3 > 36.0D) { + return; + } + + if (j >= this.minecraftServer.getMaxBuildHeight()) { + return; + } + } + + if (packetplayinblockdig.g() == 0) { + if (!this.minecraftServer.a(worldserver, i, j, k, this.player)) { + this.player.playerInteractManager.dig(i, j, k, packetplayinblockdig.f()); + } else { + // CraftBukkit start - fire PlayerInteractEvent + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, i, j, k, packetplayinblockdig.f(), this.player.inventory.getItemInHand()); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, worldserver)); + // Update any tile entity data for this block + TileEntity tileentity = worldserver.getTileEntity(i, j, k); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + // CraftBukkit end + } + } else if (packetplayinblockdig.g() == 2) { + this.player.playerInteractManager.a(i, j, k); + if (worldserver.getType(i, j, k).getMaterial() != Material.AIR) { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, worldserver)); + } + } else if (packetplayinblockdig.g() == 1) { + this.player.playerInteractManager.c(i, j, k); + if (worldserver.getType(i, j, k).getMaterial() != Material.AIR) { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, worldserver)); + } + } + } + } + + // Spigot start - limit place/interactions + private long lastPlace = -1; + private int packets = 0; + + public void a(PacketPlayInBlockPlace packetplayinblockplace) { + boolean throttled = false; + // PaperSpigot - Allow disabling the player interaction limiter + if (org.github.paperspigot.PaperSpigotConfig.interactLimitEnabled && lastPlace != -1 && packetplayinblockplace.timestamp - lastPlace < 30 && packets++ >= 4) { + throttled = true; + } else if (packetplayinblockplace.timestamp - lastPlace >= 30 || lastPlace == -1) { + lastPlace = packetplayinblockplace.timestamp; + packets = 0; + } + // Spigot end + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + + // CraftBukkit start + if (this.player.dead) return; + + // This is a horrible hack needed because the client sends 2 packets on 'right mouse click' + // aimed at a block. We shouldn't need to get the second packet if the data is handled + // but we cannot know what the client will do, so we might still get it + // + // If the time between packets is small enough, and the 'signature' similar, we discard the + // second one. This sadly has to remain until Mojang makes their packets saner. :( + // -- Grum + if (packetplayinblockplace.getFace() == 255) { + if (packetplayinblockplace.getItemStack() != null && packetplayinblockplace.getItemStack().getItem() == this.lastMaterial && this.lastPacket != null && packetplayinblockplace.timestamp - this.lastPacket < 100) { + this.lastPacket = null; + return; + } + } else { + this.lastMaterial = packetplayinblockplace.getItemStack() == null ? null : packetplayinblockplace.getItemStack().getItem(); + this.lastPacket = packetplayinblockplace.timestamp; + } + // CraftBukkit - if rightclick decremented the item, always send the update packet. */ + // this is not here for CraftBukkit's own functionality; rather it is to fix + // a notch bug where the item doesn't update correctly. + boolean always = false; + // CraftBukkit end + + ItemStack itemstack = this.player.inventory.getItemInHand(); + boolean flag = false; + int i = packetplayinblockplace.c(); + int j = packetplayinblockplace.d(); + int k = packetplayinblockplace.e(); + int l = packetplayinblockplace.getFace(); + + this.player.v(); + boolean isEnderPearl = false; + if (packetplayinblockplace.getFace() == 255 || (isEnderPearl = (!isChest(i, j, k) && itemstack != null && itemstack.getItem() != null && CraftMagicNumbers.getMaterial(itemstack.getItem()) == org.bukkit.Material.ENDER_PEARL))) { + if (itemstack == null) { + return; + } + + // CraftBukkit start + int itemstackAmount = itemstack.count; + // Spigot start - skip the event if throttled + if (!throttled) { + org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack); + if (event.useItemInHand() != Event.Result.DENY) { + this.player.playerInteractManager.useItem(this.player, this.player.world, itemstack); + + if (isEnderPearl) { + flag = true; + } + } + } + // Spigot end + + // CraftBukkit - notch decrements the counter by 1 in the above method with food, + // snowballs and so forth, but he does it in a place that doesn't cause the + // inventory update packet to get sent + always = (itemstack.count != itemstackAmount) || itemstack.getItem() == Item.getItemOf(Blocks.WATER_LILY); + // CraftBukkit end + } else if (packetplayinblockplace.d() >= this.minecraftServer.getMaxBuildHeight() - 1 && (packetplayinblockplace.getFace() == 1 || packetplayinblockplace.d() >= this.minecraftServer.getMaxBuildHeight())) { + ChatMessage chatmessage = new ChatMessage("build.tooHigh", new Object[]{Integer.valueOf(this.minecraftServer.getMaxBuildHeight())}); + + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + this.player.playerConnection.sendPacket(new PacketPlayOutChat(chatmessage)); + flag = true; + } else { + // CraftBukkit start - Check if we can actually do something over this large a distance + Location eyeLoc = this.getPlayer().getEyeLocation(); + double reachDistance = NumberConversions.square(eyeLoc.getX() - i) + NumberConversions.square(eyeLoc.getY() - j) + NumberConversions.square(eyeLoc.getZ() - k); + if (reachDistance > (this.getPlayer().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) { + return; + } + + if (throttled || !this.player.playerInteractManager.interact(this.player, worldserver, itemstack, i, j, k, l, packetplayinblockplace.h(), packetplayinblockplace.i(), packetplayinblockplace.j())) { // Spigot - skip the event if throttled + always = true; // force PacketPlayOutSetSlot to be sent to client to update ItemStack count + } + // CraftBukkit end + + flag = true; + } + + if (flag) { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, worldserver)); + + boolean sendSecondUpdate = true; + if (l == 0) { + --j; + } else if (l == 1) { + ++j; + } else if (l == 2) { + --k; + } else if (l == 3) { + ++k; + } else if (l == 4) { + --i; + } else if (l == 5) { + ++i; + } else { + sendSecondUpdate = false; + } + + if (sendSecondUpdate) { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, worldserver)); + } + } + + itemstack = this.player.inventory.getItemInHand(); + if (itemstack != null && itemstack.count <= 0) { // EMC + this.player.inventory.items[this.player.inventory.itemInHandIndex] = null; + itemstack = null; + } + + if (itemstack == null || itemstack.n() == 0) { + this.player.g = true; + this.player.inventory.items[this.player.inventory.itemInHandIndex] = ItemStack.b(this.player.inventory.items[this.player.inventory.itemInHandIndex]); + Slot slot = this.player.activeContainer.getSlot((IInventory) this.player.inventory, this.player.inventory.itemInHandIndex); + + this.player.activeContainer.b(); + this.player.g = false; + // CraftBukkit - TODO CHECK IF NEEDED -- new if structure might not need 'always'. Kept it in for now, but may be able to remove in future + if (!ItemStack.matches(this.player.inventory.getItemInHand(), packetplayinblockplace.getItemStack()) || always) { + this.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, slot.rawSlotIndex, this.player.inventory.getItemInHand())); + } + } + } + + private boolean isChest(int x, int y, int z) { + org.bukkit.Material bukkitMaterial = CraftMagicNumbers.getMaterial(this.player.world.getType(x, y, z)); + return bukkitMaterial == org.bukkit.Material.CHEST || bukkitMaterial == org.bukkit.Material.TRAPPED_CHEST || bukkitMaterial == org.bukkit.Material.ENDER_CHEST; + } + + public void a(IChatBaseComponent ichatbasecomponent) { + // CraftBukkit start - Rarely it would send a disconnect line twice + if (this.processedDisconnect) { + return; + } else { + this.processedDisconnect = true; + } + // CraftBukkit end + + try { + c.info(this.player.getName() + " lost connection: " + ichatbasecomponent.c()); // CraftBukkit - Don't toString the component + } catch (Exception e) { + c.info(this.player.getName() + " lost connection: I'M NOT TELLING YOU WHY BECAUSE SHIT WOULD BREAK SORRY"); + e.printStackTrace(); + System.out.println(ichatbasecomponent); + } + + this.minecraftServer.az(); + // CraftBukkit start - Replace vanilla quit message handling with our own. + /* + ChatMessage chatmessage = new ChatMessage("multiplayer.player.left", new Object[] { this.player.getScoreboardDisplayName()}); + + chatmessage.getChatModifier().setColor(EnumChatFormat.YELLOW); + this.minecraftServer.getPlayerList().sendMessage(chatmessage); + */ + + this.player.n(); + String quitMessage = this.minecraftServer.getPlayerList().disconnect(this.player); + if ((quitMessage != null) && (quitMessage.length() > 0)) { + this.minecraftServer.getPlayerList().sendMessage(CraftChatMessage.fromString(quitMessage)); + } + // CraftBukkit end + if (this.minecraftServer.N() && this.player.getName().equals(this.minecraftServer.M())) { + c.info("Stopping singleplayer server as player logged out"); + this.minecraftServer.safeShutdown(); + } + } + + public void sendPacket(Packet packet) { + // Spigot start - protocol patch + if (NetworkManager.a(networkManager).attr(NetworkManager.protocolVersion).get() >= 17) { + if (packet instanceof PacketPlayOutWindowItems) { + PacketPlayOutWindowItems items = (PacketPlayOutWindowItems) packet; + if (player.activeContainer instanceof ContainerEnchantTable + && player.activeContainer.windowId == items.a) { + ItemStack[] old = items.b; + items.b = new ItemStack[old.length + 1]; + items.b[0] = old[0]; + System.arraycopy(old, 1, items.b, 2, old.length - 1); + items.b[1] = new ItemStack(Items.INK_SACK, 3, 4); + + } + } else if (packet instanceof PacketPlayOutSetSlot) { + PacketPlayOutSetSlot items = (PacketPlayOutSetSlot) packet; + if (player.activeContainer instanceof ContainerEnchantTable + && player.activeContainer.windowId == items.a) { + if (items.b >= 1) { + items.b++; + } + } + } + } + // Spigot end + if (packet instanceof PacketPlayOutChat) { + PacketPlayOutChat packetplayoutchat = (PacketPlayOutChat) packet; + EnumChatVisibility enumchatvisibility = this.player.getChatFlags(); + + if (enumchatvisibility == EnumChatVisibility.HIDDEN) { + return; + } + + if (enumchatvisibility == EnumChatVisibility.SYSTEM && !packetplayoutchat.d()) { + return; + } + } + + // CraftBukkit start + if (packet == null) { + return; + } + + try { + // Loop through cancellable handlers first + for (PacketHandler handler : ValorSpigot.INSTANCE.getPacketHandlers()) { + try { + if (!handler.handleSentPacketCancellable(this, packet)) { + return; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Loop through normal handlers + for (PacketHandler handler : ValorSpigot.INSTANCE.getPacketHandlers()) { + handler.handleSentPacket(this, packet); + } + + this.networkManager.handle(packet, NetworkManager.emptyListenerArray); // Poweruser + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Sending packet"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Packet being sent"); + + crashreportsystemdetails.a("Packet class", (Callable) (new CrashReportConnectionPacketClass(this, packet))); + throw new ReportedException(crashreport); + } + + if (packet instanceof PacketPlayOutSpawnPosition) { + PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet; + this.player.compassTarget = new Location(this.getPlayer().getWorld(), packet6.x, packet6.y, packet6.z); + } + // CraftBukkit end + + // Guardian start + if (((packet instanceof PacketPlayOutEntityVelocity)) && (((PacketPlayOutEntityVelocity) packet).a == this.player.getId())) { + this.velocitiesSent.add((PacketPlayOutEntityVelocity) packet); + this.velocitySentTimes.add(System.currentTimeMillis()); + this.lastVelocitySentTick = MinecraftServer.currentTick; + } + + if ((packet instanceof PacketPlayOutPosition)) { + this.positionSentTime = System.currentTimeMillis(); + } + // Guardian end + } + + // Guardian start + public void handleKeepAliveSync(PacketPlayInKeepAlive packet) { + this.keepAlives.remove(packet.c()); + + long latency = 1000 + this.keepAlives.size() * 1000; + + Iterator it = this.velocitySentTimes.iterator(); + int i = 0; + while (it.hasNext()) { + long ts = (Long) it.next(); + + if (this.networkManager.currentTime - ts > latency) { + it.remove(); + this.velocitiesSent.remove(i); + } else { + i++; + } + } + + } + // Guardian end + + public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) { + // CraftBukkit start + if (this.player.dead) return; + + if (packetplayinhelditemslot.c() >= 0 && packetplayinhelditemslot.c() < PlayerInventory.getHotbarSize()) { + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getPlayer(), this.player.inventory.itemInHandIndex, packetplayinhelditemslot.c()); + this.server.getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex)); + this.player.v(); + return; + } + // CraftBukkit end + + this.player.inventory.itemInHandIndex = packetplayinhelditemslot.c(); + this.player.v(); + } else { + c.warn(this.player.getName() + " tried to set an invalid carried item"); + this.disconnect("Invalid hotbar selection (Hacking?)"); // CraftBukkit //Spigot "Nope" -> Descriptive reason + } + } + + public void a(PacketPlayInChat packetplayinchat) { + if (this.player.dead || this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales + ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); + + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + this.sendPacket(new PacketPlayOutChat(chatmessage)); + } else { + this.player.v(); + String s = packetplayinchat.c(); + + s = StringUtils.normalizeSpace(s); + + for (int i = 0; i < s.length(); ++i) { + if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) { + // CraftBukkit start - threadsafety + if (packetplayinchat.a()) { + // Poweruser start + if (!this.networkManager.lockDownIncomingTraffic()) { + Runnable runnable = new Runnable() { + @Override + public void run() { + try { + PlayerConnection.this.disconnect("Illegal characters in chat"); + } catch (Exception e) { + c.warn(e.toString()); + } + } + }; + this.minecraftServer.processQueue.add(runnable); + } + // Poweruser end + } else { + this.disconnect("Illegal characters in chat"); + } + // CraftBukkit end + return; + } + } + + // CraftBukkit start + if (!packetplayinchat.a()) { + try { + this.minecraftServer.server.playerCommandState = true; + this.handleCommand(s); + } finally { + this.minecraftServer.server.playerCommandState = false; + } + } else if (s.isEmpty()) { + c.warn(this.player.getName() + " tried to send an empty message"); + } else if (getPlayer().isConversing()) { + // Spigot start + final String message = s; + this.minecraftServer.processQueue.add(new Waitable() { + @Override + protected Object evaluate() { + getPlayer().acceptConversationInput(message); + return null; + } + }); + // Spigot end + } else if (this.player.getChatFlags() == EnumChatVisibility.SYSTEM) { // Re-add "Command Only" flag check + ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); + + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + this.sendPacket(new PacketPlayOutChat(chatmessage)); + } else if (true) { + this.chat(s, true); + // CraftBukkit end - the below is for reference. :) + } else { + ChatMessage chatmessage1 = new ChatMessage("chat.type.text", new Object[]{this.player.getScoreboardDisplayName(), s}); + + this.minecraftServer.getPlayerList().sendMessage(chatmessage1, false); + } + + // Spigot - spam exclusions + boolean counted = true; + for (String exclude : org.spigotmc.SpigotConfig.spamExclusions) { + if (exclude != null && s.startsWith(exclude)) { + counted = false; + break; + } + } + // CraftBukkit start - replaced with thread safe throttle + // this.chatThrottle += 20; + if (counted && chatSpamField.addAndGet(this, 20) > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { + if (packetplayinchat.a()) { + // Poweruser start + if (!this.networkManager.lockDownIncomingTraffic()) { + Runnable runnable = new Runnable() { + @Override + public void run() { + try { + PlayerConnection.this.disconnect("disconnect.spam"); + } catch (Exception e) { + c.warn(e.toString()); + } + } + }; + this.minecraftServer.processQueue.add(runnable); + } + // Poweruser end + } else { + this.disconnect("disconnect.spam"); + } + // CraftBukkit end + } + } + } + + // CraftBukkit start - add method + public void chat(String s, boolean async) { + if (s.isEmpty() || this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { + return; + } + + if (!async && s.startsWith("/")) { + this.handleCommand(s); + } else if (this.player.getChatFlags() == EnumChatVisibility.SYSTEM) { + // Do nothing, this is coming from a plugin + } else { + Player player = this.getPlayer(); + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(async, player, s, new LazyPlayerSet()); + this.server.getPluginManager().callEvent(event); + + if (PlayerChatEvent.getHandlerList().getRegisteredListeners().length != 0) { + // Evil plugins still listening to deprecated event + final PlayerChatEvent queueEvent = new PlayerChatEvent(player, event.getMessage(), event.getFormat(), event.getRecipients()); + queueEvent.setCancelled(event.isCancelled()); + // Poweruser start + Runnable runnable = new Runnable() { + @Override + public void run() { + try { + org.bukkit.Bukkit.getPluginManager().callEvent(queueEvent); + + if (queueEvent.isCancelled()) { + return; + } + + String message = String.format(queueEvent.getFormat(), queueEvent.getPlayer().getDisplayName(), queueEvent.getMessage()); + PlayerConnection.this.minecraftServer.console.sendMessage(message); + if (((LazyPlayerSet) queueEvent.getRecipients()).isLazy()) { + for (Object player : PlayerConnection.this.minecraftServer.getPlayerList().players) { + ((EntityPlayer) player).sendMessage(CraftChatMessage.fromString(message)); + } + } else { + for (Player player : queueEvent.getRecipients()) { + player.sendMessage(message); + } + } + } catch (Exception e) { + c.warn(e.toString()); + } + } + }; + if (async) { + minecraftServer.processQueue.add(runnable); + } else { + runnable.run(); + } + // Poweruser end + } else { + if (event.isCancelled()) { + return; + } + + s = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); + minecraftServer.console.sendMessage(s); + if (((LazyPlayerSet) event.getRecipients()).isLazy()) { + for (Object recipient : minecraftServer.getPlayerList().players) { + ((EntityPlayer) recipient).sendMessage(CraftChatMessage.fromString(s)); + } + } else { + for (Player recipient : event.getRecipients()) { + recipient.sendMessage(s); + } + } + } + } + } + // CraftBukkit end + + private void handleCommand(String s) { + org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.startTiming(); // Spigot + + // CraftBukkit start - whole method + if (org.spigotmc.SpigotConfig.logCommands) + PlayerConnection.c.info(this.player.getName() + " issued server command: " + s); + + CraftPlayer player = this.getPlayer(); + + PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet()); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot + return; + } + + try { + if (this.server.dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) { + org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot + return; + } + } catch (org.bukkit.command.CommandException ex) { + player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); + java.util.logging.Logger.getLogger(PlayerConnection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot + return; + } + org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot + //this.minecraftServer.getCommandHandler().a(this.player, s); + // CraftBukkit end + } + + public void a(PacketPlayInArmAnimation packetplayinarmanimation) { + if (this.player.dead) return; // CraftBukkit + this.player.v(); + if (packetplayinarmanimation.d() == 1) { + // CraftBukkit start - Raytrace to look for 'rogue armswings' + + // we only ever use this event when players are sneaking so why raytrace for no reason + if (this.player.isSneaking()) { + float f = 1.0F; + float f1 = this.player.lastPitch + (this.player.pitch - this.player.lastPitch) * f; + float f2 = this.player.lastYaw + (this.player.yaw - this.player.lastYaw) * f; + double d0 = this.player.lastX + (this.player.locX - this.player.lastX) * (double) f; + double d1 = this.player.lastY + (this.player.locY - this.player.lastY) * (double) f + 1.62D - (double) this.player.height; + double d2 = this.player.lastZ + (this.player.locZ - this.player.lastZ) * (double) f; + Vec3D vec3d = Vec3D.a(d0, d1, d2); + + float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); + float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); + float f5 = -MathHelper.cos(-f1 * 0.017453292F); + float f6 = MathHelper.sin(-f1 * 0.017453292F); + float f7 = f4 * f5; + float f8 = f3 * f5; + double d3 = player.playerInteractManager.getGameMode() == EnumGamemode.CREATIVE ? 5.0D : 4.5D; // Spigot + Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); + MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1, false); + + if (movingobjectposition == null || movingobjectposition.type != EnumMovingObjectType.BLOCK) { + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.inventory.getItemInHand()); + } + } + + // Arm swing animation + PlayerAnimationEvent event = new PlayerAnimationEvent(this.getPlayer()); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + // CraftBukkit end + + this.player.ba(); + } + } + + public void a(PacketPlayInEntityAction packetplayinentityaction) { + // CraftBukkit start + if (this.player.dead) return; + + this.player.v(); + if (packetplayinentityaction.d() == 1 || packetplayinentityaction.d() == 2) { + PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this.getPlayer(), packetplayinentityaction.d() == 1); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + + if (packetplayinentityaction.d() == 4 || packetplayinentityaction.d() == 5) { + PlayerToggleSprintEvent event = new PlayerToggleSprintEvent(this.getPlayer(), packetplayinentityaction.d() == 4); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end + + if (packetplayinentityaction.d() == 1) { + this.player.setSneaking(true); + } else if (packetplayinentityaction.d() == 2) { + this.player.setSneaking(false); + } else if (packetplayinentityaction.d() == 4) { + this.player.setSprinting(true); + this.player.setSneaking(false); // MineHQ + + // Guardian start + if (this.player.isBlocking()) { + this.player.bA(); // stopUsingItem + } + // Guardian end + } else if (packetplayinentityaction.d() == 5) { + this.player.setSprinting(false); + } else if (packetplayinentityaction.d() == 3) { + this.player.a(false, true, true); + //this.checkMovement = false; // CraftBukkit - this is handled in teleport + } else if (packetplayinentityaction.d() == 6) { + if (this.player.vehicle != null && this.player.vehicle instanceof EntityHorse) { + ((EntityHorse) this.player.vehicle).w(packetplayinentityaction.e()); + } + } else if (packetplayinentityaction.d() == 7 && this.player.vehicle != null && this.player.vehicle instanceof EntityHorse) { + ((EntityHorse) this.player.vehicle).g(this.player); + } + } + + public void a(PacketPlayInUseEntity packetplayinuseentity) { + if (packetplayinuseentity.c() == null) return; // Spigot - protocol patch + if (this.player.dead) return; // CraftBukkit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + Entity entity = packetplayinuseentity.a((World) worldserver); + // Spigot Start + if (entity == player) { + disconnect("Cannot interact with self!"); + return; + } + // Spigot End + + this.player.v(); + if (entity != null) { + boolean flag = this.player.hasLineOfSight(entity); + double d0 = 36.0D; + + if (!flag) { + d0 = 9.0D; + } + + if (this.player.f(entity) < d0) { + ItemStack itemInHand = this.player.inventory.getItemInHand(); // CraftBukkit + if (packetplayinuseentity.c() == EnumEntityUseAction.INTERACT) { + // CraftBukkit start + boolean triggerTagUpdate = itemInHand != null && itemInHand.getItem() == Items.NAME_TAG && entity instanceof EntityInsentient; + boolean triggerChestUpdate = itemInHand != null && itemInHand.getItem() == Item.getItemOf(Blocks.CHEST) && entity instanceof EntityHorse; + boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEASH && entity instanceof EntityInsentient; + PlayerInteractEntityEvent event = new PlayerInteractEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity()); + this.server.getPluginManager().callEvent(event); + + if (triggerLeashUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Items.LEASH)) { + // Refresh the current leash state + this.sendPacket(new PacketPlayOutAttachEntity(1, entity, ((EntityInsentient) entity).getLeashHolder())); + } + + if (triggerTagUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Items.NAME_TAG)) { + // Refresh the current entity metadata + this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); + } + if (triggerChestUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Item.getItemOf(Blocks.CHEST))) { + this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); + } + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + + this.player.q(entity); + + // CraftBukkit start + if (itemInHand != null && itemInHand.count <= -1) { + this.player.updateInventory(this.player.activeContainer); + } + // CraftBukkit end + } else if (packetplayinuseentity.c() == EnumEntityUseAction.ATTACK) { + if (entity instanceof EntityItem || entity instanceof EntityExperienceOrb || entity instanceof EntityArrow || entity == this.player) { + this.disconnect("Attempting to attack an invalid entity"); + this.minecraftServer.warning("Player " + this.player.getName() + " tried to attack an invalid entity"); + return; + } + + // Guardian start + if (this.player.isBlocking()) { + this.player.bA(); // stopUsingItem + } + // Guardian end + + this.player.attack(entity); + + // CraftBukkit start + if (itemInHand != null && itemInHand.count <= -1) { + this.player.updateInventory(this.player.activeContainer); + } + // CraftBukkit end + } + } + } + } + + public void a(PacketPlayInClientCommand packetplayinclientcommand) { + this.player.v(); + EnumClientCommand enumclientcommand = packetplayinclientcommand.c(); + + switch (ClientCommandOrdinalWrapper.a[enumclientcommand.ordinal()]) { + case 1: + if (this.player.viewingCredits) { + this.minecraftServer.getPlayerList().changeDimension(this.player, 0, PlayerTeleportEvent.TeleportCause.END_PORTAL); // CraftBukkit - reroute logic through custom portal management + } else if (this.player.r().getWorldData().isHardcore()) { + if (this.minecraftServer.N() && this.player.getName().equals(this.minecraftServer.M())) { + this.player.playerConnection.disconnect("You have died. Game over, man, it\'s game over!"); + this.minecraftServer.U(); + } else { + GameProfileBanEntry gameprofilebanentry = new GameProfileBanEntry(this.player.getProfile(), (Date) null, "(You just lost the game)", (Date) null, "Death in Hardcore"); + + this.minecraftServer.getPlayerList().getProfileBans().add(gameprofilebanentry); + this.player.playerConnection.disconnect("You have died. Game over, man, it\'s game over!"); + } + } else { + if (this.player.getHealth() > 0.0F) { + return; + } + + this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, false); + } + break; + + case 2: + this.player.getStatisticManager().a(this.player); + break; + + case 3: + this.player.a((Statistic) AchievementList.f); + } + } + + public void a(PacketPlayInCloseWindow packetplayinclosewindow) { + if (this.player.dead) return; // CraftBukkit + + CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit + + this.player.m(); + } + + public void a(PacketPlayInWindowClick packetplayinwindowclick) { + if (this.player.dead) return; // CraftBukkit + + this.player.v(); + if (!this.player.activeContainer.a(this.player)) + return; // PaperSpigot - check if player is able to use this container + if (this.player.activeContainer.windowId == packetplayinwindowclick.c() && this.player.activeContainer.c(this.player)) { + // CraftBukkit start - Call InventoryClickEvent + if (packetplayinwindowclick.d() < -1 && packetplayinwindowclick.d() != -999) { + return; + } + + InventoryView inventory = this.player.activeContainer.getBukkitView(); + // Spigot start - protocol patch + if (NetworkManager.a(networkManager).attr(NetworkManager.protocolVersion).get() >= 17) { + if (player.activeContainer instanceof ContainerEnchantTable) { + if (packetplayinwindowclick.slot == 1) { + return; + } else if (packetplayinwindowclick.slot > 1) { + packetplayinwindowclick.slot--; + } + } + } + // Spigot end + SlotType type = CraftInventoryView.getSlotType(inventory, packetplayinwindowclick.d()); + + InventoryClickEvent event = null; + ClickType click = ClickType.UNKNOWN; + InventoryAction action = InventoryAction.UNKNOWN; + + ItemStack itemstack = null; + + if (packetplayinwindowclick.d() == -1) { + type = SlotType.OUTSIDE; // override + click = packetplayinwindowclick.e() == 0 ? ClickType.WINDOW_BORDER_LEFT : ClickType.WINDOW_BORDER_RIGHT; + action = InventoryAction.NOTHING; + } else if (packetplayinwindowclick.h() == 0) { + if (packetplayinwindowclick.e() == 0) { + click = ClickType.LEFT; + } else if (packetplayinwindowclick.e() == 1) { + click = ClickType.RIGHT; + } + if (packetplayinwindowclick.e() == 0 || packetplayinwindowclick.e() == 1) { + action = InventoryAction.NOTHING; // Don't want to repeat ourselves + if (packetplayinwindowclick.d() == -999) { + if (player.inventory.getCarried() != null) { + action = packetplayinwindowclick.e() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; + } + } else { + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.d()); + if (slot != null) { + ItemStack clickedItem = slot.getItem(); + ItemStack cursor = player.inventory.getCarried(); + if (clickedItem == null) { + if (cursor != null) { + action = packetplayinwindowclick.e() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE; + } + } else if (slot.isAllowed(player)) { + if (cursor == null) { + action = packetplayinwindowclick.e() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF; + } else if (slot.isAllowed(cursor)) { + if (clickedItem.doMaterialsMatch(cursor) && ItemStack.equals(clickedItem, cursor)) { + int toPlace = packetplayinwindowclick.e() == 0 ? cursor.count : 1; + toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.count); + toPlace = Math.min(toPlace, slot.inventory.getMaxStackSize() - clickedItem.count); + if (toPlace == 1) { + action = InventoryAction.PLACE_ONE; + } else if (toPlace == cursor.count) { + action = InventoryAction.PLACE_ALL; + } else if (toPlace < 0) { + action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks + } else if (toPlace != 0) { + action = InventoryAction.PLACE_SOME; + } + } else if (cursor.count <= slot.getMaxStackSize()) { + action = InventoryAction.SWAP_WITH_CURSOR; + } + } else if (cursor.getItem() == clickedItem.getItem() && (!cursor.usesData() || cursor.getData() == clickedItem.getData()) && ItemStack.equals(cursor, clickedItem)) { + if (clickedItem.count >= 0) { + if (clickedItem.count + cursor.count <= cursor.getMaxStackSize()) { + // As of 1.5, this is result slots only + action = InventoryAction.PICKUP_ALL; + } + } + } + } + } + } + } + } else if (packetplayinwindowclick.h() == 1) { + if (packetplayinwindowclick.e() == 0) { + click = ClickType.SHIFT_LEFT; + } else if (packetplayinwindowclick.e() == 1) { + click = ClickType.SHIFT_RIGHT; + } + if (packetplayinwindowclick.e() == 0 || packetplayinwindowclick.e() == 1) { + if (packetplayinwindowclick.d() < 0) { + action = InventoryAction.NOTHING; + } else { + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.d()); + if (slot != null && slot.isAllowed(this.player) && slot.hasItem()) { + action = InventoryAction.MOVE_TO_OTHER_INVENTORY; + } else { + action = InventoryAction.NOTHING; + } + } + } + } else if (packetplayinwindowclick.h() == 2) { + if (packetplayinwindowclick.e() >= 0 && packetplayinwindowclick.e() < 9) { + click = ClickType.NUMBER_KEY; + Slot clickedSlot = this.player.activeContainer.getSlot(packetplayinwindowclick.d()); + if (clickedSlot.isAllowed(player)) { + ItemStack hotbar = this.player.inventory.getItem(packetplayinwindowclick.e()); + boolean canCleanSwap = hotbar == null || (clickedSlot.inventory == player.inventory && clickedSlot.isAllowed(hotbar)); // the slot will accept the hotbar item + if (clickedSlot.hasItem()) { + if (canCleanSwap) { + action = InventoryAction.HOTBAR_SWAP; + } else { + int firstEmptySlot = player.inventory.getFirstEmptySlotIndex(); + if (firstEmptySlot > -1) { + action = InventoryAction.HOTBAR_MOVE_AND_READD; + } else { + action = InventoryAction.NOTHING; // This is not sane! Mojang: You should test for other slots of same type + } + } + } else if (!clickedSlot.hasItem() && hotbar != null && clickedSlot.isAllowed(hotbar)) { + action = InventoryAction.HOTBAR_SWAP; + } else { + action = InventoryAction.NOTHING; + } + } else { + action = InventoryAction.NOTHING; + } + // Special constructor for number key + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.d(), click, action, packetplayinwindowclick.e()); + } + } else if (packetplayinwindowclick.h() == 3) { + if (packetplayinwindowclick.e() == 2) { + click = ClickType.MIDDLE; + if (packetplayinwindowclick.d() == -999) { + action = InventoryAction.NOTHING; + } else { + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.d()); + if (slot != null && slot.hasItem() && player.abilities.canInstantlyBuild && player.inventory.getCarried() == null) { + action = InventoryAction.CLONE_STACK; + } else { + action = InventoryAction.NOTHING; + } + } + } else { + click = ClickType.UNKNOWN; + action = InventoryAction.UNKNOWN; + } + } else if (packetplayinwindowclick.h() == 4) { + if (packetplayinwindowclick.d() >= 0) { + if (packetplayinwindowclick.e() == 0) { + click = ClickType.DROP; + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.d()); + if (slot != null && slot.hasItem() && slot.isAllowed(player) && slot.getItem() != null && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { + action = InventoryAction.DROP_ONE_SLOT; + } else { + action = InventoryAction.NOTHING; + } + } else if (packetplayinwindowclick.e() == 1) { + click = ClickType.CONTROL_DROP; + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.d()); + if (slot != null && slot.hasItem() && slot.isAllowed(player) && slot.getItem() != null && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { + action = InventoryAction.DROP_ALL_SLOT; + } else { + action = InventoryAction.NOTHING; + } + } + } else { + // Sane default (because this happens when they are holding nothing. Don't ask why.) + click = ClickType.LEFT; + if (packetplayinwindowclick.e() == 1) { + click = ClickType.RIGHT; + } + action = InventoryAction.NOTHING; + } + } else if (packetplayinwindowclick.h() == 5) { + itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.d(), packetplayinwindowclick.e(), 5, this.player); + } else if (packetplayinwindowclick.h() == 6) { + click = ClickType.DOUBLE_CLICK; + action = InventoryAction.NOTHING; + if (packetplayinwindowclick.d() >= 0 && this.player.inventory.getCarried() != null) { + ItemStack cursor = this.player.inventory.getCarried(); + action = InventoryAction.NOTHING; + // Quick check for if we have any of the item + if (inventory.getTopInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem()))) || inventory.getBottomInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem())))) { + action = InventoryAction.COLLECT_TO_CURSOR; + } + } + } + // TODO check on updates + + if (packetplayinwindowclick.h() != 5) { + if (click == ClickType.NUMBER_KEY) { + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.d(), click, action, packetplayinwindowclick.e()); + } else { + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.d(), click, action); + } + + org.bukkit.inventory.Inventory top = inventory.getTopInventory(); + if (packetplayinwindowclick.d() == 0 && top instanceof CraftingInventory) { + org.bukkit.inventory.Recipe recipe = ((CraftingInventory) top).getRecipe(); + if (recipe != null) { + if (click == ClickType.NUMBER_KEY) { + event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.d(), click, action, packetplayinwindowclick.e()); + } else { + event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.d(), click, action); + } + } + } + + server.getPluginManager().callEvent(event); + + switch (event.getResult()) { + case ALLOW: + case DEFAULT: + itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.d(), packetplayinwindowclick.e(), packetplayinwindowclick.h(), this.player); + // PaperSpigot start - Stackable Buckets + if (itemstack != null && + ((itemstack.getItem() == Items.LAVA_BUCKET && PaperSpigotConfig.stackableLavaBuckets) || + (itemstack.getItem() == Items.WATER_BUCKET && PaperSpigotConfig.stackableWaterBuckets) || + (itemstack.getItem() == Items.MILK_BUCKET && PaperSpigotConfig.stackableMilkBuckets))) { + if (action == InventoryAction.MOVE_TO_OTHER_INVENTORY) { + this.player.updateInventory(this.player.activeContainer); + } else { + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.d(), this.player.activeContainer.getSlot(packetplayinwindowclick.d()).getItem())); + } + } + // PaperSpigot end + break; + case DENY: + /* Needs enum constructor in InventoryAction + if (action.modifiesOtherSlots()) { + + } else { + if (action.modifiesCursor()) { + this.player.playerConnection.sendPacket(new Packet103SetSlot(-1, -1, this.player.inventory.getCarried())); + } + if (action.modifiesClicked()) { + this.player.playerConnection.sendPacket(new Packet103SetSlot(this.player.activeContainer.windowId, packet102windowclick.slot, this.player.activeContainer.getSlot(packet102windowclick.slot).getItem())); + } + }*/ + switch (action) { + // Modified other slots + case PICKUP_ALL: + case MOVE_TO_OTHER_INVENTORY: + case HOTBAR_MOVE_AND_READD: + case HOTBAR_SWAP: + case COLLECT_TO_CURSOR: + case UNKNOWN: + this.player.updateInventory(this.player.activeContainer); + break; + // Modified cursor and clicked + case PICKUP_SOME: + case PICKUP_HALF: + case PICKUP_ONE: + case PLACE_ALL: + case PLACE_SOME: + case PLACE_ONE: + case SWAP_WITH_CURSOR: + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.d(), this.player.activeContainer.getSlot(packetplayinwindowclick.d()).getItem())); + break; + // Modified clicked only + case DROP_ALL_SLOT: + case DROP_ONE_SLOT: + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.d(), this.player.activeContainer.getSlot(packetplayinwindowclick.d()).getItem())); + break; + // Modified cursor only + case DROP_ALL_CURSOR: + case DROP_ONE_CURSOR: + case CLONE_STACK: + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); + break; + // Nothing + case NOTHING: + break; + } + return; + } + } + // CraftBukkit end + + if (ItemStack.matches(packetplayinwindowclick.g(), itemstack)) { + this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.c(), packetplayinwindowclick.f(), true)); + this.player.g = true; + this.player.activeContainer.b(); + this.player.broadcastCarriedItem(); + this.player.g = false; + } else { + this.n.a(this.player.activeContainer.windowId, Short.valueOf(packetplayinwindowclick.f())); + this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.c(), packetplayinwindowclick.f(), false)); + this.player.activeContainer.a(this.player, false); + ArrayList arraylist = new ArrayList(this.player.activeContainer.c.size()); // Velt + + for (int i = 0; i < this.player.activeContainer.c.size(); ++i) { + arraylist.add(((Slot) this.player.activeContainer.c.get(i)).getItem()); + } + + this.player.a(this.player.activeContainer, arraylist); + + // CraftBukkit start - Send a Set Slot to update the crafting result slot + if (type == SlotType.RESULT && itemstack != null) { + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, 0, itemstack)); + } + // CraftBukkit end + } + } + } + + public void a(PacketPlayInEnchantItem packetplayinenchantitem) { + this.player.v(); + if (this.player.activeContainer.windowId == packetplayinenchantitem.c() && this.player.activeContainer.c(this.player)) { + this.player.activeContainer.a(this.player, packetplayinenchantitem.d()); + this.player.activeContainer.b(); + } + } + + public void a(PacketPlayInSetCreativeSlot packetplayinsetcreativeslot) { + if (this.player.playerInteractManager.isCreative()) { + boolean flag = packetplayinsetcreativeslot.c() < 0; + ItemStack itemstack = packetplayinsetcreativeslot.getItemStack(); + boolean flag1 = packetplayinsetcreativeslot.c() >= 1 && packetplayinsetcreativeslot.c() < 36 + PlayerInventory.getHotbarSize(); + // CraftBukkit - Add invalidItems check + boolean flag2 = itemstack == null || itemstack.getItem() != null && (!invalidItems.contains(Item.getId(itemstack.getItem())) || !org.spigotmc.SpigotConfig.filterCreativeItems); // Spigot + boolean flag3 = itemstack == null || itemstack.getData() >= 0 && itemstack.count <= 64 && itemstack.count > 0; + + // CraftBukkit start - Call click event + if (flag || (flag1 && !ItemStack.matches(this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.c()).getItem(), packetplayinsetcreativeslot.getItemStack()))) { // Insist on valid slot + + org.bukkit.entity.HumanEntity player = this.player.getBukkitEntity(); + InventoryView inventory = new CraftInventoryView(player, player.getInventory(), this.player.defaultContainer); + org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packetplayinsetcreativeslot.getItemStack()); + + SlotType type = SlotType.QUICKBAR; + if (flag) { + type = SlotType.OUTSIDE; + } else if (packetplayinsetcreativeslot.c() < 36) { + if (packetplayinsetcreativeslot.c() >= 5 && packetplayinsetcreativeslot.c() < 9) { + type = SlotType.ARMOR; + } else { + type = SlotType.CONTAINER; + } + } + InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, flag ? -999 : packetplayinsetcreativeslot.c(), item); + server.getPluginManager().callEvent(event); + + itemstack = CraftItemStack.asNMSCopy(event.getCursor()); + + switch (event.getResult()) { + case ALLOW: + // Plugin cleared the id / stacksize checks + flag2 = flag3 = true; + break; + case DEFAULT: + break; + case DENY: + // Reset the slot + if (packetplayinsetcreativeslot.c() >= 0) { + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.defaultContainer.windowId, packetplayinsetcreativeslot.c(), this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.c()).getItem())); + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, null)); + } + return; + } + } + // CraftBukkit end + + if (flag1 && flag2 && flag3) { + if (itemstack == null) { + this.player.defaultContainer.setItem(packetplayinsetcreativeslot.c(), (ItemStack) null); + } else { + this.player.defaultContainer.setItem(packetplayinsetcreativeslot.c(), itemstack); + } + + this.player.defaultContainer.a(this.player, true); + } else if (flag && flag2 && flag3 && this.x < 200) { + this.x += 20; + EntityItem entityitem = this.player.drop(itemstack, true); + + if (entityitem != null) { + entityitem.e(); + } + // Spigot start - protocol patch + } else { + if (flag1) { + player.playerConnection.sendPacket( + new PacketPlayOutSetSlot(0, + packetplayinsetcreativeslot.c(), + player.defaultContainer.getSlot(packetplayinsetcreativeslot.c()).getItem() + ) + ); + } + } + // Spigot end + } + } + + public void a(PacketPlayInTransaction packetplayintransaction) { + if (this.player.dead) return; // CraftBukkit + if (!this.player.activeContainer.a(this.player)) + return; // PaperSpigot - check if player is able to use this container + Short oshort = (Short) this.n.get(this.player.activeContainer.windowId); + + if (oshort != null && packetplayintransaction.d() == oshort.shortValue() && this.player.activeContainer.windowId == packetplayintransaction.c() && !this.player.activeContainer.c(this.player)) { + this.player.activeContainer.a(this.player, true); + } + } + + public void a(PacketPlayInUpdateSign packetplayinupdatesign) { + if (this.player.dead) return; // CraftBukkit + + this.player.v(); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + + if (worldserver.isLoaded(packetplayinupdatesign.c(), packetplayinupdatesign.d(), packetplayinupdatesign.e())) { + TileEntity tileentity = worldserver.getTileEntity(packetplayinupdatesign.c(), packetplayinupdatesign.d(), packetplayinupdatesign.e()); + + if (tileentity instanceof TileEntitySign) { + TileEntitySign tileentitysign = (TileEntitySign) tileentity; + + if (!tileentitysign.a() || tileentitysign.b() != this.player) { + this.minecraftServer.warning("Player " + this.player.getName() + " just tried to change non-editable sign"); + this.sendPacket(new PacketPlayOutUpdateSign(packetplayinupdatesign.c(), packetplayinupdatesign.d(), packetplayinupdatesign.e(), tileentitysign.lines)); // CraftBukkit + return; + } + } + + int i; + int j; + + for (j = 0; j < 4; ++j) { + boolean flag = true; + packetplayinupdatesign.f()[j] = packetplayinupdatesign.f()[j].replaceAll("\uF700", "").replaceAll("\uF701", ""); // Spigot - Mac OSX sends weird chars + + if (packetplayinupdatesign.f()[j].length() > 15) { + flag = false; + } else { + for (i = 0; i < packetplayinupdatesign.f()[j].length(); ++i) { + if (!SharedConstants.isAllowedChatCharacter(packetplayinupdatesign.f()[j].charAt(i))) { + flag = false; + } + } + } + + if (!flag) { + packetplayinupdatesign.f()[j] = "!?"; + } + } + + if (tileentity instanceof TileEntitySign) { + j = packetplayinupdatesign.c(); + int k = packetplayinupdatesign.d(); + + i = packetplayinupdatesign.e(); + TileEntitySign tileentitysign1 = (TileEntitySign) tileentity; + + // CraftBukkit start + Player player = this.server.getPlayer(this.player); + SignChangeEvent event = new SignChangeEvent((org.bukkit.craftbukkit.block.CraftBlock) player.getWorld().getBlockAt(j, k, i), this.server.getPlayer(this.player), packetplayinupdatesign.f()); + this.server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + tileentitysign1.lines = org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines()); + tileentitysign1.isEditable = false; + } + // System.arraycopy(packetplayinupdatesign.f(), 0, tileentitysign1.lines, 0, 4); + // CraftBukkit end + + tileentitysign1.update(); + worldserver.notify(j, k, i); + } + } + } + + public void a(PacketPlayInKeepAlive packetplayinkeepalive) { + // Guardian start + this.lastKeepAlivePacketReceived = networkManager.currentTime; // change this logic + + this.packetsNotReceived -= 1; + + if ((this.player.isAlive()) && (!this.player.sleeping) && (this.lastKAPacketTick + 20L > MinecraftServer.currentTick) && (this.lastKAMovementPacket + 100L < MinecraftServer.currentTick) && + (this.lastNotificationTick + 20L < MinecraftServer.currentTick)) { + this.lastNotificationTick = MinecraftServer.currentTick; + } + + this.lastKAPacketTick = MinecraftServer.currentTick; + // Guardian end + + if (packetplayinkeepalive.c() == this.h) { + int i = (int) (this.d() - this.i); + + this.player.ping = (this.player.ping * 3 + i) / 4; + } + } + + private long d() { + return System.nanoTime() / 1000000L; + } + + public void a(PacketPlayInAbilities packetplayinabilities) { + // CraftBukkit start + if (this.player.abilities.canFly && this.player.abilities.isFlying != packetplayinabilities.isFlying()) { + PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.server.getPlayer(this.player), packetplayinabilities.isFlying()); + this.server.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.player.abilities.isFlying = packetplayinabilities.isFlying(); // Actually set the player's flying status + } else { + this.player.updateAbilities(); // Tell the player their ability was reverted + } + } + // CraftBukkit end + } + + public void a(PacketPlayInTabComplete packetplayintabcomplete) { + // Spigot start - Update 20141113a + if (PlayerConnection.chatSpamField.addAndGet(this, 20) > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { + this.disconnect("disconnect.spam"); + return; + } + // Spigot end + ArrayList arraylist = Lists.newArrayList(); + Iterator iterator = this.minecraftServer.a(this.player, packetplayintabcomplete.c()).iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + arraylist.add(s); + } + + this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete((String[]) arraylist.toArray(new String[arraylist.size()]))); + } + + public void a(PacketPlayInSettings packetplayinsettings) { + this.player.a(packetplayinsettings); + } + + public void a(PacketPlayInCustomPayload packetplayincustompayload) { + PacketDataSerializer packetdataserializer; + ItemStack itemstack; + ItemStack itemstack1; + + // CraftBukkit start - Ignore empty payloads + if (packetplayincustompayload.length <= 0) { + return; + } + // CraftBukkit end + + if ("MC|BEdit".equals(packetplayincustompayload.c())) { + packetdataserializer = new PacketDataSerializer(Unpooled.wrappedBuffer(packetplayincustompayload.e()), networkManager.getVersion()); // Spigot - protocol patch + + try { + itemstack = packetdataserializer.c(); + if (itemstack != null) { + if (!ItemBookAndQuill.a(itemstack.getTag())) { + throw new IOException("Invalid book tag!"); + } + + itemstack1 = this.player.inventory.getItemInHand(); + if (itemstack1 == null) { + return; + } + + if (itemstack.getItem() == Items.BOOK_AND_QUILL && itemstack.getItem() == itemstack1.getItem()) { + // MineHQ start - handle book editting better + ItemStack newBook = itemstack1.cloneItemStack(); + if (!newBook.hasTag()) { + newBook.setTag(new NBTTagCompound()); + } + newBook.tag.set("pages", itemstack.getTag().getList("pages", 8)); + CraftEventFactory.handleEditBookEvent(player, newBook); // CraftBukkit + // MineHQ end + } + + return; + } + // CraftBukkit start + } catch (Exception exception) { + c.error("Couldn\'t handle book info", exception); + this.disconnect("Invalid book data!"); + return; + // CraftBukkit end + } finally { + packetdataserializer.release(); + } + + return; + } else if ("MC|BSign".equals(packetplayincustompayload.c())) { + packetdataserializer = new PacketDataSerializer(Unpooled.wrappedBuffer(packetplayincustompayload.e()), networkManager.getVersion()); // Spigot - protocol patch + + try { + itemstack = packetdataserializer.c(); + if (itemstack != null) { + if (!ItemWrittenBook.a(itemstack.getTag())) { + throw new IOException("Invalid book tag!"); + } + + itemstack1 = this.player.inventory.getItemInHand(); + if (itemstack1 == null) { + return; + } + + if (itemstack.getItem() == Items.WRITTEN_BOOK && itemstack1.getItem() == Items.BOOK_AND_QUILL) { + // MineHQ start - handle book editting better + ItemStack newBook = itemstack1.cloneItemStack(); + if (!newBook.hasTag()) { + newBook.setTag(new NBTTagCompound()); + } + newBook.tag.set("author", new NBTTagString(this.player.getName())); + newBook.tag.set("title", new NBTTagString(itemstack.getTag().getString("title"))); + newBook.tag.set("pages", itemstack.getTag().getList("pages", 8)); + newBook.setItem(Items.WRITTEN_BOOK); + CraftEventFactory.handleEditBookEvent(player, newBook); // CraftBukkit + // MineHQ end + } + + return; + } + // CraftBukkit start + } catch (Throwable exception1) { + c.error("Couldn\'t sign book", exception1); + this.disconnect("Invalid book data!"); + // CraftBukkit end + return; + } finally { + packetdataserializer.release(); + } + + return; + } else { + int i; + DataInputStream datainputstream; + + if ("MC|TrSel".equals(packetplayincustompayload.c())) { + try { + datainputstream = new DataInputStream(new ByteArrayInputStream(packetplayincustompayload.e())); + i = datainputstream.readInt(); + Container container = this.player.activeContainer; + + if (container instanceof ContainerMerchant) { + ((ContainerMerchant) container).e(i); + } + // CraftBukkit start + } catch (Throwable exception2) { + c.error("Couldn\'t select trade", exception2); + this.disconnect("Invalid trade data!"); + // CraftBukkit end + } + } else if ("MC|AdvCdm".equals(packetplayincustompayload.c())) { + if (!this.minecraftServer.getEnableCommandBlock()) { + this.player.sendMessage(new ChatMessage("advMode.notEnabled", new Object[0])); + } else if (this.player.a(2, "") && this.player.abilities.canInstantlyBuild) { + packetdataserializer = new PacketDataSerializer(Unpooled.wrappedBuffer(packetplayincustompayload.e())); + + try { + byte b0 = packetdataserializer.readByte(); + CommandBlockListenerAbstract commandblocklistenerabstract = null; + + if (b0 == 0) { + TileEntity tileentity = this.player.world.getTileEntity(packetdataserializer.readInt(), packetdataserializer.readInt(), packetdataserializer.readInt()); + + if (tileentity instanceof TileEntityCommand) { + commandblocklistenerabstract = ((TileEntityCommand) tileentity).getCommandBlock(); + } + } else if (b0 == 1) { + Entity entity = this.player.world.getEntity(packetdataserializer.readInt()); + + if (entity instanceof EntityMinecartCommandBlock) { + commandblocklistenerabstract = ((EntityMinecartCommandBlock) entity).getCommandBlock(); + } + } + + String s = packetdataserializer.c(packetdataserializer.readableBytes()); + + if (commandblocklistenerabstract != null) { + commandblocklistenerabstract.setCommand(s); + commandblocklistenerabstract.e(); + this.player.sendMessage(new ChatMessage("advMode.setCommand.success", new Object[]{s})); + } + // CraftBukkit start + } catch (Throwable exception3) { + c.error("Couldn\'t set command block", exception3); + this.disconnect("Invalid CommandBlock data!"); + // CraftBukkit end + } finally { + packetdataserializer.release(); + } + } else { + this.player.sendMessage(new ChatMessage("advMode.notAllowed", new Object[0])); + } + } else if ("MC|Beacon".equals(packetplayincustompayload.c())) { + if (this.player.activeContainer instanceof ContainerBeacon) { + try { + datainputstream = new DataInputStream(new ByteArrayInputStream(packetplayincustompayload.e())); + i = datainputstream.readInt(); + int j = datainputstream.readInt(); + ContainerBeacon containerbeacon = (ContainerBeacon) this.player.activeContainer; + Slot slot = containerbeacon.getSlot(0); + + if (slot.hasItem()) { + slot.a(1); + TileEntityBeacon tileentitybeacon = containerbeacon.e(); + + tileentitybeacon.d(i); + tileentitybeacon.e(j); + tileentitybeacon.update(); + } + // CraftBukkit start + } catch (Throwable exception4) { + c.error("Couldn\'t set beacon", exception4); + this.disconnect("Invalid beacon data!"); + // CraftBukkit end + } + } + } else if ("MC|ItemName".equals(packetplayincustompayload.c()) && this.player.activeContainer instanceof ContainerAnvil) { + ContainerAnvil containeranvil = (ContainerAnvil) this.player.activeContainer; + + if (packetplayincustompayload.e() != null && packetplayincustompayload.e().length >= 1) { + String s1 = SharedConstants.a(new String(packetplayincustompayload.e(), Charsets.UTF_8)); + + if (s1.length() <= 30) { + containeranvil.a(s1); + } + } else { + containeranvil.a(""); + } + } + // CraftBukkit start + else if (packetplayincustompayload.c().equals("REGISTER")) { + try { + String channels = new String(packetplayincustompayload.e(), "UTF8"); + for (String channel : channels.split("\0")) { + getPlayer().addChannel(channel); + } + } catch (UnsupportedEncodingException ex) { + throw new AssertionError(ex); + } + } else if (packetplayincustompayload.c().equals("UNREGISTER")) { + try { + String channels = new String(packetplayincustompayload.e(), "UTF8"); + for (String channel : channels.split("\0")) { + getPlayer().removeChannel(channel); + } + } catch (UnsupportedEncodingException ex) { + throw new AssertionError(ex); + } + } else { + server.getMessenger().dispatchIncomingMessage(player.getBukkitEntity(), packetplayincustompayload.c(), packetplayincustompayload.e()); + } + // CraftBukkit end + } + } + + public void a(EnumProtocol enumprotocol, EnumProtocol enumprotocol1) { + if (enumprotocol1 != EnumProtocol.PLAY) { + throw new IllegalStateException("Unexpected change in protocol!"); + } + } + + // CraftBukkit start - Add "isDisconnected" method + public boolean isDisconnected() { + return !this.player.joining && !NetworkManager.a(this.networkManager).config().isAutoRead(); + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerDatFileConverter.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerDatFileConverter.java new file mode 100644 index 0000000..27651b5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerDatFileConverter.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +import java.io.File; +import java.util.UUID; + +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.ProfileLookupCallback; +import net.minecraft.util.com.mojang.authlib.yggdrasil.ProfileNotFoundException; + +final class PlayerDatFileConverter implements ProfileLookupCallback { + + final DedicatedServer a; + final File b; + final File c; + final File d; + final String[] e; + + PlayerDatFileConverter(DedicatedServer dedicatedserver, File file1, File file2, File file3, String[] astring) { + this.a = dedicatedserver; + this.b = file1; + this.c = file2; + this.d = file3; + this.e = astring; + } + + public void onProfileLookupSucceeded(GameProfile gameprofile) { + this.a.getUserCache().a(gameprofile); + UUID uuid = gameprofile.getId(); + + if (uuid == null) { + throw new FileConversionException("Missing UUID for user profile " + gameprofile.getName(), (PredicateEmptyList) null); + } else { + this.a(this.b, this.a(gameprofile), uuid.toString()); + } + } + + public void onProfileLookupFailed(GameProfile gameprofile, Exception exception) { + NameReferencingFileConverter.a().warn("Could not lookup user uuid for " + gameprofile.getName(), exception); + if (exception instanceof ProfileNotFoundException) { + String s = this.a(gameprofile); + + this.a(this.c, s, s); + } else { + throw new FileConversionException("Could not request user " + gameprofile.getName() + " from backend systems", exception, (PredicateEmptyList) null); + } + } + + private void a(File file1, String s, String s1) { + File file2 = new File(this.d, s + ".dat"); + File file3 = new File(file1, s1 + ".dat"); + + // CraftBukkit start - Use old file name to seed lastKnownName + NBTTagCompound root = null; + + try { + root = NBTCompressedStreamTools.a(new java.io.FileInputStream(file2)); + } catch (Exception exception) { + exception.printStackTrace(); + } + + if (root != null) { + if (!root.hasKey("bukkit")) { + root.set("bukkit", new NBTTagCompound()); + } + NBTTagCompound data = root.getCompound("bukkit"); + data.setString("lastKnownName", s); + + try { + NBTCompressedStreamTools.a(root, new java.io.FileOutputStream(file2)); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + // CraftBukkit end + + NameReferencingFileConverter.a(file1); + if (!file2.renameTo(file3)) { + throw new FileConversionException("Could not convert file for " + s, (PredicateEmptyList) null); + } + } + + private String a(GameProfile gameprofile) { + String s = null; + + for (int i = 0; i < this.e.length; ++i) { + if (this.e[i] != null && this.e[i].equalsIgnoreCase(gameprofile.getName())) { + s = this.e[i]; + break; + } + } + + if (s == null) { + throw new FileConversionException("Could not find the filename for " + gameprofile.getName() + " anymore", (PredicateEmptyList) null); + } else { + return s; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerInteractManager.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerInteractManager.java new file mode 100644 index 0000000..cd9d863 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -0,0 +1,456 @@ +package net.minecraft.server; + +// CraftBukkit start + +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDropItemsEvent; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftItem; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.Event; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +// CraftBukkit end + +public class PlayerInteractManager { + + public World world; + public EntityPlayer player; + private EnumGamemode gamemode; + private boolean d; + private int lastDigTick; + private int f; + private int g; + private int h; + private int currentTick; + private boolean j; + private int k; + private int l; + private int m; + private int n; + private int o; + + public PlayerInteractManager(World world) { + this.gamemode = EnumGamemode.NONE; + this.o = -1; + this.world = world; + } + + public void setGameMode(EnumGamemode enumgamemode) { + this.gamemode = enumgamemode; + enumgamemode.a(this.player.abilities); + this.player.updateAbilities(); + } + + public EnumGamemode getGameMode() { + return this.gamemode; + } + + public boolean isCreative() { + return this.gamemode.d(); + } + + public void b(EnumGamemode enumgamemode) { + if (this.gamemode == EnumGamemode.NONE) { + this.gamemode = enumgamemode; + } + + this.setGameMode(this.gamemode); + } + + public void a() { + this.currentTick = MinecraftServer.currentTick; // CraftBukkit + float f; + int i; + + if (this.j) { + int j = this.currentTick - this.n; + Block block = this.world.getType(this.k, this.l, this.m); + + if (block.getMaterial() == Material.AIR) { + this.j = false; + } else { + f = block.getDamage(this.player, this.player.world, this.k, this.l, this.m) * (float) (j + 1); + i = (int) (f * 10.0F); + if (i != this.o) { + this.world.d(this.player.getId(), this.k, this.l, this.m, i); + this.o = i; + } + + if (f >= 1.0F) { + this.j = false; + this.breakBlock(this.k, this.l, this.m); + } + } + } else if (this.d) { + Block block1 = this.world.getType(this.f, this.g, this.h); + + if (block1.getMaterial() == Material.AIR) { + this.world.d(this.player.getId(), this.f, this.g, this.h, -1); + this.o = -1; + this.d = false; + } else { + int k = this.currentTick - this.lastDigTick; + + f = block1.getDamage(this.player, this.player.world, this.f, this.g, this.h) * (float) (k + 1); + i = (int) (f * 10.0F); + if (i != this.o) { + this.world.d(this.player.getId(), this.f, this.g, this.h, i); + this.o = i; + } + } + } + } + + public void dig(int i, int j, int k, int l) { + // CraftBukkit start + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, i, j, k, l, this.player.inventory.getItemInHand()); + if (!this.gamemode.isAdventure() || this.player.d(i, j, k)) { + if (event.isCancelled()) { + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + // Update any tile entity data for this block + TileEntity tileentity = this.world.getTileEntity(i, j, k); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + + this.player.playerConnection.isDigging = false; // Guardian + return; + } + // CraftBukkit end + if (this.isCreative()) { + if (!this.world.douseFire((EntityHuman) null, i, j, k, l)) { + this.breakBlock(i, j, k); + } + } else { + // this.world.douseFire((EntityHuman) null, i, j, k, l); // CraftBukkit - Moved down + this.lastDigTick = this.currentTick; + float f = 1.0F; + Block block = this.world.getType(i, j, k); + // CraftBukkit start - Swings at air do *NOT* exist. + if (event.useInteractedBlock() == Event.Result.DENY) { + // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. + if (block == Blocks.WOODEN_DOOR) { + // For some reason *BOTH* the bottom/top part have to be marked updated. + boolean bottom = (this.world.getData(i, j, k) & 8) == 0; + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j + (bottom ? 1 : -1), k, this.world)); + } else if (block == Blocks.TRAP_DOOR) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + } + } else if (block.getMaterial() != Material.AIR) { + block.attack(this.world, i, j, k, this.player); + f = block.getDamage(this.player, this.player.world, i, j, k); + // Allow fire punching to be blocked + this.world.douseFire((EntityHuman) null, i, j, k, l); + } + + if (event.useItemInHand() == Event.Result.DENY) { + // If we 'insta destroyed' then the client needs to be informed. + if (f > 1.0f) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + } + + this.player.playerConnection.isDigging = false; // Guardian + return; + } + org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, i, j, k, this.player.inventory.getItemInHand(), f >= 1.0f); + + if (blockEvent.isCancelled()) { + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + return; + } + + if (blockEvent.getInstaBreak()) { + f = 2.0f; + } + // CraftBukkit end + + if (block.getMaterial() != Material.AIR && f >= 1.0F) { + this.player.playerConnection.isDigging = false; // Guardian + + this.breakBlock(i, j, k); + } else { + this.d = true; + this.f = i; + this.g = j; + this.h = k; + int i1 = (int) (f * 10.0F); + + this.world.d(this.player.getId(), i, j, k, i1); + this.o = i1; + } + } + world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, i, j, k); // Spigot + } + } + + public void a(int i, int j, int k) { + if (i == this.f && j == this.g && k == this.h) { + this.currentTick = MinecraftServer.currentTick; // CraftBukkit + int l = this.currentTick - this.lastDigTick; + Block block = this.world.getType(i, j, k); + + if (block.getMaterial() != Material.AIR) { + float f = block.getDamage(this.player, this.player.world, i, j, k) * (float) (l + 1); + + if (f >= 0.7F) { + this.d = false; + this.world.d(this.player.getId(), i, j, k, -1); + this.breakBlock(i, j, k); + } else if (!this.j) { + this.d = false; + this.j = true; + this.k = i; + this.l = j; + this.m = k; + this.n = this.lastDigTick; + } + } + // CraftBukkit start - Force block reset to client + } else { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + // CraftBukkit end + } + } + + public void c(int i, int j, int k) { + this.d = false; + this.world.d(this.player.getId(), this.f, this.g, this.h, -1); + } + + private boolean d(int i, int j, int k) { + Block block = this.world.getType(i, j, k); + int l = this.world.getData(i, j, k); + + block.a(this.world, i, j, k, l, this.player); + boolean flag = this.world.setAir(i, j, k); + + if (flag) { + block.postBreak(this.world, i, j, k, l); + } + + return flag; + } + + public boolean breakBlock(int i, int j, int k) { + // CraftBukkit start - fire BlockBreakEvent + BlockBreakEvent event = null; + + if (this.player instanceof EntityPlayer) { + org.bukkit.block.Block block = this.world.getWorld().getBlockAt(i, j, k); + + // Tell client the block is gone immediately then process events + if (world.getTileEntity(i, j, k) == null) { + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(i, j, k, this.world); + packet.block = Blocks.AIR; + packet.data = 0; + ((EntityPlayer) this.player).playerConnection.sendPacket(packet); + } + + event = new BlockBreakEvent(block, this.player.getBukkitEntity()); + + // Adventure mode pre-cancel + event.setCancelled(this.gamemode.isAdventure() && !this.player.d(i, j, k)); + + // Sword + Creative mode pre-cancel + event.setCancelled(event.isCancelled() || (this.gamemode.d() && this.player.be() != null && this.player.be().getItem() instanceof ItemSword)); + + // Calculate default block experience + Block nmsBlock = this.world.getType(i, j, k); + + if (nmsBlock != null && !event.isCancelled() && !this.isCreative() && this.player.a(nmsBlock)) { + // Copied from block.a(world, entityhuman, int, int, int, int) + if (!(nmsBlock.E() && EnchantmentManager.hasSilkTouchEnchantment(this.player))) { + int data = block.getData(); + int bonusLevel = EnchantmentManager.getBonusBlockLootEnchantmentLevel(this.player); + + event.setExpToDrop(nmsBlock.getExpDrop(this.world, data, bonusLevel)); + } + } + + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + // Update any tile entity data for this block + TileEntity tileentity = this.world.getTileEntity(i, j, k); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + return false; + } + } + + if (false && this.gamemode.isAdventure() && !this.player.d(i, j, k)) { // Never trigger + // CraftBukkit end + return false; + } else if (false && this.gamemode.d() && this.player.be() != null && this.player.be().getItem() instanceof ItemSword) { // CraftBukkit - never trigger + return false; + } else { + Block block = this.world.getType(i, j, k); + if (block == Blocks.AIR) return false; // CraftBukkit - A plugin set block to air without cancelling + int l = this.world.getData(i, j, k); + + // CraftBukkit start - Special case skulls, their item data comes from a tile entity + if (block == Blocks.SKULL && !this.isCreative()) { + block.dropNaturally(world, i, j, k, l, 1.0F, 0); + return this.d(i, j, k); + } + // CraftBukkit end + + this.world.a(this.player, 2001, i, j, k, Block.getId(block) + (this.world.getData(i, j, k) << 12)); + boolean flag = this.d(i, j, k); + + if (this.isCreative()) { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j, k, this.world)); + } else { + ItemStack itemstack = this.player.bF(); + boolean flag1 = this.player.a(block); + + if (itemstack != null) { + itemstack.a(this.world, block, i, j, k, this.player); + if (itemstack.count == 0) { + this.player.bG(); + } + } + + boolean drop = event == null || !event.isCancelDrops(); + if (flag && flag1 && drop) { + // MineHQ start + List items = new ArrayList<>(1); + block.droppedItemsCatcher = items; + block.a(this.world, this.player, i, j, k, l); + block.droppedItemsCatcher = null; + BlockDropItemsEvent dropItemsEvent = new BlockDropItemsEvent(this.world.getWorld().getBlockAt(i, j, k), this.player.getBukkitEntity(), items); + Bukkit.getPluginManager().callEvent(dropItemsEvent); + if (!dropItemsEvent.isCancelled() && dropItemsEvent.getToDrop() != null) { + for (final org.bukkit.entity.Item item : dropItemsEvent.getToDrop()) { + this.world.addEntity(((CraftItem) item).getHandle()); + } + } + // MineHQ end + } + } + + // CraftBukkit start - Drop event experience + if (flag && event != null) { + block.dropExperience(this.world, i, j, k, event.getExpToDrop()); + } + // CraftBukkit end + + return flag; + } + } + + public boolean useItem(EntityHuman entityhuman, World world, ItemStack itemstack) { + int i = itemstack.count; + int j = itemstack.getData(); + ItemStack itemstack1 = itemstack.a(world, entityhuman); + + // Spigot start - protocol patch + if (itemstack1 != null && itemstack1.getItem() == Items.WRITTEN_BOOK) { + player.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|BOpen", new byte[0])); + } + // Spigot end + + if (itemstack1 == itemstack && (itemstack1 == null || itemstack1.count == i && itemstack1.n() <= 0 && itemstack1.getData() == j)) { + return false; + } else { + entityhuman.inventory.items[entityhuman.inventory.itemInHandIndex] = itemstack1; + if (this.isCreative()) { + itemstack1.count = i; + if (itemstack1.g()) { + itemstack1.setData(j); + } + } + + if (itemstack1.count <= 0) { // EMC + entityhuman.inventory.items[entityhuman.inventory.itemInHandIndex] = null; + } + + if (!entityhuman.by()) { + ((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer); + } + + return true; + } + } + + public boolean interact(EntityHuman entityhuman, World world, ItemStack itemstack, int i, int j, int k, int l, float f, float f1, float f2) { + /* CraftBukkit start - whole method + if ((!entityhuman.isSneaking() || entityhuman.be() == null) && world.getType(i, j, k).interact(world, i, j, k, entityhuman, l, f, f1, f2)) { + return true; + } else if (itemstack == null) { + return false; + } else if (this.isCreative()) { + int i1 = itemstack.getData(); + int j1 = itemstack.count; + boolean flag = itemstack.placeItem(entityhuman, world, i, j, k, l, f, f1, f2); + + itemstack.setData(i1); + itemstack.count = j1; + return flag; + } else { + return itemstack.placeItem(entityhuman, world, i, j, k, l, f, f1, f2); + } + // Interract event */ + Block block = world.getType(i, j, k); + boolean result = false; + if (block != Blocks.AIR) { + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(entityhuman, Action.RIGHT_CLICK_BLOCK, i, j, k, l, itemstack); + if (event.useInteractedBlock() == Event.Result.DENY) { + // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. + if (block == Blocks.WOODEN_DOOR) { + boolean bottom = (world.getData(i, j, k) & 8) == 0; + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(i, j + (bottom ? 1 : -1), k, world)); + } + result = (event.useItemInHand() != Event.Result.ALLOW); + } else if (!entityhuman.isSneaking() || itemstack == null) { + result = block.interact(world, i, j, k, entityhuman, l, f, f1, f2); + } + + if (itemstack != null && !result) { + int j1 = itemstack.getData(); + int k1 = itemstack.count; + + // MineHQ start - hack to silence sounds from cancelled block place + try { + world.interceptSounds(); + result = itemstack.placeItem(entityhuman, world, i, j, k, l, f, f1, f2); + } finally { + if (result) { + world.sendInterceptedSounds(); + } else { + world.clearInterceptedSounds(); + } + } + // MineHQ end + + // The item count should not decrement in Creative mode. + if (this.isCreative()) { + itemstack.setData(j1); + itemstack.count = k1; + } + } + + // If we have 'true' and no explicit deny *or* an explicit allow -- run the item part of the hook + if (itemstack != null && ((!result && event.useItemInHand() != Event.Result.DENY && !(block == Blocks.FENCE || block == Blocks.NETHER_FENCE)) || event.useItemInHand() == Event.Result.ALLOW)) { // Poweruser - special case fences + this.useItem(entityhuman, world, itemstack); + } + } + return result; + // CraftBukkit end + } + + public void a(WorldServer worldserver) { + this.world = worldserver; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerInventory.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerInventory.java new file mode 100644 index 0000000..12255d8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerInventory.java @@ -0,0 +1,558 @@ +package net.minecraft.server; + +import java.util.concurrent.Callable; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class PlayerInventory implements IInventory { + + public ItemStack[] items = new ItemStack[36]; + public ItemStack[] armor = new ItemStack[4]; + public int itemInHandIndex; + public EntityHuman player; + private ItemStack g; + public boolean e; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public ItemStack[] getArmorContents() { + return this.armor; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return this.player.getBukkitEntity(); + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public PlayerInventory(EntityHuman entityhuman) { + this.player = entityhuman; + } + + public ItemStack getItemInHand() { + return this.itemInHandIndex < 9 && this.itemInHandIndex >= 0 ? this.items[this.itemInHandIndex] : null; + } + + public static int getHotbarSize() { + return 9; + } + + private int c(Item item) { + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null && this.items[i].getItem() == item) { + return i; + } + } + + return -1; + } + + private int firstPartial(ItemStack itemstack) { + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null && this.items[i].getItem() == itemstack.getItem() && this.items[i].isStackable() && this.items[i].count < this.items[i].getMaxStackSize() && this.items[i].count < this.getMaxStackSize() && (!this.items[i].usesData() || this.items[i].getData() == itemstack.getData()) && ItemStack.equals(this.items[i], itemstack)) { + return i; + } + } + + return -1; + } + + // CraftBukkit start - Watch method above! :D + public int canHold(ItemStack itemstack) { + int remains = itemstack.count; + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] == null) return itemstack.count; + + // Taken from firstPartial(ItemStack) + if (this.items[i] != null && this.items[i].getItem() == itemstack.getItem() && this.items[i].isStackable() && this.items[i].count < this.items[i].getMaxStackSize() && this.items[i].count < this.getMaxStackSize() && (!this.items[i].usesData() || this.items[i].getData() == itemstack.getData()) && ItemStack.equals(this.items[i], itemstack)) { + remains -= (this.items[i].getMaxStackSize() < this.getMaxStackSize() ? this.items[i].getMaxStackSize() : this.getMaxStackSize()) - this.items[i].count; + } + if (remains <= 0) return itemstack.count; + } + return itemstack.count - remains; + } + // CraftBukkit end + + public int getFirstEmptySlotIndex() { + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] == null) { + return i; + } + } + + return -1; + } + + public int a(Item item, int i) { + int j = 0; + + int k; + ItemStack itemstack; + + for (k = 0; k < this.items.length; ++k) { + itemstack = this.items[k]; + if (itemstack != null && (item == null || itemstack.getItem() == item) && (i <= -1 || itemstack.getData() == i)) { + j += itemstack.count; + this.items[k] = null; + } + } + + for (k = 0; k < this.armor.length; ++k) { + itemstack = this.armor[k]; + if (itemstack != null && (item == null || itemstack.getItem() == item) && (i <= -1 || itemstack.getData() == i)) { + j += itemstack.count; + this.player.setEquipment(k, null); + } + } + + if (this.g != null) { + if (item != null && this.g.getItem() != item) { + return j; + } + + if (i > -1 && this.g.getData() != i) { + return j; + } + + j += this.g.count; + this.setCarried((ItemStack) null); + } + + return j; + } + + private int e(ItemStack itemstack) { + Item item = itemstack.getItem(); + int i = itemstack.count; + int j; + + if (itemstack.getMaxStackSize() == 1) { + j = this.getFirstEmptySlotIndex(); + if (j < 0) { + return i; + } else { + if (this.items[j] == null) { + this.items[j] = ItemStack.b(itemstack); + } + + return 0; + } + } else { + j = this.firstPartial(itemstack); + if (j < 0) { + j = this.getFirstEmptySlotIndex(); + } + + if (j < 0) { + return i; + } else { + if (this.items[j] == null) { + this.items[j] = new ItemStack(item, 0, itemstack.getData()); + if (itemstack.hasTag()) { + this.items[j].setTag((NBTTagCompound) itemstack.getTag().clone()); + } + } + + int k = i; + + if (i > this.items[j].getMaxStackSize() - this.items[j].count) { + k = this.items[j].getMaxStackSize() - this.items[j].count; + } + + if (k > this.getMaxStackSize() - this.items[j].count) { + k = this.getMaxStackSize() - this.items[j].count; + } + + if (k == 0) { + return i; + } else { + i -= k; + this.items[j].count += k; + this.items[j].c = 5; + return i; + } + } + } + } + + public void k() { + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + this.items[i].a(this.player.world, this.player, i, this.itemInHandIndex == i); + } + } + } + + public boolean a(Item item) { + int i = this.c(item); + + if (i < 0) { + return false; + } else { + if (--this.items[i].count <= 0) { + this.setItem(i, null); + } + + return true; + } + } + + public boolean b(Item item) { + int i = this.c(item); + + return i >= 0; + } + + public boolean pickup(ItemStack itemstack) { + if (itemstack != null && itemstack.count != 0 && itemstack.getItem() != null) { + try { + int i; + + if (itemstack.i()) { + i = this.getFirstEmptySlotIndex(); + if (i >= 0) { + this.setItem(i, ItemStack.b(itemstack));; + this.items[i].c = 5; + itemstack.count = 0; + return true; + } else if (this.player.abilities.canInstantlyBuild) { + itemstack.count = 0; + return true; + } else { + return false; + } + } else { + do { + i = itemstack.count; + itemstack.count = this.e(itemstack); + } while (itemstack.count > 0 && itemstack.count < i); + + if (itemstack.count == i && this.player.abilities.canInstantlyBuild) { + itemstack.count = 0; + return true; + } else { + return itemstack.count < i; + } + } + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Adding item to inventory"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added"); + + crashreportsystemdetails.a("Item ID", Integer.valueOf(Item.getId(itemstack.getItem()))); + crashreportsystemdetails.a("Item data", Integer.valueOf(itemstack.getData())); + crashreportsystemdetails.a("Item name", (Callable) (new CrashReportItemName(this, itemstack))); + throw new ReportedException(crashreport); + } + } else { + return false; + } + } + + public ItemStack splitStack(int i, int j) { + ItemStack[] aitemstack = this.items; + + boolean settingArmor = i >= this.items.length; + if (settingArmor) { + aitemstack = this.armor; + i -= this.items.length; + } + + if (aitemstack[i] != null) { + ItemStack itemstack; + + if (aitemstack[i].count <= j) { + itemstack = aitemstack[i]; + if (settingArmor) { + this.player.setEquipment(i, null); + } else { + aitemstack[i] = null; + } + return itemstack; + } else { + itemstack = aitemstack[i].a(j); + if (aitemstack[i].count == 0) { + if (settingArmor) { + this.player.setEquipment(i, null); + } else { + aitemstack[i] = null; + } + } + + return itemstack; + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + ItemStack[] aitemstack = this.items; + + boolean settingArmor = i >= this.items.length; + if (settingArmor) { + aitemstack = this.armor; + i -= this.items.length; + } + + if (aitemstack[i] != null) { + ItemStack itemstack = aitemstack[i]; + + if (settingArmor) { + this.player.setEquipment(i, null); + } else { + aitemstack[i] = null; + } + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + ItemStack[] aitemstack = this.items; + + if (i >= aitemstack.length) { + i -= aitemstack.length; + this.player.setEquipment(i, itemstack); + } else { + aitemstack[i] = itemstack; + } + + + } + + public float a(Block block) { + float f = 1.0F; + + if (this.items[this.itemInHandIndex] != null) { + f *= this.items[this.itemInHandIndex].a(block); + } + + return f; + } + + public NBTTagList a(NBTTagList nbttaglist) { + int i; + NBTTagCompound nbttagcompound; + + for (i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte) i); + this.items[i].save(nbttagcompound); + nbttaglist.add(nbttagcompound); + } + } + + for (i = 0; i < this.armor.length; ++i) { + if (this.armor[i] != null) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte) (i + 100)); + this.armor[i].save(nbttagcompound); + nbttaglist.add(nbttagcompound); + } + } + + return nbttaglist; + } + + public void b(NBTTagList nbttaglist) { + this.items = new ItemStack[36]; + this.armor = new ItemStack[4]; + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.get(i); + int j = nbttagcompound.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.createStack(nbttagcompound); + + if (itemstack != null) { + if (j >= 0 && j < this.items.length) { + this.items[j] = itemstack; + } + + j -= 100; + if (j >= 0 && j < this.armor.length) { + this.player.setEquipment(j, itemstack); + } + } + } + } + + public int getSize() { + return this.items.length + 4; + } + + public ItemStack getItem(int i) { + ItemStack[] aitemstack = this.items; + + if (i >= aitemstack.length) { + i -= aitemstack.length; + aitemstack = this.armor; + } + + return aitemstack[i]; + } + + public String getInventoryName() { + return "container.inventory"; + } + + public boolean k_() { + return false; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean b(Block block) { + if (block.getMaterial().isAlwaysDestroyable()) { + return true; + } else { + ItemStack itemstack = this.getItem(this.itemInHandIndex); + + return itemstack != null ? itemstack.b(block) : false; + } + } + + public ItemStack d(int i) { + return this.armor[i]; + } + + public int l() { + int i = 0; + + for (int j = 0; j < this.armor.length; ++j) { + if (this.armor[j] != null && this.armor[j].getItem() instanceof ItemArmor) { + int k = ((ItemArmor) this.armor[j].getItem()).c; + + i += k; + } + } + + return i; + } + + public void a(float f) { + f /= org.spigotmc.SpigotConfig.reduceArmorDamage ? 8.0F : 4.0F; // MineHQ + if (f < 1.0F) { + f = 1.0F; + } + + for (int i = 0; i < this.armor.length; ++i) { + if (this.armor[i] != null && this.armor[i].getItem() instanceof ItemArmor) { + this.armor[i].damage((int) f, this.player); + if (this.armor[i].count == 0) { + this.player.setEquipment(i, null); + } + } + } + } + + public void m() { + int i; + + for (i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + this.player.a(this.items[i], true, false); + this.setItem(i, null); + } + } + + for (i = 0; i < this.armor.length; ++i) { + if (this.armor[i] != null) { + this.player.a(this.armor[i], true, false); + this.player.setEquipment(i, null); + } + } + } + + public void update() { + this.e = true; + } + + public void setCarried(ItemStack itemstack) { + this.g = itemstack; + } + + public ItemStack getCarried() { + // CraftBukkit start + if (this.g != null && this.g.count <= 0) { // EMC + this.setCarried(null); + } + // CraftBukkit end + return this.g; + } + + public boolean a(EntityHuman entityhuman) { + return this.player.dead ? false : entityhuman.f(this.player) <= 64.0D; + } + + public boolean c(ItemStack itemstack) { + int i; + + for (i = 0; i < this.armor.length; ++i) { + if (this.armor[i] != null && this.armor[i].doMaterialsMatch(itemstack)) { + return true; + } + } + + for (i = 0; i < this.items.length; ++i) { + if (this.items[i] != null && this.items[i].doMaterialsMatch(itemstack)) { + return true; + } + } + + return false; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public void b(PlayerInventory playerinventory) { + int i; + + for (i = 0; i < this.items.length; ++i) { + this.setItem(i, ItemStack.b(playerinventory.items[i])); + } + + for (i = 0; i < this.armor.length; ++i) { + this.player.setEquipment(i, ItemStack.b(playerinventory.armor[i])); + } + + this.itemInHandIndex = playerinventory.itemInHandIndex; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerList.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerList.java new file mode 100644 index 0000000..d85279a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +1,1380 @@ +package net.minecraft.server; + +import java.io.File; +import java.net.SocketAddress; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Map.Entry; + +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.google.common.collect.Lists; +import net.minecraft.util.com.google.common.collect.Maps; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.TravelAgent; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.util.Vector; +import org.spigotmc.SpigotConfig; +import org.spigotmc.event.player.PlayerSpawnLocationEvent; +// CraftBukkit end + +public abstract class PlayerList { + + // MineHQ start - Dedicated config directory + public static final File a = new File("config/misc", "banned-players.json"); + public static final File b = new File("config/misc", "banned-ips.json"); + public static final File c = new File("config/misc", "ops.json"); + public static final File d = new File("config/misc", "whitelist.json"); + // MineHQ end + private static final Logger g = LogManager.getLogger(); + private static final SimpleDateFormat h = new SimpleDateFormat("yyyy-MM-dd \'at\' HH:mm:ss z"); + private final MinecraftServer server; + public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety + // PaperSpigot start - Player lookup improvements + public final Map playerMap = new java.util.HashMap() { + @Override + public EntityPlayer put(String key, EntityPlayer value) { + return super.put(key.toLowerCase(), value); + } + + @Override + public EntityPlayer get(Object key) { + // put the .playerConnection check done in other places here + EntityPlayer player = super.get(key instanceof String ? ((String)key).toLowerCase() : key); + return (player != null && player.playerConnection != null) ? player : null; + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public EntityPlayer remove(Object key) { + return super.remove(key instanceof String ? ((String)key).toLowerCase() : key); + } + }; + // MineHQ start - Disguises + public final Map disguisePlayerMap = new HashMap() { + @Override + public EntityPlayer put(String key, EntityPlayer value) { + return super.put(key.toLowerCase(), value); + } + + @Override + public EntityPlayer get(Object key) { + // put the .playerConnection check done in other places here + EntityPlayer player = super.get(key instanceof String ? ((String)key).toLowerCase() : key); + return (player != null && player.playerConnection != null) ? player : null; + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public EntityPlayer remove(Object key) { + return super.remove(key instanceof String ? ((String)key).toLowerCase() : key); + } + }; + // MineHQ end + public final Map uuidMap = new java.util.HashMap() { + @Override + public EntityPlayer get(Object key) { + // put the .playerConnection check done in other places here + EntityPlayer player = super.get(key instanceof String ? ((String)key).toLowerCase() : key); + return (player != null && player.playerConnection != null) ? player : null; + } + }; + // PaperSpigot end + private final GameProfileBanList j; + private final IpBanList k; + private final OpList operators; + private final WhiteList whitelist; + private final Map n; + public IPlayerFileData playerFileData; // CraftBukkit - private -> public + public boolean hasWhitelist; // CraftBukkit - private -> public + protected int maxPlayers; + private int q; + private EnumGamemode r; + private boolean s; + private int t; + + // CraftBukkit start + private CraftServer cserver; + + public PlayerList(MinecraftServer minecraftserver) { + minecraftserver.server = new CraftServer(minecraftserver, this); + minecraftserver.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance(); + minecraftserver.reader.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(minecraftserver.server)); + this.cserver = minecraftserver.server; + // CraftBukkit end + + this.j = new GameProfileBanList(a); + this.k = new IpBanList(b); + this.operators = new OpList(c); + this.whitelist = new WhiteList(d); + this.n = Maps.newHashMap(); + this.server = minecraftserver; + this.j.a(false); + this.k.a(false); + this.maxPlayers = 8; + } + + public void a(NetworkManager networkmanager, EntityPlayer entityplayer) { + GameProfile gameprofile = entityplayer.getProfile(); + UserCache usercache = this.server.getUserCache(); + GameProfile gameprofile1 = usercache.a(gameprofile.getId()); + String s = gameprofile1 == null ? gameprofile.getName() : gameprofile1.getName(); + + usercache.a(gameprofile); + NBTTagCompound nbttagcompound = this.a(entityplayer); + + entityplayer.spawnIn(this.server.getWorldServer(entityplayer.dimension)); + entityplayer.playerInteractManager.a((WorldServer) entityplayer.world); + String s1 = "local"; + + if (networkmanager.getSocketAddress() != null) { + s1 = networkmanager.getSocketAddress().toString(); + } + + // Spigot start - spawn location event + Player bukkitPlayer = entityplayer.getBukkitEntity(); + PlayerSpawnLocationEvent ev = new PlayerSpawnLocationEvent(bukkitPlayer, bukkitPlayer.getLocation()); + Bukkit.getPluginManager().callEvent(ev); + + Location loc = ev.getSpawnLocation(); + WorldServer world = ((CraftWorld) loc.getWorld()).getHandle(); + + entityplayer.spawnIn(world); + entityplayer.setPosition(loc.getX(), loc.getY(), loc.getZ()); + entityplayer.b(loc.getYaw(), loc.getPitch()); // should be setYawAndPitch + // Spigot end + + // CraftBukkit - Moved message to after join + // g.info(entityplayer.getName() + "[" + s1 + "] logged in with entity id " + entityplayer.getId() + " at (" + entityplayer.locX + ", " + entityplayer.locY + ", " + entityplayer.locZ + ")"); + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + ChunkCoordinates chunkcoordinates = worldserver.getSpawn(); + + this.a(entityplayer, (EntityPlayer) null, worldserver); + PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer); + + // CraftBukkit start - Don't send a higher than 60 MaxPlayer size, otherwise the PlayerInfo window won't render correctly. + int maxPlayers = this.getMaxPlayers(); + if (maxPlayers > 60) { + maxPlayers = 60; + } + playerconnection.sendPacket(new PacketPlayOutLogin(entityplayer.getId(), entityplayer.playerInteractManager.getGameMode(), worldserver.getWorldData().isHardcore(), worldserver.worldProvider.dimension, worldserver.difficulty, maxPlayers, worldserver.getWorldData().getType())); + entityplayer.getBukkitEntity().sendSupportedChannels(); + // CraftBukkit end + playerconnection.sendPacket(new PacketPlayOutCustomPayload("MC|Brand", this.getServer().getServerModName().getBytes(Charsets.UTF_8))); + playerconnection.sendPacket(new PacketPlayOutSpawnPosition(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z)); + playerconnection.sendPacket(new PacketPlayOutAbilities(entityplayer.abilities)); + playerconnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex)); + entityplayer.getStatisticManager().d(); + entityplayer.getStatisticManager().updateStatistics(entityplayer); + this.sendScoreboard((ScoreboardServer) worldserver.getScoreboard(), entityplayer); + this.server.az(); + /* CraftBukkit start - login message is handled in the event + ChatMessage chatmessage; + + if (!entityplayer.getName().equalsIgnoreCase(s)) { + chatmessage = new ChatMessage("multiplayer.player.joined.renamed", new Object[] { entityplayer.getScoreboardDisplayName(), s}); + } else { + chatmessage = new ChatMessage("multiplayer.player.joined", new Object[] { entityplayer.getScoreboardDisplayName()}); + } + + chatmessage.getChatModifier().setColor(EnumChatFormat.YELLOW); + this.sendMessage(chatmessage); + // CraftBukkit end */ + this.c(entityplayer); + worldserver = this.server.getWorldServer(entityplayer.dimension); // CraftBukkit - Update in case join event changed it + playerconnection.a(entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw, entityplayer.pitch); + this.b(entityplayer, worldserver); + if (this.server.getResourcePack().length() > 0) { + entityplayer.setResourcePack(this.server.getResourcePack()); + } + + Iterator iterator = entityplayer.getEffects().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + playerconnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobeffect)); + } + + entityplayer.syncInventory(); + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("Riding", 10)) { + Entity entity = EntityTypes.a(nbttagcompound.getCompound("Riding"), worldserver); + + if (entity != null) { + entity.attachedToPlayer = true; + worldserver.addEntity(entity); + entityplayer.mount(entity); + entity.attachedToPlayer = false; + } + } + + // CraftBukkit - Moved from above, added world + g.info(entityplayer.getName() + " logged in at (" + entityplayer.world.worldData.getName() + ", " + String.format("%.1f", entityplayer.locX) + ", " + String.format("%.1f", entityplayer.locY) + ", " + String.format("%.1f", entityplayer.locZ) + ")"); + } + + public void sendScoreboard(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) { // CraftBukkit - protected -> public + HashSet hashset = new HashSet(); + Iterator iterator = scoreboardserver.getTeams().iterator(); + + while (iterator.hasNext()) { + ScoreboardTeam scoreboardteam = (ScoreboardTeam) iterator.next(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutScoreboardTeam(scoreboardteam, 0)); + } + + for (int i = 0; i < 3; ++i) { + ScoreboardObjective scoreboardobjective = scoreboardserver.getObjectiveForSlot(i); + + if (scoreboardobjective != null && !hashset.contains(scoreboardobjective)) { + List list = scoreboardserver.getScoreboardScorePacketsForObjective(scoreboardobjective); + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + Packet packet = (Packet) iterator1.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + + hashset.add(scoreboardobjective); + } + } + + scoreboardserver.addViewer(entityplayer); // MineHQ + } + + public void setPlayerFileData(WorldServer[] aworldserver) { + if (this.playerFileData != null) return; // CraftBukkit + this.playerFileData = aworldserver[0].getDataManager().getPlayerFileData(); + } + + public void a(EntityPlayer entityplayer, WorldServer worldserver) { + WorldServer worldserver1 = entityplayer.r(); + + if (worldserver != null) { + worldserver.getPlayerChunkMap().removePlayer(entityplayer); + } + + worldserver1.getPlayerChunkMap().addPlayer(entityplayer); + worldserver1.chunkProviderServer.getChunkAt(MathHelper.floor(entityplayer.locX) >> 4, MathHelper.floor(entityplayer.locZ) >> 4); + } + + public int d() { + return PlayerChunkMap.getFurthestViewableBlock(this.s()); + } + + public NBTTagCompound a(EntityPlayer entityplayer) { + // CraftBukkit - fix reference to worldserver array + NBTTagCompound nbttagcompound = this.server.worlds.get(0).getWorldData().i(); + NBTTagCompound nbttagcompound1; + + if (entityplayer.getName().equals(this.server.M()) && nbttagcompound != null) { + entityplayer.f(nbttagcompound); + nbttagcompound1 = nbttagcompound; + g.debug("loading single player"); + } else { + nbttagcompound1 = this.playerFileData.load(entityplayer); + } + + return nbttagcompound1; + } + + protected void b(EntityPlayer entityplayer) { + if (SpigotConfig.disableSaving) return; // MineHQ + if (org.spigotmc.SpigotConfig.disablePlayerFileSaving) { return; } // Poweruser + this.playerFileData.save(entityplayer); + ServerStatisticManager serverstatisticmanager = (ServerStatisticManager) this.n.get(entityplayer.getUniqueID()); + + if (serverstatisticmanager != null) { + serverstatisticmanager.b(); + } + } + + public void c(EntityPlayer entityplayer) { + cserver.detectListNameConflict(entityplayer); // CraftBukkit + // this.sendAll(new PacketPlayOutPlayerInfo(entityplayer.getName(), true, 1000)); // CraftBukkit - replaced with loop below + this.players.add(entityplayer); + this.playerMap.put(entityplayer.getName(), entityplayer); // PaperSpigot + this.uuidMap.put(entityplayer.getUniqueID(), entityplayer); // PaperSpigot + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + + // CraftBukkit start + PlayerJoinEvent playerJoinEvent = new PlayerJoinEvent(this.cserver.getPlayer(entityplayer), "\u00A7e" + entityplayer.getName() + " joined the game."); + this.cserver.getPluginManager().callEvent(playerJoinEvent); + + String joinMessage = playerJoinEvent.getJoinMessage(); + + if ((joinMessage != null) && (joinMessage.length() > 0)) { + for (IChatBaseComponent line : org.bukkit.craftbukkit.util.CraftChatMessage.fromString(joinMessage)) { + this.server.getPlayerList().sendAll(new PacketPlayOutChat(line)); + } + } + this.cserver.onPlayerJoin(playerJoinEvent.getPlayer()); + + ChunkIOExecutor.adjustPoolSize(this.getPlayerCount()); + // CraftBukkit end + + // CraftBukkit start - Only add if the player wasn't moved in the event + if (entityplayer.world == worldserver && !worldserver.players.contains(entityplayer)) { + worldserver.addEntity(entityplayer); + this.a(entityplayer, (WorldServer) null); + } + // CraftBukkit end + + if (SpigotConfig.onlyCustomTab) return; // MineHQ + + // CraftBukkit start - sendAll above replaced with this loop + PacketPlayOutPlayerInfo packet = PacketPlayOutPlayerInfo.addPlayer( entityplayer ); // Spigot - protocol patch + PacketPlayOutPlayerInfo displayPacket = PacketPlayOutPlayerInfo.updateDisplayName( entityplayer ); // Spigot Update - 20140927a + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer1 = (EntityPlayer) this.players.get(i); + + if (entityplayer1.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) { + entityplayer1.playerConnection.sendPacket(packet); + // Spigot start - Update 20140927a // Update - 20141001a + if ( !entityplayer.getName().equals( entityplayer.listName ) && entityplayer1.playerConnection.networkManager.getVersion() > 28 ) { + entityplayer1.playerConnection.sendPacket( displayPacket ); + } + // Spigot end + } + } + // CraftBukkit end + + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer1 = (EntityPlayer) this.players.get(i); + + // CraftBukkit start + if (!entityplayer.getBukkitEntity().canSee(entityplayer1.getBukkitEntity())) { + continue; + } + // .name -> .listName + entityplayer.playerConnection.sendPacket(PacketPlayOutPlayerInfo.addPlayer( entityplayer1 )); // Spigot - protocol patch + // Spigot start - Update 20140927a // Update - 20141001a + if ( !entityplayer.getName().equals( entityplayer.listName ) && entityplayer.playerConnection.networkManager.getVersion() > 28 ) { + entityplayer.playerConnection.sendPacket( PacketPlayOutPlayerInfo.updateDisplayName( entityplayer1 ) ); + } + // Spigot end + // CraftBukkit end + } + } + + public void d(EntityPlayer entityplayer) { + entityplayer.r().getPlayerChunkMap().movePlayer(entityplayer); + entityplayer.world.playerMap.move(entityplayer); // MineHQ + } + + public String disconnect(EntityPlayer entityplayer) { // CraftBukkit - return string + entityplayer.a(StatisticList.f); + + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it + org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(entityplayer); + + PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(this.cserver.getPlayer(entityplayer), "\u00A7e" + entityplayer.getName() + " left the game."); + this.cserver.getPluginManager().callEvent(playerQuitEvent); + entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); + // CraftBukkit end + + this.b(entityplayer); + WorldServer worldserver = entityplayer.r(); + + if (entityplayer.vehicle != null && !(entityplayer.vehicle instanceof EntityPlayer)) { // CraftBukkit - Don't remove players + worldserver.removeEntity(entityplayer.vehicle); + g.debug("removing player mount"); + } + + worldserver.kill(entityplayer); + worldserver.getPlayerChunkMap().removePlayer(entityplayer); + this.players.remove(entityplayer); + this.playerMap.remove(entityplayer.getName()); // PaperSpigot + // MineHQ start - Disguises + if (entityplayer.getBukkitEntity().isDisguised()) { + this.disguisePlayerMap.remove(entityplayer.getBukkitEntity().getDisguisedName()); + } + // MineHQ end + this.uuidMap.remove(entityplayer.getUniqueID()); // PaperSpigot + this.n.remove(entityplayer.getUniqueID()); + ChunkIOExecutor.adjustPoolSize(this.getPlayerCount()); // CraftBukkit + + // CraftBukkit start - .name -> .listName, replace sendAll with loop + // this.sendAll(new PacketPlayOutPlayerInfo(entityplayer.getName(), false, 9999)); + PacketPlayOutPlayerInfo packet = PacketPlayOutPlayerInfo.removePlayer( entityplayer ); // Spigot - protocol patch + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer1 = (EntityPlayer) this.players.get(i); + + if (entityplayer1.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) { + if (!SpigotConfig.playerListPackets) continue; // MineHQ + entityplayer1.playerConnection.sendPacket(packet); + } else { + entityplayer1.getBukkitEntity().removeDisconnectingPlayer(entityplayer.getBukkitEntity()); + } + } + // This removes the scoreboard (and player reference) for the specific player in the manager + this.cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity()); + + return playerQuitEvent.getQuitMessage(); + // CraftBukkit end + } + + // CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer + public EntityPlayer attemptLogin(LoginListener loginlistener, GameProfile gameprofile, String hostname) { + // Instead of kicking then returning, we need to store the kick reason + // in the event, check with plugins to see if it's ok, and THEN kick + // depending on the outcome. + SocketAddress socketaddress = loginlistener.networkManager.getSocketAddress(); + + EntityPlayer entity = new EntityPlayer(this.server, this.server.getWorldServer(0), gameprofile, new PlayerInteractManager(this.server.getWorldServer(0))); + Player player = entity.getBukkitEntity(); + PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.networkManager.getRawAddress()).getAddress()); + String s; + + if (this.j.isBanned(gameprofile) && !this.j.get(gameprofile).hasExpired()) { + GameProfileBanEntry gameprofilebanentry = (GameProfileBanEntry) this.j.get(gameprofile); + + s = "You are banned from this server!\nReason: " + gameprofilebanentry.getReason(); + if (gameprofilebanentry.getExpires() != null) { + s = s + "\nYour ban will be removed on " + h.format(gameprofilebanentry.getExpires()); + } + + // return s; + if (!gameprofilebanentry.hasExpired()) event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s); // Spigot + } else if (!this.isWhitelisted(gameprofile)) { + // return "You are not white-listed on this server!"; + event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, org.spigotmc.SpigotConfig.whitelistMessage); // Spigot + } else if (this.k.isBanned(socketaddress) && !this.k.get(socketaddress).hasExpired()) { // Spigot + IpBanEntry ipbanentry = this.k.get(socketaddress); + + s = "Your IP address is banned from this server!\nReason: " + ipbanentry.getReason(); + if (ipbanentry.getExpires() != null) { + s = s + "\nYour ban will be removed on " + h.format(ipbanentry.getExpires()); + } + + // return s; + event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s); + } else { + // return this.players.size() >= this.maxPlayers ? "The server is full!" : null; + if (this.players.size() >= this.maxPlayers && !player.isOp()) { // <- CavePvP allow ops to join + event.disallow(PlayerLoginEvent.Result.KICK_FULL, org.spigotmc.SpigotConfig.serverFullMessage); // Spigot + } + } + + this.cserver.getPluginManager().callEvent(event); + if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { + loginlistener.disconnect(event.getKickMessage()); + return null; + } + + return entity; + // CraftBukkit end + } + + public EntityPlayer processLogin(GameProfile gameprofile, EntityPlayer player) { // CraftBukkit - added EntityPlayer + UUID uuid = EntityHuman.a(gameprofile); + + EntityPlayer entityplayer; + + /* // PaperSpigot start - Use exact lookup below + for (int i = 0; i < this.players.size(); ++i) { + entityplayer = (EntityPlayer) this.players.get(i); + if (entityplayer.getUniqueID().equals(uuid)) { + arraylist.add(entityplayer); + } + } + + Iterator iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + entityplayer = (EntityPlayer) iterator.next(); + */ + if ((entityplayer = uuidMap.get(uuid)) != null) { + // PaperSpigot end + entityplayer.playerConnection.disconnect("You logged in from another location"); + } + + /* CraftBukkit start + Object object; + + if (this.server.R()) { + object = new DemoPlayerInteractManager(this.server.getWorldServer(0)); + } else { + object = new PlayerInteractManager(this.server.getWorldServer(0)); + } + + return new EntityPlayer(this.server, this.server.getWorldServer(0), gameprofile, (PlayerInteractManager) object); + // */ + return player; + // CraftBukkit end + } + + // CraftBukkit start + public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag) { + return this.moveToWorld(entityplayer, i, flag, null, true); + } + + public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag, Location location, boolean avoidSuffocation) { + // CraftBukkit end + entityplayer.r().getTracker().untrackPlayer(entityplayer); + // entityplayer.r().getTracker().untrackEntity(entityplayer); // CraftBukkit + entityplayer.r().getPlayerChunkMap().removePlayer(entityplayer); + this.players.remove(entityplayer); + this.server.getWorldServer(entityplayer.dimension).removeEntity(entityplayer); + ChunkCoordinates chunkcoordinates = entityplayer.getBed(); + boolean flag1 = entityplayer.isRespawnForced(); + + /* CraftBukkit start + entityplayer.dimension = i; + Object object; + + if (this.server.R()) { + object = new DemoPlayerInteractManager(this.server.getWorldServer(entityplayer.dimension)); + } else { + object = new PlayerInteractManager(this.server.getWorldServer(entityplayer.dimension)); + } + + EntityPlayer entityplayer1 = new EntityPlayer(this.server, this.server.getWorldServer(entityplayer.dimension), entityplayer.getProfile(), (PlayerInteractManager) object); + // */ + EntityPlayer entityplayer1 = entityplayer; + org.bukkit.World fromWorld = entityplayer1.getBukkitEntity().getWorld(); + entityplayer1.viewingCredits = false; + // CraftBukkit end + + entityplayer1.playerConnection = entityplayer.playerConnection; + entityplayer1.copyTo(entityplayer, flag); + entityplayer1.d(entityplayer.getId()); + // WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); // CraftBukkit - handled later + + // this.a(entityplayer1, entityplayer, worldserver); // CraftBukkit - removed + ChunkCoordinates chunkcoordinates1; + + // CraftBukkit start - fire PlayerRespawnEvent + if (location == null) { + boolean isBedSpawn = false; + CraftWorld cworld = (CraftWorld) this.server.server.getWorld(entityplayer.spawnWorld); + if (cworld != null && chunkcoordinates != null) { + chunkcoordinates1 = EntityHuman.getBed(cworld.getHandle(), chunkcoordinates, flag1); + if (chunkcoordinates1 != null) { + isBedSpawn = true; + location = new Location(cworld, chunkcoordinates1.x + 0.5, chunkcoordinates1.y, chunkcoordinates1.z + 0.5); + } else { + entityplayer1.setRespawnPosition(null, true); + entityplayer1.playerConnection.sendPacket(new PacketPlayOutGameStateChange(0, 0)); + } + } + + if (location == null) { + cworld = (CraftWorld) this.server.server.getWorlds().get(0); + chunkcoordinates = cworld.getHandle().getSpawn(); + + location = new Location(cworld, chunkcoordinates.x + 0.5, chunkcoordinates.y, chunkcoordinates.z + 0.5, cworld.getHandle().getWorldData().getSpawnYaw(), cworld.getHandle().getWorldData().getSpawnPitch()); // Poweruser + } + + Player respawnPlayer = this.cserver.getPlayer(entityplayer1); + PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn); + this.cserver.getPluginManager().callEvent(respawnEvent); + // Spigot Start + if (entityplayer.playerConnection.isDisconnected()) { + return entityplayer; + } + // Spigot End + + location = respawnEvent.getRespawnLocation(); + entityplayer.reset(); + } else { + location.setWorld(this.server.getWorldServer(i).getWorld()); + } + WorldServer worldserver = ((CraftWorld) location.getWorld()).getHandle(); + entityplayer1.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + // CraftBukkit end + + worldserver.chunkProviderServer.getChunkAt(MathHelper.floor(entityplayer1.locX) >> 4, MathHelper.floor(entityplayer1.locZ) >> 4); + + while (avoidSuffocation && !worldserver.getCubes(entityplayer1, entityplayer1.boundingBox).isEmpty()) { // CraftBukkit + entityplayer1.setPosition(entityplayer1.locX, entityplayer1.locY + 1.0D, entityplayer1.locZ); + } + + // CraftBukkit start + byte actualDimension = (byte) (worldserver.getWorld().getEnvironment().getId()); + // Force the client to refresh their chunk cache. + entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn((byte) (actualDimension >= 0 ? -1 : 0), worldserver.difficulty, worldserver.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode())); + entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn(actualDimension, worldserver.difficulty, worldserver.getWorldData().getType(), entityplayer1.playerInteractManager.getGameMode())); + entityplayer1.spawnIn(worldserver); + entityplayer1.dead = false; + entityplayer1.playerConnection.teleport(new Location(worldserver.getWorld(), entityplayer1.locX, entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch)); + entityplayer1.setSneaking(false); + chunkcoordinates1 = worldserver.getSpawn(); + // entityplayer1.playerConnection.a(entityplayer1.locX, entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch); + // CraftBukkit end + entityplayer1.playerConnection.sendPacket(new PacketPlayOutSpawnPosition(chunkcoordinates1.x, chunkcoordinates1.y, chunkcoordinates1.z)); + entityplayer1.playerConnection.sendPacket(new PacketPlayOutExperience(entityplayer1.exp, entityplayer1.expTotal, entityplayer1.expLevel)); + this.b(entityplayer1, worldserver); + // CraftBukkit start + // Don't re-add player to player list if disconnected + if (!entityplayer.playerConnection.isDisconnected()) { + worldserver.getPlayerChunkMap().addPlayer(entityplayer1); + worldserver.addEntity(entityplayer1); + this.players.add(entityplayer1); + } + // Added from changeDimension + this.updateClient(entityplayer1); // Update health, etc... + entityplayer1.updateAbilities(); + Iterator iterator = entityplayer1.getEffects().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityplayer1.playerConnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer1.getId(), mobeffect)); + } + // entityplayer1.syncInventory(); + // CraftBukkit end + entityplayer1.setHealth(entityplayer1.getHealth()); + + // CraftBukkit start + // Don't fire on respawn + if (fromWorld != location.getWorld()) { + PlayerChangedWorldEvent event = new PlayerChangedWorldEvent((Player) entityplayer1.getBukkitEntity(), fromWorld); + Bukkit.getServer().getPluginManager().callEvent(event); + } + + // Save player file again if they were disconnected + if (entityplayer.playerConnection.isDisconnected()) { + this.b(entityplayer1); + } + // CraftBukkit end + + return entityplayer1; + } + + // CraftBukkit start - Replaced the standard handling of portals with a more customised method. + public void changeDimension(EntityPlayer entityplayer, int i, TeleportCause cause) { + WorldServer exitWorld = null; + if (entityplayer.dimension < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // plugins must specify exit from custom Bukkit worlds + // only target existing worlds (compensate for allow-nether/allow-end as false) + for (WorldServer world : this.server.worlds) { + if (world.dimension == i) { + exitWorld = world; + } + } + } + + Location enter = entityplayer.getBukkitEntity().getLocation(); + Location exit = null; + boolean useTravelAgent = false; // don't use agent for custom worlds or return from THE_END + if (exitWorld != null) { + if ((cause == TeleportCause.END_PORTAL) && (i == 0)) { + // THE_END -> NORMAL; use bed if available, otherwise default spawn + exit = ((org.bukkit.craftbukkit.entity.CraftPlayer) entityplayer.getBukkitEntity()).getBedSpawnLocation(); + if (exit == null || ((CraftWorld) exit.getWorld()).getHandle().dimension != 0) { + exit = exitWorld.getWorld().getSpawnLocation(); + } + } else { + // NORMAL <-> NETHER or NORMAL -> THE_END + exit = this.calculateTarget(enter, exitWorld); + useTravelAgent = true; + } + } + + TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent() : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins + agent.setCanCreatePortal(cause != TeleportCause.END_PORTAL); // PaperSpigot - Configurable end credits, don't allow End Portals to create portals + + PlayerPortalEvent event = new PlayerPortalEvent(entityplayer.getBukkitEntity(), enter, exit, agent, cause); + event.useTravelAgent(useTravelAgent); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled() || event.getTo() == null) { + return; + } + + // PaperSpigot - Configurable end credits, if a plugin sets to use a travel agent even if the cause is an end portal, ignore it + exit = cause != TeleportCause.END_PORTAL && event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); + if (exit == null) { + return; + } + exitWorld = ((CraftWorld) exit.getWorld()).getHandle(); + + Vector velocity = entityplayer.getBukkitEntity().getVelocity(); + boolean before = exitWorld.chunkProviderServer.forceChunkLoad; + exitWorld.chunkProviderServer.forceChunkLoad = true; + exitWorld.getTravelAgent().adjustExit(entityplayer, exit, velocity); + exitWorld.chunkProviderServer.forceChunkLoad = before; + + this.moveToWorld(entityplayer, exitWorld.dimension, true, exit, false); // Vanilla doesn't check for suffocation when handling portals, so neither should we + if (entityplayer.motX != velocity.getX() || entityplayer.motY != velocity.getY() || entityplayer.motZ != velocity.getZ()) { + entityplayer.getBukkitEntity().setVelocity(velocity); + } + // CraftBukkit end + } + + public void a(Entity entity, int i, WorldServer worldserver, WorldServer worldserver1) { + // CraftBukkit start - Split into modular functions + Location exit = this.calculateTarget(entity.getBukkitEntity().getLocation(), worldserver1); + this.repositionEntity(entity, exit, true); + } + + // Copy of original a(Entity, int, WorldServer, WorldServer) method with only location calculation logic + public Location calculateTarget(Location enter, World target) { + WorldServer worldserver = ((CraftWorld) enter.getWorld()).getHandle(); + WorldServer worldserver1 = ((CraftWorld) target.getWorld()).getHandle(); + int i = worldserver.dimension; + + double y = enter.getY(); + float yaw = enter.getYaw(); + float pitch = enter.getPitch(); + double d0 = enter.getX(); + double d1 = enter.getZ(); + double d2 = 8.0D; + /* + double d3 = entity.locX; + double d4 = entity.locY; + double d5 = entity.locZ; + float f = entity.yaw; + + worldserver.methodProfiler.a("moving"); + */ + if (worldserver1.dimension == -1) { + d0 /= d2; + d1 /= d2; + /* + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + */ + } else if (worldserver1.dimension == 0) { + d0 *= d2; + d1 *= d2; + /* + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + */ + } else { + ChunkCoordinates chunkcoordinates; + + if (i == 1) { + // use default NORMAL world spawn instead of target + worldserver1 = this.server.worlds.get(0); + chunkcoordinates = worldserver1.getSpawn(); + } else { + // Poweruser start + if(worldserver1.spigotConfig.useAlternateEndSpawn) { + chunkcoordinates = worldserver1.getSpawn(); + } else { + chunkcoordinates = worldserver1.getDimensionSpawn(); + } + // Poweruser end + } + + d0 = (double) chunkcoordinates.x; + y = (double) chunkcoordinates.y; + d1 = (double) chunkcoordinates.z; + // Poweruser start + yaw = worldserver1.getWorldData().getSpawnYaw(); + pitch = worldserver1.getWorldData().getSpawnPitch(); + // Poweruser end + /* + entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + */ + } + + // worldserver.methodProfiler.b(); + if (i != 1) { + // worldserver.methodProfiler.a("placing"); + d0 = (double) MathHelper.a((int) d0, -29999872, 29999872); + d1 = (double) MathHelper.a((int) d1, -29999872, 29999872); + /* + if (entity.isAlive()) { + worldserver1.addEntity(entity); + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + worldserver1.entityJoinedWorld(entity, false); + worldserver1.getTravelAgent().a(entity, d3, d4, d5, f); + } + + worldserver.methodProfiler.b(); + */ + } + + // entity.spawnIn(worldserver1); + return new Location(worldserver1.getWorld(), d0, y, d1, yaw, pitch); + } + + // copy of original a(Entity, int, WorldServer, WorldServer) method with only entity repositioning logic + public void repositionEntity(Entity entity, Location exit, boolean portal) { + int i = entity.dimension; + WorldServer worldserver = (WorldServer) entity.world; + WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); + /* + double d0 = entity.locX; + double d1 = entity.locZ; + double d2 = 8.0D; + double d3 = entity.locX; + double d4 = entity.locY; + double d5 = entity.locZ; + float f = entity.yaw; + */ + + worldserver.methodProfiler.a("moving"); + entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + /* + if (entity.dimension == -1) { + d0 /= d2; + d1 /= d2; + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + } else if (entity.dimension == 0) { + d0 *= d2; + d1 *= d2; + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + } else { + ChunkCoordinates chunkcoordinates; + + if (i == 1) { + chunkcoordinates = worldserver1.getSpawn(); + } else { + chunkcoordinates = worldserver1.getDimensionSpawn(); + } + + d0 = (double) chunkcoordinates.x; + entity.locY = (double) chunkcoordinates.y; + d1 = (double) chunkcoordinates.z; + entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + } + */ + + worldserver.methodProfiler.b(); + if (i != 1) { + worldserver.methodProfiler.a("placing"); + /* + d0 = (double) MathHelper.a((int) d0, -29999872, 29999872); + d1 = (double) MathHelper.a((int) d1, -29999872, 29999872); + */ + if (entity.isAlive()) { + // entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch) + // worldserver1.getTravelAgent().a(entity, d3, d4, d5, f); + if (portal) { + Vector velocity = entity.getBukkitEntity().getVelocity(); + worldserver1.getTravelAgent().adjustExit(entity, exit, velocity); + entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); + if (entity.motX != velocity.getX() || entity.motY != velocity.getY() || entity.motZ != velocity.getZ()) { + entity.getBukkitEntity().setVelocity(velocity); + } + } + worldserver1.addEntity(entity); + worldserver1.entityJoinedWorld(entity, false); + } + + worldserver.methodProfiler.b(); + } + + entity.spawnIn(worldserver1); + // CraftBukkit end + } + + private int currentPing = 0; + + public void tick() { + if (++this.t > 600) { + this.t = 0; + } + + /* CraftBukkit start - Remove updating of lag to players -- it spams way to much on big servers. + if (this.t < this.players.size()) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(this.p); + + this.sendAll(new PacketPlayOutPlayerInfo(entityplayer.getName(), true, entityplayer.ping)); + } + // CraftBukkit end */ + // Spigot start + try + { + if ( !players.isEmpty() /*&& SpigotConfig.updatePingOnTablist*/) + { + currentPing = ( currentPing + 1 ) % this.players.size(); + EntityPlayer player = (EntityPlayer) this.players.get( currentPing ); + int oldPingToBars = pingToBars(player.lastPing); + int newPingToBars = pingToBars(player.ping); + if ( player.lastPing == -1 || oldPingToBars != newPingToBars ) + { + Packet packet = PacketPlayOutPlayerInfo.updatePing( player ); // Spigot - protocol patch + for ( EntityPlayer splayer : (List) this.players ) + { + if ( splayer.getBukkitEntity().canSee( player.getBukkitEntity() ) ) + { + splayer.playerConnection.sendPacket( packet ); + } + } + player.lastPing = player.ping; + } + } + } catch ( Exception e ) { + // Better safe than sorry :) + } + // Spigot end + } + + // MineHQ start + private int pingToBars(int ping) { + if (ping < 0) return 5; + else if (ping < 150) return 0; + else if (ping < 300) return 1; + else if (ping < 600) return 2; + else if (ping < 1_000) return 3; + else if (ping < Short.MAX_VALUE) return 4; + else return 5; + } + // MineHQ end + + public void sendAll(Packet packet) { + for (int i = 0; i < this.players.size(); ++i) { + ((EntityPlayer) this.players.get(i)).playerConnection.sendPacket(packet); + } + } + + public void a(Packet packet, int i) { + for (int j = 0; j < this.players.size(); ++j) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(j); + + if (entityplayer.dimension == i) { + entityplayer.playerConnection.sendPacket(packet); + } + } + } + + public String b(boolean flag) { + String s = ""; + ArrayList arraylist = Lists.newArrayList(this.players); + + for (int i = 0; i < arraylist.size(); ++i) { + if (i > 0) { + s = s + ", "; + } + + s = s + ((EntityPlayer) arraylist.get(i)).getName(); + if (flag) { + s = s + " (" + ((EntityPlayer) arraylist.get(i)).getUniqueID().toString() + ")"; + } + } + + return s; + } + + public String[] f() { + String[] astring = new String[this.players.size()]; + + for (int i = 0; i < this.players.size(); ++i) { + astring[i] = ((EntityPlayer) this.players.get(i)).getName(); + } + + return astring; + } + + public GameProfile[] g() { + GameProfile[] agameprofile = new GameProfile[this.players.size()]; + + for (int i = 0; i < this.players.size(); ++i) { + agameprofile[i] = ((EntityPlayer) this.players.get(i)).getProfile(); + } + + return agameprofile; + } + + public GameProfileBanList getProfileBans() { + return this.j; + } + + public IpBanList getIPBans() { + return this.k; + } + + public void addOp(GameProfile gameprofile) { + this.operators.add(new OpListEntry(gameprofile, this.server.l())); + + // CraftBukkit start + Player player = server.server.getPlayer(gameprofile.getId()); + if (player != null) { + player.recalculatePermissions(); + } + // CraftBukkit end + } + + public void removeOp(GameProfile gameprofile) { + this.operators.remove(gameprofile); + + // CraftBukkit start + Player player = server.server.getPlayer(gameprofile.getId()); + if (player != null) { + player.recalculatePermissions(); + } + // CraftBukkit end + } + + public boolean isWhitelisted(GameProfile gameprofile) { + return !this.hasWhitelist || this.operators.d(gameprofile) || this.whitelist.d(gameprofile); + } + + public boolean isOp(GameProfile gameprofile) { + // CraftBukkit - fix reference to worldserver array + return this.operators.d(gameprofile) || this.server.N() && this.server.worlds.get(0).getWorldData().allowCommands() && this.server.M().equalsIgnoreCase(gameprofile.getName()) || this.s; + } + + public EntityPlayer getPlayer(String s) { + if (true) { return playerMap.get(s); } // PaperSpigot + Iterator iterator = this.players.iterator(); + + EntityPlayer entityplayer; + + do { + if (!iterator.hasNext()) { + return null; + } + + entityplayer = (EntityPlayer) iterator.next(); + } while (!entityplayer.getName().equalsIgnoreCase(s)); + + return entityplayer; + } + + public List a(ChunkCoordinates chunkcoordinates, int i, int j, int k, int l, int i1, int j1, Map map, String s, String s1, World world) { + if (this.players.isEmpty()) { + return Collections.emptyList(); + } else { + Object object = new ArrayList(); + boolean flag = k < 0; + boolean flag1 = s != null && s.startsWith("!"); + boolean flag2 = s1 != null && s1.startsWith("!"); + int k1 = i * i; + int l1 = j * j; + + k = MathHelper.a(k); + if (flag1) { + s = s.substring(1); + } + + if (flag2) { + s1 = s1.substring(1); + } + + for (int i2 = 0; i2 < this.players.size(); ++i2) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(i2); + + if ((world == null || entityplayer.world == world) && (s == null || flag1 != s.equalsIgnoreCase(entityplayer.getName()))) { + if (s1 != null) { + ScoreboardTeamBase scoreboardteambase = entityplayer.getScoreboardTeam(); + String s2 = scoreboardteambase == null ? "" : scoreboardteambase.getName(); + + if (flag2 == s1.equalsIgnoreCase(s2)) { + continue; + } + } + + if (chunkcoordinates != null && (i > 0 || j > 0)) { + float f = chunkcoordinates.e(entityplayer.getChunkCoordinates()); + + if (i > 0 && f < (float) k1 || j > 0 && f > (float) l1) { + continue; + } + } + + if (this.a((EntityHuman) entityplayer, map) && (l == EnumGamemode.NONE.getId() || l == entityplayer.playerInteractManager.getGameMode().getId()) && (i1 <= 0 || entityplayer.expLevel >= i1) && entityplayer.expLevel <= j1) { + ((List) object).add(entityplayer); + } + } + } + + if (chunkcoordinates != null) { + Collections.sort((List) object, new PlayerDistanceComparator(chunkcoordinates)); + } + + if (flag) { + Collections.reverse((List) object); + } + + if (k > 0) { + object = ((List) object).subList(0, Math.min(k, ((List) object).size())); + } + + return (List) object; + } + } + + private boolean a(EntityHuman entityhuman, Map map) { + if (map != null && map.size() != 0) { + Iterator iterator = map.entrySet().iterator(); + + Entry entry; + boolean flag; + int i; + + do { + if (!iterator.hasNext()) { + return true; + } + + entry = (Entry) iterator.next(); + String s = (String) entry.getKey(); + + flag = false; + if (s.endsWith("_min") && s.length() > 4) { + flag = true; + s = s.substring(0, s.length() - 4); + } + + Scoreboard scoreboard = entityhuman.getScoreboard(); + ScoreboardObjective scoreboardobjective = scoreboard.getObjective(s); + + if (scoreboardobjective == null) { + return false; + } + + ScoreboardScore scoreboardscore = entityhuman.getScoreboard().getPlayerScoreForObjective(entityhuman.getName(), scoreboardobjective); + + i = scoreboardscore.getScore(); + if (i < ((Integer) entry.getValue()).intValue() && flag) { + return false; + } + } while (i <= ((Integer) entry.getValue()).intValue() || flag); + + return false; + } else { + return true; + } + } + + public void sendPacketNearby(double d0, double d1, double d2, double d3, int i, Packet packet) { + this.sendPacketNearby((EntityHuman) null, d0, d1, d2, d3, i, packet); + } + + public void sendPacketNearby(EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, Packet packet) { + for (int j = 0; j < this.players.size(); ++j) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(j); + + // CraftBukkit start - Test if player receiving packet can see the source of the packet + if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { + continue; + } + // CraftBukkit end + + if (entityplayer != entityhuman && entityplayer.dimension == i) { + double d4 = d0 - entityplayer.locX; + double d5 = d1 - entityplayer.locY; + double d6 = d2 - entityplayer.locZ; + + if (d4 * d4 + d5 * d5 + d6 * d6 < d3 * d3) { + entityplayer.playerConnection.sendPacket(packet); + } + } + } + } + + public void savePlayers() { + if (SpigotConfig.disableSaving) return; // MineHQ + if (org.spigotmc.SpigotConfig.disablePlayerFileSaving) { return; } // Poweruser + for (int i = 0; i < this.players.size(); ++i) { + this.b((EntityPlayer) this.players.get(i)); + } + } + + public void addWhitelist(GameProfile gameprofile) { + this.whitelist.add(new WhiteListEntry(gameprofile)); + } + + public void removeWhitelist(GameProfile gameprofile) { + this.whitelist.remove(gameprofile); + } + + public WhiteList getWhitelist() { + return this.whitelist; + } + + public String[] getWhitelisted() { + return this.whitelist.getEntries(); + } + + public OpList getOPs() { + return this.operators; + } + + public String[] n() { + return this.operators.getEntries(); + } + + public void reloadWhitelist() {} + + public void b(EntityPlayer entityplayer, WorldServer worldserver) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle"))); + if (worldserver.Q()) { + // CraftBukkit start - handle player weather + // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1, 0.0F)); + // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, worldserver.j(1.0F))); + // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, worldserver.h(1.0F))); + entityplayer.setPlayerWeather(org.bukkit.WeatherType.DOWNFALL, false); + // CraftBukkit end + } + } + + public void updateClient(EntityPlayer entityplayer) { + entityplayer.updateInventory(entityplayer.defaultContainer); + entityplayer.getBukkitEntity().updateScaledHealth(); // CraftBukkit - Update scaled health on respawn and worldchange + entityplayer.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex)); + } + + public int getPlayerCount() { + return this.players.size(); + } + + public int getMaxPlayers() { + return this.maxPlayers; + } + + //CavePvP + public void setMaxPlayers(int i) { + this.maxPlayers = i; + } + + public String[] getSeenPlayers() { + // CraftBukkit - fix reference to worldserver array + return this.server.worlds.get(0).getDataManager().getPlayerFileData().getSeenPlayers(); + } + + public boolean getHasWhitelist() { + return this.hasWhitelist; + } + + public void setHasWhitelist(boolean flag) { + this.hasWhitelist = flag; + } + + public List b(String s) { + ArrayList arraylist = new ArrayList(); + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (entityplayer.s().equals(s)) { + arraylist.add(entityplayer); + } + } + + return arraylist; + } + + public int s() { + return this.q; + } + + public MinecraftServer getServer() { + return this.server; + } + + public NBTTagCompound t() { + return null; + } + + private void a(EntityPlayer entityplayer, EntityPlayer entityplayer1, World world) { + if (entityplayer1 != null) { + entityplayer.playerInteractManager.setGameMode(entityplayer1.playerInteractManager.getGameMode()); + } else if (this.r != null) { + entityplayer.playerInteractManager.setGameMode(this.r); + } + + entityplayer.playerInteractManager.b(world.getWorldData().getGameType()); + } + + public void u() { + while (!this.players.isEmpty()) { + // Spigot start + EntityPlayer p = (EntityPlayer) this.players.get( 0 ); + p.playerConnection.disconnect( this.server.server.getShutdownMessage() ); + if ( ( !this.players.isEmpty() ) && ( this.players.get( 0 ) == p ) ) + { + this.players.remove( 0 ); // Prevent shutdown hang if already disconnected + } + // Spigot end + } + } + + // CraftBukkit start - Support multi-line messages + public void sendMessage(IChatBaseComponent[] ichatbasecomponent) { + for (IChatBaseComponent component : ichatbasecomponent) { + sendMessage(component, true); + } + } + // CraftBukkit end + + public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { + this.server.sendMessage(ichatbasecomponent); + this.sendAll(new PacketPlayOutChat(ichatbasecomponent, flag)); + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.sendMessage(ichatbasecomponent, true); + } + + public ServerStatisticManager a(EntityHuman entityhuman) { + UUID uuid = entityhuman.getUniqueID(); + ServerStatisticManager serverstatisticmanager = uuid == null ? null : (ServerStatisticManager) this.n.get(uuid); + + if (serverstatisticmanager == null) { + File file1 = new File(this.server.getWorldServer(0).getDataManager().getDirectory(), "stats"); + File file2 = new File(file1, uuid.toString() + ".json"); + + serverstatisticmanager = new ServerStatisticManager(this.server, file2); + serverstatisticmanager.a(); + this.n.put(uuid, serverstatisticmanager); + } + + return serverstatisticmanager; + } + + public void a(int i) { + this.q = i; + if (this.server.worldServer != null) { + WorldServer[] aworldserver = this.server.worldServer; + int j = aworldserver.length; + + for (int k = 0; k < j; ++k) { + WorldServer worldserver = aworldserver[k]; + + if (worldserver != null) { + worldserver.getPlayerChunkMap().a(i); + } + } + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/server/PlayerSelector.java b/vspigot-server/src/main/java/net/minecraft/server/PlayerSelector.java new file mode 100644 index 0000000..14d44f0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PlayerSelector.java @@ -0,0 +1,251 @@ +package net.minecraft.server; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PlayerSelector { + + private static final Pattern a = Pattern.compile("^@([parf])(?:\\[([\\w=,!-]*)\\])?$"); + private static final Pattern b = Pattern.compile("\\G([-!]?[\\w-]*)(?:$|,)"); + private static final Pattern c = Pattern.compile("\\G(\\w+)=([-!]?[\\w-]*)(?:$|,)"); + + public static EntityPlayer getPlayer(ICommandListener icommandlistener, String s) { + EntityPlayer[] aentityplayer = getPlayers(icommandlistener, s); + + return aentityplayer != null && aentityplayer.length == 1 ? aentityplayer[0] : null; + } + + public static IChatBaseComponent getPlayerNames(ICommandListener icommandlistener, String s) { + EntityPlayer[] aentityplayer = getPlayers(icommandlistener, s); + + if (aentityplayer != null && aentityplayer.length != 0) { + IChatBaseComponent[] aichatbasecomponent = new IChatBaseComponent[aentityplayer.length]; + + for (int i = 0; i < aichatbasecomponent.length; ++i) { + aichatbasecomponent[i] = aentityplayer[i].getScoreboardDisplayName(); + } + + return CommandAbstract.a(aichatbasecomponent); + } else { + return null; + } + } + + public static EntityPlayer[] getPlayers(ICommandListener icommandlistener, String s) { + // CraftBukkit start - disable playerselections for ICommandListeners other than command blocks + if (!(icommandlistener instanceof CommandBlockListenerAbstract)) { + return null; + } + // CraftBukkit end + + Matcher matcher = a.matcher(s); + + if (matcher.matches()) { + Map map = h(matcher.group(2)); + String s1 = matcher.group(1); + int i = c(s1); + int j = d(s1); + int k = f(s1); + int l = e(s1); + int i1 = g(s1); + int j1 = EnumGamemode.NONE.getId(); + ChunkCoordinates chunkcoordinates = icommandlistener.getChunkCoordinates(); + Map map1 = a(map); + String s2 = null; + String s3 = null; + boolean flag = false; + + if (map.containsKey("rm")) { + i = MathHelper.a((String) map.get("rm"), i); + flag = true; + } + + if (map.containsKey("r")) { + j = MathHelper.a((String) map.get("r"), j); + flag = true; + } + + if (map.containsKey("lm")) { + k = MathHelper.a((String) map.get("lm"), k); + } + + if (map.containsKey("l")) { + l = MathHelper.a((String) map.get("l"), l); + } + + if (map.containsKey("x")) { + chunkcoordinates.x = MathHelper.a((String) map.get("x"), chunkcoordinates.x); + flag = true; + } + + if (map.containsKey("y")) { + chunkcoordinates.y = MathHelper.a((String) map.get("y"), chunkcoordinates.y); + flag = true; + } + + if (map.containsKey("z")) { + chunkcoordinates.z = MathHelper.a((String) map.get("z"), chunkcoordinates.z); + flag = true; + } + + if (map.containsKey("m")) { + j1 = MathHelper.a((String) map.get("m"), j1); + } + + if (map.containsKey("c")) { + i1 = MathHelper.a((String) map.get("c"), i1); + } + + if (map.containsKey("team")) { + s3 = (String) map.get("team"); + } + + if (map.containsKey("name")) { + s2 = (String) map.get("name"); + } + + World world = flag ? icommandlistener.getWorld() : null; + List list; + + if (!s1.equals("p") && !s1.equals("a")) { + if (s1.equals("r")) { + list = MinecraftServer.getServer().getPlayerList().a(chunkcoordinates, i, j, 0, j1, k, l, map1, s2, s3, world); + Collections.shuffle(list); + list = list.subList(0, Math.min(i1, list.size())); + return list.isEmpty() ? new EntityPlayer[0] : (EntityPlayer[]) list.toArray(new EntityPlayer[list.size()]); + } else { + return null; + } + } else { + list = MinecraftServer.getServer().getPlayerList().a(chunkcoordinates, i, j, i1, j1, k, l, map1, s2, s3, world); + return list.isEmpty() ? new EntityPlayer[0] : (EntityPlayer[]) list.toArray(new EntityPlayer[list.size()]); + } + } else { + return null; + } + } + + public static Map a(Map map) { + HashMap hashmap = new HashMap(); + Iterator iterator = map.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + if (s.startsWith("score_") && s.length() > "score_".length()) { + String s1 = s.substring("score_".length()); + + hashmap.put(s1, Integer.valueOf(MathHelper.a((String) map.get(s), 1))); + } + } + + return hashmap; + } + + public static boolean isList(String s) { + Matcher matcher = a.matcher(s); + + if (matcher.matches()) { + Map map = h(matcher.group(2)); + String s1 = matcher.group(1); + int i = g(s1); + + if (map.containsKey("c")) { + i = MathHelper.a((String) map.get("c"), i); + } + + return i != 1; + } else { + return false; + } + } + + public static boolean isPattern(String s, String s1) { + Matcher matcher = a.matcher(s); + + if (matcher.matches()) { + String s2 = matcher.group(1); + + return s1 == null || s1.equals(s2); + } else { + return false; + } + } + + public static boolean isPattern(String s) { + return isPattern(s, (String) null); + } + + private static final int c(String s) { + return 0; + } + + private static final int d(String s) { + return 0; + } + + private static final int e(String s) { + return Integer.MAX_VALUE; + } + + private static final int f(String s) { + return 0; + } + + private static final int g(String s) { + return s.equals("a") ? 0 : 1; + } + + private static Map h(String s) { + HashMap hashmap = new HashMap(); + + if (s == null) { + return hashmap; + } else { + Matcher matcher = b.matcher(s); + int i = 0; + + int j; + + for (j = -1; matcher.find(); j = matcher.end()) { + String s1 = null; + + switch (i++) { + case 0: + s1 = "x"; + break; + + case 1: + s1 = "y"; + break; + + case 2: + s1 = "z"; + break; + + case 3: + s1 = "r"; + } + + if (s1 != null && matcher.group(1).length() > 0) { + hashmap.put(s1, matcher.group(1)); + } + } + + if (j < s.length()) { + matcher = c.matcher(j == -1 ? s : s.substring(j)); + + while (matcher.find()) { + hashmap.put(matcher.group(1), matcher.group(2)); + } + } + + return hashmap; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PortalCreator.java b/vspigot-server/src/main/java/net/minecraft/server/PortalCreator.java new file mode 100644 index 0000000..309239d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PortalCreator.java @@ -0,0 +1,197 @@ +package net.minecraft.server; + +import org.bukkit.event.world.PortalCreateEvent; // CraftBukkit + +public class PortalCreator { + + private final World a; + private final int b; + private final int c; + private final int d; + private int e = 0; + private ChunkCoordinates f; + private int g; + private int h; + java.util.Collection blocks = new java.util.HashSet(); // CraftBukkit - add field + + public PortalCreator(World world, int i, int j, int k, int l) { + this.a = world; + this.b = l; + this.d = BlockPortal.a[l][0]; + this.c = BlockPortal.a[l][1]; + + for (int i1 = j; j > i1 - 21 && j > 0 && this.a(world.getType(i, j - 1, k)); --j) { + ; + } + + int j1 = this.a(i, j, k, this.d) - 1; + + if (j1 >= 0) { + this.f = new ChunkCoordinates(i + j1 * Direction.a[this.d], j, k + j1 * Direction.b[this.d]); + this.h = this.a(this.f.x, this.f.y, this.f.z, this.c); + if (this.h < 2 || this.h > 21) { + this.f = null; + this.h = 0; + } + } + + if (this.f != null) { + this.g = this.a(); + } + } + + protected int a(int i, int j, int k, int l) { + int i1 = Direction.a[l]; + int j1 = Direction.b[l]; + + int k1; + Block block; + + for (k1 = 0; k1 < 22; ++k1) { + block = this.a.getType(i + i1 * k1, j, k + j1 * k1); + if (!this.a(block)) { + break; + } + + Block block1 = this.a.getType(i + i1 * k1, j - 1, k + j1 * k1); + + if (block1 != Blocks.OBSIDIAN) { + break; + } + } + + block = this.a.getType(i + i1 * k1, j, k + j1 * k1); + return block == Blocks.OBSIDIAN ? k1 : 0; + } + + protected int a() { + // CraftBukkit start + this.blocks.clear(); + org.bukkit.World bworld = this.a.getWorld(); + // CraftBukkit end + int i; + int j; + int k; + int l; + + label56: + for (this.g = 0; this.g < 21; ++this.g) { + i = this.f.y + this.g; + + for (j = 0; j < this.h; ++j) { + k = this.f.x + j * Direction.a[BlockPortal.a[this.b][1]]; + l = this.f.z + j * Direction.b[BlockPortal.a[this.b][1]]; + Block block = this.a.getType(k, i, l); + + if (!this.a(block)) { + break label56; + } + + if (block == Blocks.PORTAL) { + ++this.e; + } + + if (j == 0) { + block = this.a.getType(k + Direction.a[BlockPortal.a[this.b][0]], i, l + Direction.b[BlockPortal.a[this.b][0]]); + if (block != Blocks.OBSIDIAN) { + break label56; + // CraftBukkit start - add the block to our list + } else { + blocks.add(bworld.getBlockAt(k + Direction.a[BlockPortal.a[this.b][0]], i, l + Direction.b[BlockPortal.a[this.b][0]])); + // CraftBukkit end + } + } else if (j == this.h - 1) { + block = this.a.getType(k + Direction.a[BlockPortal.a[this.b][1]], i, l + Direction.b[BlockPortal.a[this.b][1]]); + if (block != Blocks.OBSIDIAN) { + break label56; + // CraftBukkit start - add the block to our list + } else { + blocks.add(bworld.getBlockAt(k + Direction.a[BlockPortal.a[this.b][1]], i, l + Direction.b[BlockPortal.a[this.b][1]])); + // CraftBukkit end + } + } + } + } + + for (i = 0; i < this.h; ++i) { + j = this.f.x + i * Direction.a[BlockPortal.a[this.b][1]]; + k = this.f.y + this.g; + l = this.f.z + i * Direction.b[BlockPortal.a[this.b][1]]; + if (this.a.getType(j, k, l) != Blocks.OBSIDIAN) { + this.g = 0; + break; + // CraftBukkit start - add the block to our list + } else { + blocks.add(bworld.getBlockAt(j, k, l)); + // CraftBukkit end + } + } + + if (this.g <= 21 && this.g >= 3) { + return this.g; + } else { + this.f = null; + this.h = 0; + this.g = 0; + return 0; + } + } + + protected boolean a(Block block) { + return block.material == Material.AIR || block == Blocks.FIRE || block == Blocks.PORTAL; + } + + public boolean b() { + return this.f != null && this.h >= 2 && this.h <= 21 && this.g >= 3 && this.g <= 21; + } + + // CraftBukkit start - return boolean + public boolean c() { + org.bukkit.World bworld = this.a.getWorld(); + + // Copy below for loop + for (int i = 0; i < this.h; ++i) { + int j = this.f.x + Direction.a[this.c] * i; + int k = this.f.z + Direction.b[this.c] * i; + + for (int l = 0; l < this.g; ++l) { + int i1 = this.f.y + l; + + blocks.add(bworld.getBlockAt(j, i1, k)); + } + } + + PortalCreateEvent event = new PortalCreateEvent(blocks, bworld, PortalCreateEvent.CreateReason.FIRE); + this.a.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + + for (int i = 0; i < this.h; ++i) { + int j = this.f.x + Direction.a[this.c] * i; + int k = this.f.z + Direction.b[this.c] * i; + + for (int l = 0; l < this.g; ++l) { + int i1 = this.f.y + l; + + this.a.setTypeAndData(j, i1, k, Blocks.PORTAL, this.b, 2); + } + } + + return true; // CraftBukkit + } + + static int a(PortalCreator portalcreator) { + return portalcreator.e; + } + + static int b(PortalCreator portalcreator) { + return portalcreator.h; + } + + static int c(PortalCreator portalcreator) { + return portalcreator.g; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PortalTravelAgent.java b/vspigot-server/src/main/java/net/minecraft/server/PortalTravelAgent.java new file mode 100644 index 0000000..bff6c69 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PortalTravelAgent.java @@ -0,0 +1,562 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.event.entity.EntityPortalExitEvent; +import org.bukkit.util.Vector; +// CraftBukkit end + +public class PortalTravelAgent { + + private final WorldServer a; + private final Random b; + private final LongHashMap c = new LongHashMap(); + private final List d = new ArrayList(); + + public PortalTravelAgent(WorldServer worldserver) { + this.a = worldserver; + this.b = new Random(worldserver.getSeed()); + } + + public void a(Entity entity, double d0, double d1, double d2, float f) { + if (this.a.worldProvider.dimension != 1) { + if (!this.b(entity, d0, d1, d2, f)) { + this.a(entity); + this.b(entity, d0, d1, d2, f); + } + } else { + // CraftBukkit start - Modularize end portal creation + ChunkCoordinates created = this.createEndPortal(d0, d1, d2); + // Poweruser start + float yaw = entity.yaw; + float pitch = 0.0F; + if(this.a.spigotConfig.useAlternateEndSpawn) { + yaw = this.a.getWorldData().getSpawnYaw(); + pitch = this.a.getWorldData().getSpawnPitch(); + } + entity.setPositionRotation((double) created.x, (double) created.y, (double) created.z, yaw, pitch); + // Poweruser end + entity.motX = entity.motY = entity.motZ = 0.0D; + } + } + + // Split out from original a(Entity, double, double, double, float) method in order to enable being called from createPortal + private ChunkCoordinates createEndPortal(double x, double y, double z) { + if(this.a.spigotConfig.useAlternateEndSpawn) { return this.a.getSpawn(); } // Poweruser + int i = MathHelper.floor(x); + int j = MathHelper.floor(y) - 1; + int k = MathHelper.floor(z); + // CraftBukkit end + byte b0 = 1; + byte b1 = 0; + + for (int l = -2; l <= 2; ++l) { + for (int i1 = -2; i1 <= 2; ++i1) { + for (int j1 = -1; j1 < 3; ++j1) { + int k1 = i + i1 * b0 + l * b1; + int l1 = j + j1; + int i2 = k + i1 * b1 - l * b0; + boolean flag = j1 < 0; + + this.a.setTypeUpdate(k1, l1, i2, flag ? Blocks.OBSIDIAN : Blocks.AIR); + } + } + } + + // CraftBukkit start + return new ChunkCoordinates(i, j, k); + } + + // use logic based on creation to verify end portal + private ChunkCoordinates findEndPortal(ChunkCoordinates portal) { + if(this.a.spigotConfig.useAlternateEndSpawn) { return this.a.getSpawn(); } // Poweruser + int i = portal.x; + int j = portal.y - 1; + int k = portal.z; + byte b0 = 1; + byte b1 = 0; + + for (int l = -2; l <= 2; ++l) { + for (int i1 = -2; i1 <= 2; ++i1) { + for (int j1 = -1; j1 < 3; ++j1) { + int k1 = i + i1 * b0 + l * b1; + int l1 = j + j1; + int i2 = k + i1 * b1 - l * b0; + boolean flag = j1 < 0; + + if (this.a.getType(k1, l1, i2) != (flag ? Blocks.OBSIDIAN : Blocks.AIR)) { + return null; + } + } + } + } + return new ChunkCoordinates(i, j, k); + } + // CraftBukkit end + + public boolean b(Entity entity, double d0, double d1, double d2, float f) { + // CraftBukkit start - Modularize portal search process and entity teleportation + + // Poweruser start - check a small area first + ChunkCoordinates found = this.findPortal(entity.locX, entity.locY, entity.locZ, 10); + if(found == null) { + found = this.findPortal(entity.locX, entity.locY, entity.locZ, 128); + } + // Poweruser end + + if (found == null) { + return false; + } + + Location exit = new Location(this.a.getWorld(), found.x, found.y, found.z, f, entity.pitch); + Vector velocity = entity.getBukkitEntity().getVelocity(); + this.adjustExit(entity, exit, velocity); + entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); + if (entity.motX != velocity.getX() || entity.motY != velocity.getY() || entity.motZ != velocity.getZ()) { + entity.getBukkitEntity().setVelocity(velocity); + } + return true; + } + + public ChunkCoordinates findPortal(double x, double y, double z, int short1) { + if (this.a.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { + return this.findEndPortal(this.a.worldProvider.h()); + } + // CraftBukkit end + double d3 = -1.0D; + int i = 0; + int j = 0; + int k = 0; + // CraftBukkit start + int l = MathHelper.floor(x); + int i1 = MathHelper.floor(z); + // CraftBukkit end + long j1 = ChunkCoordIntPair.a(l, i1); + boolean flag = true; + double d4; + int k1; + + if (this.c.contains(j1)) { + ChunkCoordinatesPortal chunkcoordinatesportal = (ChunkCoordinatesPortal) this.c.getEntry(j1); + + d3 = 0.0D; + i = chunkcoordinatesportal.x; + j = chunkcoordinatesportal.y; + k = chunkcoordinatesportal.z; + chunkcoordinatesportal.d = this.a.getTime(); + flag = false; + } else { + // Poweruser start + int zOffset = 0, yOffset = 0; + for (k1 = l - short1; k1 <= l + short1; ++k1) { + zOffset = (zOffset + 1) % 2; + for (int l1 = i1 - short1 + zOffset; l1 <= i1 + short1; l1 = l1 + 2) { // skipping every 2nd block in z direction and alternating from row to row in x direction + yOffset = (yOffset + 1) % 3; + for (int i2 = this.a.S() - (1 + yOffset) ; i2 >= 0; i2 = i2 - 3) { // checking only every 3rd block in y direction and alternating in high in each column + // Poweruser end + if (this.a.getType(k1, i2, l1) == Blocks.PORTAL) { + while (this.a.getType(k1, i2 - 1, l1) == Blocks.PORTAL) { + --i2; + } + + d4 = (double) i2 + 0.5D - y; // CraftBukkit + // Poweruser start + double d5 = (double) k1 + 0.5D - x; // CraftBukkit + double d6 = (double) l1 + 0.5D - z; // CraftBukkit + // Poweruser end + double d7 = d5 * d5 + d4 * d4 + d6 * d6; + + if (d3 < 0.0D || d7 < d3) { + d3 = d7; + i = k1; + j = i2; + k = l1; + } + } + } + } + } + } + + if (d3 >= 0.0D) { + if (flag) { + this.c.put(j1, new ChunkCoordinatesPortal(this, i, j, k, this.a.getTime())); + this.d.add(Long.valueOf(j1)); + } + // CraftBukkit start - Moved entity teleportation logic into exit + return new ChunkCoordinates(i, j, k); + } else { + return null; + } + } + // Entity repositioning logic split out from original b method and combined with repositioning logic for The End from original a method + public void adjustExit(Entity entity, Location position, Vector velocity) { + Location from = position.clone(); + Vector before = velocity.clone(); + int i = position.getBlockX(); + int j = position.getBlockY(); + int k = position.getBlockZ(); + float f = position.getYaw(); + + if (this.a.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { + // entity.setPositionRotation((double) i, (double) j, (double) k, entity.yaw, 0.0F); + // entity.motX = entity.motY = entity.motZ = 0.0D; + // Poweruser start + float pitch = 0.0F; + if(this.a.spigotConfig.useAlternateEndSpawn) { + pitch = this.a.getWorldData().getSpawnPitch(); + position.setYaw(this.a.getWorldData().getSpawnYaw()); + } + position.setPitch(pitch); + // Poweruser end + velocity.setX(0); + velocity.setY(0); + velocity.setZ(0); + } else { + double d4; + int k1; + // CraftBukkit end + + double d8 = (double) i + 0.5D; + double d9 = (double) j + 0.5D; + + d4 = (double) k + 0.5D; + int j2 = -1; + + if (this.a.getType(i - 1, j, k) == Blocks.PORTAL) { + j2 = 2; + } + + if (this.a.getType(i + 1, j, k) == Blocks.PORTAL) { + j2 = 0; + } + + if (this.a.getType(i, j, k - 1) == Blocks.PORTAL) { + j2 = 3; + } + + if (this.a.getType(i, j, k + 1) == Blocks.PORTAL) { + j2 = 1; + } + + int k2 = entity.ay(); + + if (j2 > -1) { + int l2 = Direction.h[j2]; + int i3 = Direction.a[j2]; + int j3 = Direction.b[j2]; + int k3 = Direction.a[l2]; + int l3 = Direction.b[l2]; + boolean flag1 = !this.a.isEmpty(i + i3 + k3, j, k + j3 + l3) || !this.a.isEmpty(i + i3 + k3, j + 1, k + j3 + l3); + boolean flag2 = !this.a.isEmpty(i + i3, j, k + j3) || !this.a.isEmpty(i + i3, j + 1, k + j3); + + if (flag1 && flag2) { + j2 = Direction.f[j2]; + l2 = Direction.f[l2]; + i3 = Direction.a[j2]; + j3 = Direction.b[j2]; + k3 = Direction.a[l2]; + l3 = Direction.b[l2]; + k1 = i - k3; + d8 -= (double) k3; + int i4 = k - l3; + + d4 -= (double) l3; + flag1 = !this.a.isEmpty(k1 + i3 + k3, j, i4 + j3 + l3) || !this.a.isEmpty(k1 + i3 + k3, j + 1, i4 + j3 + l3); + flag2 = !this.a.isEmpty(k1 + i3, j, i4 + j3) || !this.a.isEmpty(k1 + i3, j + 1, i4 + j3); + } + + float f1 = 0.5F; + float f2 = 0.5F; + + if (!flag1 && flag2) { + f1 = 1.0F; + } else if (flag1 && !flag2) { + f1 = 0.0F; + } else if (flag1 && flag2) { + f2 = 0.0F; + } + + d8 += (double) ((float) k3 * f1 + f2 * (float) i3); + d4 += (double) ((float) l3 * f1 + f2 * (float) j3); + float f3 = 0.0F; + float f4 = 0.0F; + float f5 = 0.0F; + float f6 = 0.0F; + + if (j2 == k2) { + f3 = 1.0F; + f4 = 1.0F; + } else if (j2 == Direction.f[k2]) { + f3 = -1.0F; + f4 = -1.0F; + } else if (j2 == Direction.g[k2]) { + f5 = 1.0F; + f6 = -1.0F; + } else { + f5 = -1.0F; + f6 = 1.0F; + } + + // CraftBukkit start + double d10 = velocity.getX(); + double d11 = velocity.getZ(); + // CraftBukkit end + + // CraftBukkit start - Adjust position and velocity instances instead of entity + velocity.setX(d10 * (double) f3 + d11 * (double) f6); + velocity.setZ(d10 * (double) f5 + d11 * (double) f4); + f = f - (float) (k2 * 90) + (float) (j2 * 90); + } else { + // entity.motX = entity.motY = entity.motZ = 0.0D; + velocity.setX(0); + velocity.setY(0); + velocity.setZ(0); + } + + // entity.setPositionRotation(d8, d9, d4, entity.yaw, entity.pitch); + position.setX(d8); + position.setY(d9); + position.setZ(d4); + position.setYaw(f); + } + + EntityPortalExitEvent event = new EntityPortalExitEvent(entity.getBukkitEntity(), from, position, before, velocity); + this.a.getServer().getPluginManager().callEvent(event); + Location to = event.getTo(); + if (event.isCancelled() || to == null || !entity.isAlive()) { + position.setX(from.getX()); + position.setY(from.getY()); + position.setZ(from.getZ()); + position.setYaw(from.getYaw()); + position.setPitch(from.getPitch()); + velocity.copy(before); + } else { + position.setX(to.getX()); + position.setY(to.getY()); + position.setZ(to.getZ()); + position.setYaw(to.getYaw()); + position.setPitch(to.getPitch()); + velocity.copy(event.getAfter()); // event.getAfter() will never be null, as setAfter() will cause an NPE if null is passed in + } + // CraftBukkit end + } + + public boolean a(Entity entity) { + // CraftBukkit start - Allow for portal creation to be based on coordinates instead of entity + return this.createPortal(entity.locX, entity.locY, entity.locZ, 16); + } + + public boolean createPortal(double x, double y, double z, int b0) { + if (this.a.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { + this.createEndPortal(x, y, z); + return true; + } + // CraftBukkit end + double d0 = -1.0D; + // CraftBukkit start + int i = MathHelper.floor(x); + int j = MathHelper.floor(y); + int k = MathHelper.floor(z); + // CraftBukkit end + int l = i; + int i1 = j; + int j1 = k; + int k1 = 0; + int l1 = this.b.nextInt(4); + + int i2; + double d1; + double d2; + int j2; + int k2; + int l2; + int i3; + int j3; + int k3; + int l3; + int i4; + int j4; + int k4; + double d3; + double d4; + + for (i2 = i - b0; i2 <= i + b0; ++i2) { + d1 = (double) i2 + 0.5D - x; // CraftBukkit + + for (j2 = k - b0; j2 <= k + b0; ++j2) { + d2 = (double) j2 + 0.5D - z; // CraftBukkit + + label274: + for (k2 = this.a.S() - 1; k2 >= 0; --k2) { + if (this.a.isEmpty(i2, k2, j2)) { + while (k2 > 0 && this.a.isEmpty(i2, k2 - 1, j2)) { + --k2; + } + + for (i3 = l1; i3 < l1 + 4; ++i3) { + l2 = i3 % 2; + k3 = 1 - l2; + if (i3 % 4 >= 2) { + l2 = -l2; + k3 = -k3; + } + + for (j3 = 0; j3 < 3; ++j3) { + for (i4 = 0; i4 < 4; ++i4) { + for (l3 = -1; l3 < 4; ++l3) { + k4 = i2 + (i4 - 1) * l2 + j3 * k3; + j4 = k2 + l3; + int l4 = j2 + (i4 - 1) * k3 - j3 * l2; + + if (l3 < 0 && !this.a.getType(k4, j4, l4).getMaterial().isBuildable() || l3 >= 0 && !this.a.isEmpty(k4, j4, l4)) { + continue label274; + } + } + } + } + + d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; + if (d0 < 0.0D || d4 < d0) { + d0 = d4; + l = i2; + i1 = k2; + j1 = j2; + k1 = i3 % 4; + } + } + } + } + } + } + + if (d0 < 0.0D) { + for (i2 = i - b0; i2 <= i + b0; ++i2) { + d1 = (double) i2 + 0.5D - x; // CraftBukkit + + for (j2 = k - b0; j2 <= k + b0; ++j2) { + d2 = (double) j2 + 0.5D - z; // CraftBukkit + + label222: + for (k2 = this.a.S() - 1; k2 >= 0; --k2) { + if (this.a.isEmpty(i2, k2, j2)) { + while (k2 > 0 && this.a.isEmpty(i2, k2 - 1, j2)) { + --k2; + } + + for (i3 = l1; i3 < l1 + 2; ++i3) { + l2 = i3 % 2; + k3 = 1 - l2; + + for (j3 = 0; j3 < 4; ++j3) { + for (i4 = -1; i4 < 4; ++i4) { + l3 = i2 + (j3 - 1) * l2; + k4 = k2 + i4; + j4 = j2 + (j3 - 1) * k3; + if (i4 < 0 && !this.a.getType(l3, k4, j4).getMaterial().isBuildable() || i4 >= 0 && !this.a.isEmpty(l3, k4, j4)) { + continue label222; + } + } + } + + d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; + if (d0 < 0.0D || d4 < d0) { + d0 = d4; + l = i2; + i1 = k2; + j1 = j2; + k1 = i3 % 2; + } + } + } + } + } + } + } + + int i5 = l; + int j5 = i1; + + j2 = j1; + int k5 = k1 % 2; + int l5 = 1 - k5; + + if (k1 % 4 >= 2) { + k5 = -k5; + l5 = -l5; + } + + boolean flag; + + if (d0 < 0.0D) { + if (i1 < 70) { + i1 = 70; + } + + if (i1 > this.a.S() - 10) { + i1 = this.a.S() - 10; + } + + j5 = i1; + + for (k2 = -1; k2 <= 1; ++k2) { + for (i3 = 1; i3 < 3; ++i3) { + for (l2 = -1; l2 < 3; ++l2) { + k3 = i5 + (i3 - 1) * k5 + k2 * l5; + j3 = j5 + l2; + i4 = j2 + (i3 - 1) * l5 - k2 * k5; + flag = l2 < 0; + this.a.setTypeUpdate(k3, j3, i4, flag ? Blocks.OBSIDIAN : Blocks.AIR); + } + } + } + } + + for (k2 = 0; k2 < 4; ++k2) { + for (i3 = 0; i3 < 4; ++i3) { + for (l2 = -1; l2 < 4; ++l2) { + k3 = i5 + (i3 - 1) * k5; + j3 = j5 + l2; + i4 = j2 + (i3 - 1) * l5; + flag = i3 == 0 || i3 == 3 || l2 == -1 || l2 == 3; + this.a.setTypeAndData(k3, j3, i4, flag ? Blocks.OBSIDIAN : Blocks.PORTAL, 0, 2); + } + } + + for (i3 = 0; i3 < 4; ++i3) { + for (l2 = -1; l2 < 4; ++l2) { + k3 = i5 + (i3 - 1) * k5; + j3 = j5 + l2; + i4 = j2 + (i3 - 1) * l5; + this.a.applyPhysics(k3, j3, i4, this.a.getType(k3, j3, i4)); + } + } + } + + return true; + } + + public void a(long i) { + if (i % 100L == 0L) { + Iterator iterator = this.d.iterator(); + long j = i - 600L; + + while (iterator.hasNext()) { + Long olong = (Long) iterator.next(); + ChunkCoordinatesPortal chunkcoordinatesportal = (ChunkCoordinatesPortal) this.c.getEntry(olong.longValue()); + + if (chunkcoordinatesportal == null || chunkcoordinatesportal.d < j) { + iterator.remove(); + this.c.remove(olong.longValue()); + } + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/PropertyManager.java b/vspigot-server/src/main/java/net/minecraft/server/PropertyManager.java new file mode 100644 index 0000000..21cae77 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/PropertyManager.java @@ -0,0 +1,132 @@ +package net.minecraft.server; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.spigotmc.SpigotConfig; + +import joptsimple.OptionSet; // CraftBukkit + +public class PropertyManager { + + private static final Logger loggingAgent = LogManager.getLogger(); + public final Properties properties = new Properties(); // CraftBukkit - private -> public + private final File c; + + public PropertyManager(File file1) { + this.c = file1; + if (file1.exists()) { + FileInputStream fileinputstream = null; + + try { + fileinputstream = new FileInputStream(file1); + this.properties.load(fileinputstream); + } catch (Exception exception) { + loggingAgent.warn("Failed to load " + file1, exception); + this.a(); + } finally { + if (fileinputstream != null) { + try { + fileinputstream.close(); + } catch (IOException ioexception) { + ; + } + } + } + } else { + loggingAgent.warn(file1 + " does not exist"); + this.a(); + } + } + + // CraftBukkit start + private OptionSet options = null; + + public PropertyManager(final OptionSet options) { + this((File) options.valueOf("config")); + + this.options = options; + } + + private T getOverride(String name, T value) { + if ((this.options != null) && (this.options.has(name)) && !name.equals( "online-mode")) { // Spigot + return (T) this.options.valueOf(name); + } + + return value; + } + // CraftBukkit end + + public void a() { + loggingAgent.info("Generating new properties file"); + this.savePropertiesFile(); + } + + public void savePropertiesFile() { + if (SpigotConfig.disableSaving) return; // MineHQ + FileOutputStream fileoutputstream = null; + + try { + // CraftBukkit start - Don't attempt writing to file if it's read only + if (this.c.exists() && !this.c.canWrite()) { + return; + } + // CraftBukkit end + fileoutputstream = new FileOutputStream(this.c); + this.properties.store(fileoutputstream, "Minecraft server properties"); + } catch (Exception exception) { + loggingAgent.warn("Failed to save " + this.c, exception); + this.a(); + } finally { + if (fileoutputstream != null) { + try { + fileoutputstream.close(); + } catch (IOException ioexception) { + ; + } + } + } + } + + public File c() { + return this.c; + } + + public String getString(String s, String s1) { + if (!this.properties.containsKey(s)) { + this.properties.setProperty(s, s1); + this.savePropertiesFile(); + this.savePropertiesFile(); + } + + return this.getOverride(s, this.properties.getProperty(s, s1)); // CraftBukkit + } + + public int getInt(String s, int i) { + try { + return this.getOverride(s, Integer.parseInt(this.getString(s, "" + i))); // CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + i); + this.savePropertiesFile(); + return this.getOverride(s, i); // CraftBukkit + } + } + + public boolean getBoolean(String s, boolean flag) { + try { + return this.getOverride(s, Boolean.parseBoolean(this.getString(s, "" + flag))); // CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + flag); + this.savePropertiesFile(); + return this.getOverride(s, flag); // CraftBukkit + } + } + + public void setProperty(String s, Object object) { + this.properties.setProperty(s, "" + object); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ProtocolOrdinalWrapper.java b/vspigot-server/src/main/java/net/minecraft/server/ProtocolOrdinalWrapper.java new file mode 100644 index 0000000..d6e7f36 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ProtocolOrdinalWrapper.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +// CraftBukkit - import package private class +class ProtocolOrdinalWrapper { + + static final int[] a = new int[EnumProtocol.values().length]; + + static { + try { + a[EnumProtocol.LOGIN.ordinal()] = 1; + } catch (NoSuchFieldError nosuchfielderror) { + ; + } + + try { + a[EnumProtocol.STATUS.ordinal()] = 2; + } catch (NoSuchFieldError nosuchfielderror1) { + ; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/QueuedPacket.java b/vspigot-server/src/main/java/net/minecraft/server/QueuedPacket.java new file mode 100644 index 0000000..fdebf9d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/QueuedPacket.java @@ -0,0 +1,23 @@ +package net.minecraft.server; + +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; + +// CraftBukkit - imported class because the methods are package private +class QueuedPacket { + + private final Packet a; + private final GenericFutureListener[] b; + + public QueuedPacket(Packet packet, GenericFutureListener... agenericfuturelistener) { + this.a = packet; + this.b = agenericfuturelistener; + } + + static Packet a(QueuedPacket queuedpacket) { + return queuedpacket.a; + } + + static GenericFutureListener[] b(QueuedPacket queuedpacket) { + return queuedpacket.b; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/QueuedProtocolSwitch.java b/vspigot-server/src/main/java/net/minecraft/server/QueuedProtocolSwitch.java new file mode 100644 index 0000000..5af7946 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/QueuedProtocolSwitch.java @@ -0,0 +1,40 @@ +package net.minecraft.server; + +import net.minecraft.util.io.netty.channel.ChannelFutureListener; +import net.minecraft.util.io.netty.channel.ChannelPromise; // Poweruser +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; + +public class QueuedProtocolSwitch implements Runnable { // Poweruser - public + + final EnumProtocol a; + final EnumProtocol b; + final Packet c; + final GenericFutureListener[] d; + final NetworkManager e; + + QueuedProtocolSwitch(NetworkManager networkmanager, EnumProtocol enumprotocol, EnumProtocol enumprotocol1, Packet packet, GenericFutureListener[] agenericfuturelistener) { + this.e = networkmanager; + this.a = enumprotocol; + this.b = enumprotocol1; + this.c = packet; + this.d = agenericfuturelistener; + } + + public void run() { + // Poweruser start + execute(this.e, this.a, this.b, this.c, this.d); + } + + public static void execute(NetworkManager networkmanager, EnumProtocol enumprotocol, EnumProtocol enumprotocol1, Packet packet, GenericFutureListener[] agenericfuturelistener) { + if (enumprotocol != enumprotocol1) { + networkmanager.a(enumprotocol); + } + + if(agenericfuturelistener == null || agenericfuturelistener.length == 0) { + NetworkManager.a(networkmanager).writeAndFlush(packet, NetworkManager.a(networkmanager).voidPromise()); + } else { + NetworkManager.a(networkmanager).writeAndFlush(packet).addListeners(agenericfuturelistener).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + } + } + // Poweruser end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RandomPositionGenerator.java b/vspigot-server/src/main/java/net/minecraft/server/RandomPositionGenerator.java new file mode 100644 index 0000000..fa20a7d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RandomPositionGenerator.java @@ -0,0 +1,81 @@ +package net.minecraft.server; + +import java.util.Random; + +public class RandomPositionGenerator { + + private static Vec3D a = Vec3D.a(0.0D, 0.0D, 0.0D); + + public static Vec3D a(EntityCreature entitycreature, int i, int j) { + return c(entitycreature, i, j, (Vec3D) null); + } + + public static Vec3D a(EntityCreature entitycreature, int i, int j, Vec3D vec3d) { + a.a = vec3d.a - entitycreature.locX; + a.b = vec3d.b - entitycreature.locY; + a.c = vec3d.c - entitycreature.locZ; + return c(entitycreature, i, j, a); + } + + public static Vec3D b(EntityCreature entitycreature, int i, int j, Vec3D vec3d) { + a.a = entitycreature.locX - vec3d.a; + a.b = entitycreature.locY - vec3d.b; + a.c = entitycreature.locZ - vec3d.c; + return c(entitycreature, i, j, a); + } + + private static Vec3D c(EntityCreature entitycreature, int i, int j, Vec3D vec3d) { + Random random = entitycreature.aI(); + boolean flag = false; + // PaperSpigot start - int -> double + double k = 0; + double l = 0; + double i1 = 0; + // PaperSpigot end + float f = -99999.0F; + boolean flag1; + + if (entitycreature.bY()) { + double d0 = (double) (entitycreature.bV().e(MathHelper.floor(entitycreature.locX), MathHelper.floor(entitycreature.locY), MathHelper.floor(entitycreature.locZ)) + 4.0F); + double d1 = (double) (entitycreature.bW() + (float) i); + + flag1 = d0 < d1 * d1; + } else { + flag1 = false; + } + + for (int j1 = 0; j1 < 10; ++j1) { + // PaperSpigot start - Even distribution and average of 0 + int k1 = random.nextInt(2 * i + 1) - i; + int l1 = random.nextInt(2 * j + 1) - j; + int i2 = random.nextInt(2 * i + 1) - i; + // PaperSpigot end + + if (vec3d == null || (double) k1 * vec3d.a + (double) i2 * vec3d.c >= 0.0D) { + // PaperSpigot start - Use truncated absolute destination position for checking things + int k1Mod = k1 + MathHelper.floor(entitycreature.locX); + int l1Mod = l1 + MathHelper.floor(entitycreature.locY); + int i2Mod = i2 + MathHelper.floor(entitycreature.locZ); + if (!flag1 || entitycreature.b(k1Mod, l1Mod, i2Mod)) { + float f1 = entitycreature.a(k1Mod, l1Mod, i2Mod); + + if (f1 > f) { + f = f1; + // but then the full value to set where to move + k = entitycreature.locX + k1; + l = entitycreature.locY + l1; + i1 = entitycreature.locZ + i2; + // PaperSpigot end + flag = true; + } + } + } + } + + if (flag) { + return Vec3D.a(k, l, i1); // PaperSpigot remove unnecessary cast + } else { + return null; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RecipeArmorDye.java b/vspigot-server/src/main/java/net/minecraft/server/RecipeArmorDye.java new file mode 100644 index 0000000..13bb708 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RecipeArmorDye.java @@ -0,0 +1,124 @@ +package net.minecraft.server; + +import java.util.ArrayList; + +public class RecipeArmorDye extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeArmorDye() { + super(new ItemStack(Items.LEATHER_HELMET, 0, 0), java.util.Arrays.asList(new ItemStack(Items.INK_SACK, 0, 5))); + } + // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + ItemStack itemstack = null; + ArrayList arraylist = new ArrayList(); + + for (int i = 0; i < inventorycrafting.getSize(); ++i) { + ItemStack itemstack1 = inventorycrafting.getItem(i); + + if (itemstack1 != null) { + if (itemstack1.getItem() instanceof ItemArmor) { + ItemArmor itemarmor = (ItemArmor) itemstack1.getItem(); + + if (itemarmor.m_() != EnumArmorMaterial.CLOTH || itemstack != null) { + return false; + } + + itemstack = itemstack1; + } else { + if (itemstack1.getItem() != Items.INK_SACK) { + return false; + } + + arraylist.add(itemstack1); + } + } + } + + return itemstack != null && !arraylist.isEmpty(); + } + + public ItemStack a(InventoryCrafting inventorycrafting) { + ItemStack itemstack = null; + int[] aint = new int[3]; + int i = 0; + int j = 0; + ItemArmor itemarmor = null; + + int k; + int l; + float f; + float f1; + int i1; + + for (k = 0; k < inventorycrafting.getSize(); ++k) { + ItemStack itemstack1 = inventorycrafting.getItem(k); + + if (itemstack1 != null) { + if (itemstack1.getItem() instanceof ItemArmor) { + itemarmor = (ItemArmor) itemstack1.getItem(); + if (itemarmor.m_() != EnumArmorMaterial.CLOTH || itemstack != null) { + return null; + } + + itemstack = itemstack1.cloneItemStack(); + itemstack.count = 1; + if (itemarmor.c_(itemstack1)) { + l = itemarmor.b(itemstack); + f = (float) (l >> 16 & 255) / 255.0F; + f1 = (float) (l >> 8 & 255) / 255.0F; + float f2 = (float) (l & 255) / 255.0F; + + i = (int) ((float) i + Math.max(f, Math.max(f1, f2)) * 255.0F); + aint[0] = (int) ((float) aint[0] + f * 255.0F); + aint[1] = (int) ((float) aint[1] + f1 * 255.0F); + aint[2] = (int) ((float) aint[2] + f2 * 255.0F); + ++j; + } + } else { + if (itemstack1.getItem() != Items.INK_SACK) { + return null; + } + + float[] afloat = EntitySheep.bp[BlockCloth.b(itemstack1.getData())]; + int j1 = (int) (afloat[0] * 255.0F); + int k1 = (int) (afloat[1] * 255.0F); + + i1 = (int) (afloat[2] * 255.0F); + i += Math.max(j1, Math.max(k1, i1)); + aint[0] += j1; + aint[1] += k1; + aint[2] += i1; + ++j; + } + } + } + + if (itemarmor == null) { + return null; + } else { + k = aint[0] / j; + int l1 = aint[1] / j; + + l = aint[2] / j; + f = (float) i / (float) j; + f1 = (float) Math.max(k, Math.max(l1, l)); + k = (int) ((float) k * f / f1); + l1 = (int) ((float) l1 * f / f1); + l = (int) ((float) l * f / f1); + i1 = (k << 8) + l1; + i1 = (i1 << 8) + l; + itemarmor.b(itemstack, i1); + return itemstack; + } + } + + public int a() { + return 10; + } + + public ItemStack b() { + return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RecipeBookClone.java b/vspigot-server/src/main/java/net/minecraft/server/RecipeBookClone.java new file mode 100644 index 0000000..8182a5f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RecipeBookClone.java @@ -0,0 +1,68 @@ +package net.minecraft.server; + +public class RecipeBookClone extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class + public RecipeBookClone() { + super(new ItemStack(Items.WRITTEN_BOOK, 0, -1), java.util.Arrays.asList(new ItemStack(Items.BOOK_AND_QUILL, 0, 0))); + } + // CraftBukkit end + + public boolean a(InventoryCrafting inventoryCrafting, World paramWorld) { + int i = 0; + ItemStack itemStack = null; + for (int j = 0; j < inventoryCrafting.getSize(); j++) { + ItemStack itemStack1 = inventoryCrafting.getItem(j); + if (itemStack1 != null) { + if (itemStack1.getItem() == Items.WRITTEN_BOOK) { + if (itemStack != null) { + return false; + } + itemStack = itemStack1; + } else if (itemStack1.getItem() == Items.BOOK_AND_QUILL) { + i++; + } else { + return false; + } + } + } + return (itemStack != null) && (i > 0); + } + + public ItemStack a(InventoryCrafting inventoryCrafting) { + int i = 0; + ItemStack itemStack = null; + for (int j = 0; j < inventoryCrafting.getSize(); j++) { + ItemStack itemStack2 = inventoryCrafting.getItem(j); + if (itemStack2 != null) { + if (itemStack2.getItem() == Items.WRITTEN_BOOK) { + if (itemStack != null) { + return null; + } + itemStack = itemStack2; + } else if (itemStack2.getItem() == Items.BOOK_AND_QUILL) { + i++; + } else { + return null; + } + } + } + if ((itemStack == null) || (i < 1)) { + return null; + } + ItemStack itemStack1 = new ItemStack(Items.WRITTEN_BOOK, i + 1); + itemStack1.setTag((NBTTagCompound) itemStack.getTag().clone()); + if (itemStack.hasName()) { + itemStack1.c(itemStack.getName()); + } + return itemStack1; + } + + public int a() { + return 9; + } + + public ItemStack b() { + return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RecipeFireworks.java b/vspigot-server/src/main/java/net/minecraft/server/RecipeFireworks.java new file mode 100644 index 0000000..816df8c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RecipeFireworks.java @@ -0,0 +1,176 @@ +package net.minecraft.server; + +import java.util.ArrayList; + +public class RecipeFireworks extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + private ItemStack a; + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeFireworks() { + super(new ItemStack(Items.FIREWORKS, 0, 0), java.util.Arrays.asList(new ItemStack(Items.SULPHUR, 0, 5))); + } + // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + this.a = null; + int i = 0; + int j = 0; + int k = 0; + int l = 0; + int i1 = 0; + int j1 = 0; + + for (int k1 = 0; k1 < inventorycrafting.getSize(); ++k1) { + ItemStack itemstack = inventorycrafting.getItem(k1); + + if (itemstack != null) { + if (itemstack.getItem() == Items.SULPHUR) { + ++j; + } else if (itemstack.getItem() == Items.FIREWORKS_CHARGE) { + ++l; + } else if (itemstack.getItem() == Items.INK_SACK) { + ++k; + } else if (itemstack.getItem() == Items.PAPER) { + ++i; + } else if (itemstack.getItem() == Items.GLOWSTONE_DUST) { + ++i1; + } else if (itemstack.getItem() == Items.DIAMOND) { + ++i1; + } else if (itemstack.getItem() == Items.FIREBALL) { + ++j1; + } else if (itemstack.getItem() == Items.FEATHER) { + ++j1; + } else if (itemstack.getItem() == Items.GOLD_NUGGET) { + ++j1; + } else { + if (itemstack.getItem() != Items.SKULL) { + return false; + } + + ++j1; + } + } + } + + i1 += k + j1; + if (j <= 3 && i <= 1) { + NBTTagCompound nbttagcompound; + NBTTagCompound nbttagcompound1; + + if (j >= 1 && i == 1 && i1 == 0) { + this.a = new ItemStack(Items.FIREWORKS); + if (l > 0) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound1 = new NBTTagCompound(); + NBTTagList nbttaglist = new NBTTagList(); + + for (int l1 = 0; l1 < inventorycrafting.getSize(); ++l1) { + ItemStack itemstack1 = inventorycrafting.getItem(l1); + + if (itemstack1 != null && itemstack1.getItem() == Items.FIREWORKS_CHARGE && itemstack1.hasTag() && itemstack1.getTag().hasKeyOfType("Explosion", 10)) { + nbttaglist.add(itemstack1.getTag().getCompound("Explosion")); + } + } + + nbttagcompound1.set("Explosions", nbttaglist); + nbttagcompound1.setByte("Flight", (byte) j); + nbttagcompound.set("Fireworks", nbttagcompound1); + this.a.setTag(nbttagcompound); + } + + return true; + } else if (j == 1 && i == 0 && l == 0 && k > 0 && j1 <= 1) { + this.a = new ItemStack(Items.FIREWORKS_CHARGE); + nbttagcompound = new NBTTagCompound(); + nbttagcompound1 = new NBTTagCompound(); + byte b0 = 0; + ArrayList arraylist = new ArrayList(); + + for (int i2 = 0; i2 < inventorycrafting.getSize(); ++i2) { + ItemStack itemstack2 = inventorycrafting.getItem(i2); + + if (itemstack2 != null) { + if (itemstack2.getItem() == Items.INK_SACK) { + arraylist.add(Integer.valueOf(ItemDye.c[itemstack2.getData()])); + } else if (itemstack2.getItem() == Items.GLOWSTONE_DUST) { + nbttagcompound1.setBoolean("Flicker", true); + } else if (itemstack2.getItem() == Items.DIAMOND) { + nbttagcompound1.setBoolean("Trail", true); + } else if (itemstack2.getItem() == Items.FIREBALL) { + b0 = 1; + } else if (itemstack2.getItem() == Items.FEATHER) { + b0 = 4; + } else if (itemstack2.getItem() == Items.GOLD_NUGGET) { + b0 = 2; + } else if (itemstack2.getItem() == Items.SKULL) { + b0 = 3; + } + } + } + + int[] aint = new int[arraylist.size()]; + + for (int j2 = 0; j2 < aint.length; ++j2) { + aint[j2] = ((Integer) arraylist.get(j2)).intValue(); + } + + nbttagcompound1.setIntArray("Colors", aint); + nbttagcompound1.setByte("Type", b0); + nbttagcompound.set("Explosion", nbttagcompound1); + this.a.setTag(nbttagcompound); + return true; + } else if (j == 0 && i == 0 && l == 1 && k > 0 && k == i1) { + ArrayList arraylist1 = new ArrayList(); + + for (int k2 = 0; k2 < inventorycrafting.getSize(); ++k2) { + ItemStack itemstack3 = inventorycrafting.getItem(k2); + + if (itemstack3 != null) { + if (itemstack3.getItem() == Items.INK_SACK) { + arraylist1.add(Integer.valueOf(ItemDye.c[itemstack3.getData()])); + } else if (itemstack3.getItem() == Items.FIREWORKS_CHARGE) { + this.a = itemstack3.cloneItemStack(); + this.a.count = 1; + } + } + } + + int[] aint1 = new int[arraylist1.size()]; + + for (int l2 = 0; l2 < aint1.length; ++l2) { + aint1[l2] = ((Integer) arraylist1.get(l2)).intValue(); + } + + if (this.a != null && this.a.hasTag()) { + NBTTagCompound nbttagcompound2 = this.a.getTag().getCompound("Explosion"); + + if (nbttagcompound2 == null) { + return false; + } else { + nbttagcompound2.setIntArray("FadeColors", aint1); + return true; + } + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } + } + + public ItemStack a(InventoryCrafting inventorycrafting) { + return this.a.cloneItemStack(); + } + + public int a() { + return 10; + } + + public ItemStack b() { + return this.a; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RecipeMapClone.java b/vspigot-server/src/main/java/net/minecraft/server/RecipeMapClone.java new file mode 100644 index 0000000..793883f --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RecipeMapClone.java @@ -0,0 +1,82 @@ +package net.minecraft.server; + +public class RecipeMapClone extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class + public RecipeMapClone() { + super(new ItemStack(Items.MAP, 0, -1), java.util.Arrays.asList(new ItemStack(Items.MAP_EMPTY, 0, 0))); + } + // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + int i = 0; + ItemStack itemstack = null; + + for (int j = 0; j < inventorycrafting.getSize(); ++j) { + ItemStack itemstack1 = inventorycrafting.getItem(j); + + if (itemstack1 != null) { + if (itemstack1.getItem() == Items.MAP) { + if (itemstack != null) { + return false; + } + + itemstack = itemstack1; + } else { + if (itemstack1.getItem() != Items.MAP_EMPTY) { + return false; + } + + ++i; + } + } + } + + return itemstack != null && i > 0; + } + + public ItemStack a(InventoryCrafting inventorycrafting) { + int i = 0; + ItemStack itemstack = null; + + for (int j = 0; j < inventorycrafting.getSize(); ++j) { + ItemStack itemstack1 = inventorycrafting.getItem(j); + + if (itemstack1 != null) { + if (itemstack1.getItem() == Items.MAP) { + if (itemstack != null) { + return null; + } + + itemstack = itemstack1; + } else { + if (itemstack1.getItem() != Items.MAP_EMPTY) { + return null; + } + + ++i; + } + } + } + + if (itemstack != null && i >= 1) { + ItemStack itemstack2 = new ItemStack(Items.MAP, i + 1, itemstack.getData()); + + if (itemstack.hasName()) { + itemstack2.c(itemstack.getName()); + } + + return itemstack2; + } else { + return null; + } + } + + public int a() { + return 9; + } + + public ItemStack b() { + return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RecipesFurnace.java b/vspigot-server/src/main/java/net/minecraft/server/RecipesFurnace.java new file mode 100644 index 0000000..23a1446 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RecipesFurnace.java @@ -0,0 +1,122 @@ +package net.minecraft.server; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +public class RecipesFurnace { + + private static final RecipesFurnace a = new RecipesFurnace(); + public Map recipes = new HashMap(); // CraftBukkit - private -> public + private Map c = new HashMap(); + public Map customRecipes = new HashMap(); // CraftBukkit - add field + + public static RecipesFurnace getInstance() { + return a; + } + + public RecipesFurnace() { // CraftBukkit - private -> public + this.registerRecipe(Blocks.IRON_ORE, new ItemStack(Items.IRON_INGOT), 0.7F); + this.registerRecipe(Blocks.GOLD_ORE, new ItemStack(Items.GOLD_INGOT), 1.0F); + this.registerRecipe(Blocks.DIAMOND_ORE, new ItemStack(Items.DIAMOND), 1.0F); + this.registerRecipe(Blocks.SAND, new ItemStack(Blocks.GLASS), 0.1F); + this.a(Items.PORK, new ItemStack(Items.GRILLED_PORK), 0.35F); + this.a(Items.RAW_BEEF, new ItemStack(Items.COOKED_BEEF), 0.35F); + this.a(Items.RAW_CHICKEN, new ItemStack(Items.COOKED_CHICKEN), 0.35F); + this.registerRecipe(Blocks.COBBLESTONE, new ItemStack(Blocks.STONE), 0.1F); + this.a(Items.CLAY_BALL, new ItemStack(Items.CLAY_BRICK), 0.3F); + this.registerRecipe(Blocks.CLAY, new ItemStack(Blocks.HARDENED_CLAY), 0.35F); + this.registerRecipe(Blocks.CACTUS, new ItemStack(Items.INK_SACK, 1, 2), 0.2F); + this.registerRecipe(Blocks.LOG, new ItemStack(Items.COAL, 1, 1), 0.15F); + this.registerRecipe(Blocks.LOG2, new ItemStack(Items.COAL, 1, 1), 0.15F); + this.registerRecipe(Blocks.EMERALD_ORE, new ItemStack(Items.EMERALD), 1.0F); + this.a(Items.POTATO, new ItemStack(Items.POTATO_BAKED), 0.35F); + this.registerRecipe(Blocks.NETHERRACK, new ItemStack(Items.NETHER_BRICK), 0.1F); + this.registerRecipe(Blocks.SMOOTH_BRICK, new ItemStack(Blocks.SMOOTH_BRICK, 1, 2), 0.5F); // PaperSpigot - Register cracked stone brick recipe + EnumFish[] aenumfish = EnumFish.values(); + int i = aenumfish.length; + + for (int j = 0; j < i; ++j) { + EnumFish enumfish = aenumfish[j]; + + if (enumfish.i()) { + this.a(new ItemStack(Items.RAW_FISH, 1, enumfish.a()), new ItemStack(Items.COOKED_FISH, 1, enumfish.a()), 0.35F); + } + } + + this.registerRecipe(Blocks.COAL_ORE, new ItemStack(Items.COAL), 0.1F); + this.registerRecipe(Blocks.REDSTONE_ORE, new ItemStack(Items.REDSTONE), 0.7F); + this.registerRecipe(Blocks.LAPIS_ORE, new ItemStack(Items.INK_SACK, 1, 4), 0.2F); + this.registerRecipe(Blocks.QUARTZ_ORE, new ItemStack(Items.QUARTZ), 0.2F); + } + + public void registerRecipe(Block block, ItemStack itemstack, float f) { + this.a(Item.getItemOf(block), itemstack, f); + } + + public void a(Item item, ItemStack itemstack, float f) { + this.a(new ItemStack(item, 1, 32767), itemstack, f); + } + + public void a(ItemStack itemstack, ItemStack itemstack1, float f) { + this.recipes.put(itemstack, itemstack1); + this.c.put(itemstack1, Float.valueOf(f)); + } + + // CraftBukkit start - add method + public void registerRecipe(ItemStack itemstack, ItemStack itemstack1) { + this.customRecipes.put(itemstack, itemstack1); + } + // CraftBukkit end + + public ItemStack getResult(ItemStack itemstack) { + // CraftBukkit start - initialize to customRecipes + boolean vanilla = false; + Iterator iterator = this.customRecipes.entrySet().iterator(); + // CraftBukkit end + + Entry entry; + + do { + if (!iterator.hasNext()) { + // CraftBukkit start - fall back to vanilla recipes + if (!vanilla && recipes.size() != 0) { + iterator = this.recipes.entrySet().iterator(); + vanilla = true; + } else { + return null; + } + // CraftBukkit end + } + + entry = (Entry) iterator.next(); + } while (!this.a(itemstack, (ItemStack) entry.getKey())); + + return (ItemStack) entry.getValue(); + } + + private boolean a(ItemStack itemstack, ItemStack itemstack1) { + return itemstack1.getItem() == itemstack.getItem() && (itemstack1.getData() == 32767 || itemstack1.getData() == itemstack.getData()); + } + + public Map getRecipes() { + return this.recipes; + } + + public float b(ItemStack itemstack) { + Iterator iterator = this.c.entrySet().iterator(); + + Entry entry; + + do { + if (!iterator.hasNext()) { + return 0.0F; + } + + entry = (Entry) iterator.next(); + } while (!this.a(itemstack, (ItemStack) entry.getKey())); + + return ((Float) entry.getValue()).floatValue(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RegionFile.java b/vspigot-server/src/main/java/net/minecraft/server/RegionFile.java new file mode 100644 index 0000000..056504a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RegionFile.java @@ -0,0 +1,327 @@ +package net.minecraft.server; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + +public class RegionFile { + + private static final byte[] a = new byte[4096]; // Spigot - note: if this ever changes to not be 4096 bytes, update constructor! // PAIL: empty 4k block + private final File b; + private RandomAccessFile c; + private final int[] d = new int[1024]; + private final int[] e = new int[1024]; + private ArrayList f; + private int g; + private long h; + + // Poweruser start + private Boolean[] existingChunkCache = new Boolean[1024]; + + private boolean isExistingChunkCacheEntrySet(int i, int j) { + return this.existingChunkCache[i + j * 32] != null; + } + + private boolean checkExistingChunkCache(int i, int j) { + return this.existingChunkCache[i + j * 32].booleanValue(); + } + + private void addCoordinatesToCache(int i, int j, boolean result) { + Boolean a = this.existingChunkCache[i + j * 32]; + if(a == null || a.booleanValue() != result) { + this.existingChunkCache[i + j * 32] = new Boolean(result); + } + } + // Poweruser end + + public RegionFile(File file1) { + this.b = file1; + this.g = 0; + + try { + if (file1.exists()) { + this.h = file1.lastModified(); + } + + this.c = new RandomAccessFile(file1, "rw"); + int i; + + if (this.c.length() < 4096L) { + // Spigot start - more effecient chunk zero'ing + this.c.write(RegionFile.a); + this.c.write(RegionFile.a); + // Spigot end + + this.g += 8192; + } + + if ((this.c.length() & 4095L) != 0L) { + for (i = 0; (long) i < (this.c.length() & 4095L); ++i) { + this.c.write(0); + } + } + + i = (int) this.c.length() / 4096; + this.f = new ArrayList(i); + + int j; + + for (j = 0; j < i; ++j) { + this.f.add(Boolean.valueOf(true)); + } + + this.f.set(0, Boolean.valueOf(false)); + this.f.set(1, Boolean.valueOf(false)); + this.c.seek(0L); + + int k; + + // MineHQ start + ByteBuffer header = ByteBuffer.allocate(8192); + while (header.hasRemaining()) { + if (this.c.getChannel().read(header) == -1) throw new EOFException(); + } + header.clear(); + IntBuffer headerAsInts = header.asIntBuffer(); + // MineHQ end + for (j = 0; j < 1024; ++j) { + k = headerAsInts.get(); // MineHQ + this.d[j] = k; + if (k != 0 && (k >> 8) + (k & 255) <= this.f.size()) { + for (int l = 0; l < (k & 255); ++l) { + this.f.set((k >> 8) + l, Boolean.valueOf(false)); + } + } + } + + for (j = 0; j < 1024; ++j) { + k = headerAsInts.get(); // MineHQ + this.e[j] = k; + } + } catch (IOException ioexception) { + ioexception.printStackTrace(); + } + } + + // CraftBukkit start - This is a copy (sort of) of the method below it, make sure they stay in sync + public boolean chunkExists(int i, int j) { // Poweruser - move synchronization inside method + if (this.d(i, j)) { + return false; + } else { + // Poweruser start + if(this.isExistingChunkCacheEntrySet(i, j)) { + return this.checkExistingChunkCache(i, j); + } + synchronized(this) { + // Poweruser end + try { + int k = this.e(i, j); + + if (k == 0) { + return false; + } else { + int l = k >> 8; + int i1 = k & 255; + + if (l + i1 > this.f.size()) { + return false; + } + + this.c.seek((long) (l * 4096)); + int j1 = this.c.readInt(); + + if (j1 > 4096 * i1 || j1 <= 0) { + return false; + } + + byte b0 = this.c.readByte(); + // Poweruser start + boolean foundChunk = (b0 == 1 || b0 == 2); + this.addCoordinatesToCache(i, j, foundChunk); + return foundChunk; + // Poweruser end + } + } catch (IOException ioexception) { + return false; + } + } + } + } + // CraftBukkit end + + public synchronized DataInputStream a(int i, int j) { + if (this.d(i, j)) { + return null; + } else { + try { + int k = this.e(i, j); + + if (k == 0) { + return null; + } else { + int l = k >> 8; + int i1 = k & 255; + + if (l + i1 > this.f.size()) { + return null; + } else { + this.c.seek((long) (l * 4096)); + int j1 = this.c.readInt(); + + if (j1 > 4096 * i1) { + return null; + } else if (j1 <= 0) { + return null; + } else { + byte b0 = this.c.readByte(); + byte[] abyte; + + if (b0 == 1) { + this.addCoordinatesToCache(i, j, true); // Poweruser + abyte = new byte[j1 - 1]; + this.c.read(abyte); + return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(abyte)))); + } else if (b0 == 2) { + this.addCoordinatesToCache(i, j, true); // Poweruser + abyte = new byte[j1 - 1]; + this.c.read(abyte); + return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(abyte)))); + } else { + this.addCoordinatesToCache(i, j, false); // Poweruser + return null; + } + } + } + } + } catch (IOException ioexception) { + return null; + } + } + } + + public DataOutputStream b(int i, int j) { + return this.d(i, j) ? null : new DataOutputStream(new java.io.BufferedOutputStream(new DeflaterOutputStream(new ChunkBuffer(this, i, j)))); // Spigot - use a BufferedOutputStream to greatly improve file write performance + } + + protected synchronized void a(int i, int j, byte[] abyte, int k) { + try { + int l = this.e(i, j); + int i1 = l >> 8; + int j1 = l & 255; + int k1 = (k + 5) / 4096 + 1; + + if (k1 >= 256) { + return; + } + + if (i1 != 0 && j1 == k1) { + this.a(i1, abyte, k); + } else { + int l1; + + for (l1 = 0; l1 < j1; ++l1) { + this.f.set(i1 + l1, Boolean.valueOf(true)); + } + + l1 = this.f.indexOf(Boolean.valueOf(true)); + int i2 = 0; + int j2; + + if (l1 != -1) { + for (j2 = l1; j2 < this.f.size(); ++j2) { + if (i2 != 0) { + if (((Boolean) this.f.get(j2)).booleanValue()) { + ++i2; + } else { + i2 = 0; + } + } else if (((Boolean) this.f.get(j2)).booleanValue()) { + l1 = j2; + i2 = 1; + } + + if (i2 >= k1) { + break; + } + } + } + + if (i2 >= k1) { + i1 = l1; + this.a(i, j, l1 << 8 | k1); + + for (j2 = 0; j2 < k1; ++j2) { + this.f.set(i1 + j2, Boolean.valueOf(false)); + } + + this.a(i1, abyte, k); + } else { + this.c.seek(this.c.length()); + i1 = this.f.size(); + + for (j2 = 0; j2 < k1; ++j2) { + this.c.write(a); + this.f.add(Boolean.valueOf(false)); + } + + this.g += 4096 * k1; + this.a(i1, abyte, k); + this.a(i, j, i1 << 8 | k1); + } + } + + this.b(i, j, (int) (MinecraftServer.ar() / 1000L)); + this.addCoordinatesToCache(i, j, true); // Poweruser + } catch (IOException ioexception) { + ioexception.printStackTrace(); + } + } + + private void a(int i, byte[] abyte, int j) throws IOException { // CraftBukkit - added throws + this.c.seek((long) (i * 4096)); + this.c.writeInt(j + 1); + this.c.writeByte(2); + this.c.write(abyte, 0, j); + } + + private boolean d(int i, int j) { + return i < 0 || i >= 32 || j < 0 || j >= 32; + } + + private int e(int i, int j) { + return this.d[i + j * 32]; + } + + public boolean c(int i, int j) { + return this.e(i, j) != 0; + } + + private void a(int i, int j, int k) throws IOException { // CraftBukkit - added throws + this.d[i + j * 32] = k; + this.c.seek((long) ((i + j * 32) * 4)); + this.c.writeInt(k); + } + + private void b(int i, int j, int k) throws IOException { // CraftBukkit - added throws + this.e[i + j * 32] = k; + this.c.seek((long) (4096 + (i + j * 32) * 4)); + this.c.writeInt(k); + } + + public void c() throws IOException { // CraftBukkit - added throws + if (this.c != null) { + this.c.close(); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RegionFileCache.java b/vspigot-server/src/main/java/net/minecraft/server/RegionFileCache.java new file mode 100644 index 0000000..81ff30a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RegionFileCache.java @@ -0,0 +1,74 @@ +package net.minecraft.server; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class RegionFileCache { + + public static final Map a = new HashMap(); // CraftBukkit - private -> public + + // PaperSpigot start + public static synchronized RegionFile a(File file1, int i, int j) { + return a(file1, i, j, true); + } + + public static synchronized RegionFile a(File file1, int i, int j, boolean create) { + // PaperSpigot end + File file2 = new File(file1, "region"); + File file3 = new File(file2, "r." + (i >> 5) + "." + (j >> 5) + ".mca"); + RegionFile regionfile = (RegionFile) a.get(file3); + + if (regionfile != null) { + return regionfile; + } else { + if (!create && !file3.exists()) { return null; } // PaperSpigot + if (!file2.exists()) { + file2.mkdirs(); + } + + if (a.size() >= 256) { + a(); + } + + RegionFile regionfile1 = new RegionFile(file3); + + a.put(file3, regionfile1); + return regionfile1; + } + } + + public static synchronized void a() { + Iterator iterator = a.values().iterator(); + + while (iterator.hasNext()) { + RegionFile regionfile = (RegionFile) iterator.next(); + + try { + if (regionfile != null) { + regionfile.c(); + } + } catch (IOException ioexception) { + ioexception.printStackTrace(); + } + } + + a.clear(); + } + + public static DataInputStream c(File file1, int i, int j) { + RegionFile regionfile = a(file1, i, j); + + return regionfile.a(i & 31, j & 31); + } + + public static DataOutputStream d(File file1, int i, int j) { + RegionFile regionfile = a(file1, i, j); + + return regionfile.b(i & 31, j & 31); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RegistryBlocks.java b/vspigot-server/src/main/java/net/minecraft/server/RegistryBlocks.java new file mode 100644 index 0000000..f9891be --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RegistryBlocks.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +public class RegistryBlocks extends RegistryMaterials { + + private final String d; + private Object e; + + public RegistryBlocks(String s) { + this.d = s; + } + + public void a(int i, String s, Object object) { + if (this.d.equals(s)) { + this.e = object; + } + + super.a(i, s, object); + } + + public Object get(String s) { + Object object = super.get(s); + + return object == null ? this.e : object; + } + + public Object a(int i) { + Object object = super.a(i); + + return object == null ? this.e : object; + } + + public Object get(Object object) { + return this.get((String) object); + } + + // Poweruser start + public Object getDefaultBlock() { + return this.e; + } + + public Object getByIdWithoutDefaulting(int i) { + return super.a(i); + } + // Poweruser end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/RemoteControlCommandListener.java b/vspigot-server/src/main/java/net/minecraft/server/RemoteControlCommandListener.java new file mode 100644 index 0000000..fd967c1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/RemoteControlCommandListener.java @@ -0,0 +1,47 @@ +package net.minecraft.server; + +public class RemoteControlCommandListener implements ICommandListener { + + public static final RemoteControlCommandListener instance = new RemoteControlCommandListener(); + private StringBuffer b = new StringBuffer(); + + public RemoteControlCommandListener() {} + + public void e() { + this.b.setLength(0); + } + + public String f() { + return this.b.toString(); + } + + public String getName() { + return "Rcon"; + } + + public IChatBaseComponent getScoreboardDisplayName() { + return new ChatComponentText(this.getName()); + } + + // CraftBukkit start - Send a String + public void sendMessage(String message) { + this.b.append(message); + } + // CraftBukkit end + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.b.append(ichatbasecomponent.c()); + } + + public boolean a(int i, String s) { + return true; + } + + public ChunkCoordinates getChunkCoordinates() { + return new ChunkCoordinates(0, 0, 0); + } + + public World getWorld() { + return MinecraftServer.getServer().getWorld(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Scoreboard.java b/vspigot-server/src/main/java/net/minecraft/server/Scoreboard.java new file mode 100644 index 0000000..1f01c6b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Scoreboard.java @@ -0,0 +1,294 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class Scoreboard { + + // MineHQ start + public java.util.Set viewers() { + return viewers; + } + + protected java.util.Set viewers = Collections.EMPTY_SET; + // MineHQ end + + private final Map objectivesByName = new HashMap(); + private final Map objectivesByCriteria = new HashMap(); + private final Map playerScores = new HashMap(); + private final ScoreboardObjective[] displaySlots = new ScoreboardObjective[3]; + private final Map teamsByName = new HashMap(); + private final Map teamsByPlayer = new HashMap(); + + public Scoreboard() {} + + public ScoreboardObjective getObjective(String s) { + return (ScoreboardObjective) this.objectivesByName.get(s); + } + + // MineHQ start + public void addViewer(EntityPlayer player) { + if (viewers == Collections.EMPTY_SET) viewers = new java.util.HashSet(); + viewers.add(player); + } + + public void removeViewer(EntityPlayer player) { + if (viewers != Collections.EMPTY_SET && viewers.remove(player) && viewers.isEmpty()) viewers = Collections.EMPTY_SET; + } + // MineHQ end + + public ScoreboardObjective registerObjective(String s, IScoreboardCriteria iscoreboardcriteria) { + ScoreboardObjective scoreboardobjective = this.getObjective(s); + + if (scoreboardobjective != null) { + throw new IllegalArgumentException("An objective with the name \'" + s + "\' already exists!"); + } else { + scoreboardobjective = new ScoreboardObjective(this, s, iscoreboardcriteria); + Object object = (List) this.objectivesByCriteria.get(iscoreboardcriteria); + + if (object == null) { + object = new ArrayList(); + this.objectivesByCriteria.put(iscoreboardcriteria, object); + } + + ((List) object).add(scoreboardobjective); + this.objectivesByName.put(s, scoreboardobjective); + this.handleObjectiveAdded(scoreboardobjective); + return scoreboardobjective; + } + } + + public Collection getObjectivesForCriteria(IScoreboardCriteria iscoreboardcriteria) { + Collection collection = (Collection) this.objectivesByCriteria.get(iscoreboardcriteria); + + return collection == null ? new ArrayList() : new ArrayList(collection); + } + + public ScoreboardScore getPlayerScoreForObjective(String s, ScoreboardObjective scoreboardobjective) { + Object object = (Map) this.playerScores.get(s); + + if (object == null) { + object = new HashMap(); + this.playerScores.put(s, object); + } + + ScoreboardScore scoreboardscore = (ScoreboardScore) ((Map) object).get(scoreboardobjective); + + if (scoreboardscore == null) { + scoreboardscore = new ScoreboardScore(this, scoreboardobjective, s); + ((Map) object).put(scoreboardobjective, scoreboardscore); + } + + return scoreboardscore; + } + + public Collection getScoresForObjective(ScoreboardObjective scoreboardobjective) { + ArrayList arraylist = new ArrayList(); + Iterator iterator = this.playerScores.values().iterator(); + + while (iterator.hasNext()) { + Map map = (Map) iterator.next(); + ScoreboardScore scoreboardscore = (ScoreboardScore) map.get(scoreboardobjective); + + if (scoreboardscore != null) { + arraylist.add(scoreboardscore); + } + } + + Collections.sort(arraylist, ScoreboardScore.a); + return arraylist; + } + + public Collection getObjectives() { + return this.objectivesByName.values(); + } + + public Collection getPlayers() { + return this.playerScores.keySet(); + } + + public void resetPlayerScores(String s) { + Map map = (Map) this.playerScores.remove(s); + + if (map != null) { + this.handlePlayerRemoved(s); + } + } + + public Collection getScores() { + Collection collection = this.playerScores.values(); + ArrayList arraylist = new ArrayList(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Map map = (Map) iterator.next(); + + arraylist.addAll(map.values()); + } + + return arraylist; + } + + public Map getPlayerObjectives(String s) { + Object object = (Map) this.playerScores.get(s); + + if (object == null) { + object = new HashMap(); + } + + return (Map) object; + } + + public void unregisterObjective(ScoreboardObjective scoreboardobjective) { + this.objectivesByName.remove(scoreboardobjective.getName()); + + for (int i = 0; i < 3; ++i) { + if (this.getObjectiveForSlot(i) == scoreboardobjective) { + this.setDisplaySlot(i, (ScoreboardObjective) null); + } + } + + List list = (List) this.objectivesByCriteria.get(scoreboardobjective.getCriteria()); + + if (list != null) { + list.remove(scoreboardobjective); + } + + Iterator iterator = this.playerScores.values().iterator(); + + while (iterator.hasNext()) { + Map map = (Map) iterator.next(); + + map.remove(scoreboardobjective); + } + + this.handleObjectiveRemoved(scoreboardobjective); + } + + public void setDisplaySlot(int i, ScoreboardObjective scoreboardobjective) { + this.displaySlots[i] = scoreboardobjective; + } + + public ScoreboardObjective getObjectiveForSlot(int i) { + return this.displaySlots[i]; + } + + public ScoreboardTeam getTeam(String s) { + return (ScoreboardTeam) this.teamsByName.get(s); + } + + public ScoreboardTeam createTeam(String s) { + ScoreboardTeam scoreboardteam = this.getTeam(s); + + if (scoreboardteam != null) { + throw new IllegalArgumentException("A team with the name \'" + s + "\' already exists!"); + } else { + scoreboardteam = new ScoreboardTeam(this, s); + this.teamsByName.put(s, scoreboardteam); + this.handleTeamAdded(scoreboardteam); + return scoreboardteam; + } + } + + public void removeTeam(ScoreboardTeam scoreboardteam) { + this.teamsByName.remove(scoreboardteam.getName()); + Iterator iterator = scoreboardteam.getPlayerNameSet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + this.teamsByPlayer.remove(s); + } + + this.handleTeamRemoved(scoreboardteam); + } + + public boolean addPlayerToTeam(String s, String s1) { + if (!this.teamsByName.containsKey(s1)) { + return false; + } else { + ScoreboardTeam scoreboardteam = this.getTeam(s1); + + if (this.getPlayerTeam(s) != null) { + this.removePlayerFromTeam(s); + } + + this.teamsByPlayer.put(s, scoreboardteam); + scoreboardteam.getPlayerNameSet().add(s); + return true; + } + } + + public boolean removePlayerFromTeam(String s) { + ScoreboardTeam scoreboardteam = this.getPlayerTeam(s); + + if (scoreboardteam != null) { + this.removePlayerFromTeam(s, scoreboardteam); + return true; + } else { + return false; + } + } + + public void removePlayerFromTeam(String s, ScoreboardTeam scoreboardteam) { + if (this.getPlayerTeam(s) != scoreboardteam) { + throw new IllegalStateException("Player is either on another team or not on any team. Cannot remove from team \'" + scoreboardteam.getName() + "\'."); + } else { + this.teamsByPlayer.remove(s); + scoreboardteam.getPlayerNameSet().remove(s); + } + } + + public Collection getTeamNames() { + return this.teamsByName.keySet(); + } + + public Collection getTeams() { + return this.teamsByName.values(); + } + + public ScoreboardTeam getPlayerTeam(String s) { + return (ScoreboardTeam) this.teamsByPlayer.get(s); + } + + public void handleObjectiveAdded(ScoreboardObjective scoreboardobjective) {} + + public void handleObjectiveChanged(ScoreboardObjective scoreboardobjective) {} + + public void handleObjectiveRemoved(ScoreboardObjective scoreboardobjective) {} + + public void handleScoreChanged(ScoreboardScore scoreboardscore) {} + + public void handlePlayerRemoved(String s) {} + + public void handleTeamAdded(ScoreboardTeam scoreboardteam) {} + + public void handleTeamChanged(ScoreboardTeam scoreboardteam) {} + + public void handleTeamRemoved(ScoreboardTeam scoreboardteam) {} + + public static String getSlotName(int i) { + switch (i) { + case 0: + return "list"; + + case 1: + return "sidebar"; + + case 2: + return "belowName"; + + default: + return null; + } + } + + public static int getSlotForName(String s) { + return s.equalsIgnoreCase("list") ? 0 : (s.equalsIgnoreCase("sidebar") ? 1 : (s.equalsIgnoreCase("belowName") ? 2 : -1)); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ScoreboardServer.java b/vspigot-server/src/main/java/net/minecraft/server/ScoreboardServer.java new file mode 100644 index 0000000..faeb34a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ScoreboardServer.java @@ -0,0 +1,221 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class ScoreboardServer extends Scoreboard { + + private final MinecraftServer a; + private final Set b = new HashSet(); + private PersistentScoreboard c; + + public ScoreboardServer(MinecraftServer minecraftserver) { + this.a = minecraftserver; + } + + public void handleScoreChanged(ScoreboardScore scoreboardscore) { + super.handleScoreChanged(scoreboardscore); + if (this.b.contains(scoreboardscore.getObjective())) { + this.sendAll(new PacketPlayOutScoreboardScore(scoreboardscore, 0)); // CraftBukkit - Internal packet method + } + + this.b(); + } + + public void handlePlayerRemoved(String s) { + super.handlePlayerRemoved(s); + this.sendAll(new PacketPlayOutScoreboardScore(s)); // CraftBukkit - Internal packet method + this.b(); + } + + public void setDisplaySlot(int i, ScoreboardObjective scoreboardobjective) { + ScoreboardObjective scoreboardobjective1 = this.getObjectiveForSlot(i); + + super.setDisplaySlot(i, scoreboardobjective); + if (scoreboardobjective1 != scoreboardobjective && scoreboardobjective1 != null) { + if (this.h(scoreboardobjective1) > 0) { + this.sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); // CraftBukkit - Internal packet method + } else { + this.g(scoreboardobjective1); + } + } + + if (scoreboardobjective != null) { + if (this.b.contains(scoreboardobjective)) { + this.sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); // CraftBukkit - Internal packet method + } else { + this.e(scoreboardobjective); + } + } + + this.b(); + } + + public boolean addPlayerToTeam(String s, String s1) { + if (super.addPlayerToTeam(s, s1)) { + ScoreboardTeam scoreboardteam = this.getTeam(s1); + + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 3)); // CraftBukkit - Internal packet method + this.b(); + return true; + } else { + return false; + } + } + + public void removePlayerFromTeam(String s, ScoreboardTeam scoreboardteam) { + super.removePlayerFromTeam(s, scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 4)); // CraftBukkit - Internal packet method + this.b(); + } + + public void handleObjectiveAdded(ScoreboardObjective scoreboardobjective) { + super.handleObjectiveAdded(scoreboardobjective); + this.b(); + } + + public void handleObjectiveChanged(ScoreboardObjective scoreboardobjective) { + super.handleObjectiveChanged(scoreboardobjective); + if (this.b.contains(scoreboardobjective)) { + this.sendAll(new PacketPlayOutScoreboardObjective(scoreboardobjective, 2)); // CraftBukkit - Internal packet method + } + + this.b(); + } + + public void handleObjectiveRemoved(ScoreboardObjective scoreboardobjective) { + super.handleObjectiveRemoved(scoreboardobjective); + if (this.b.contains(scoreboardobjective)) { + this.g(scoreboardobjective); + } + + this.b(); + } + + public void handleTeamAdded(ScoreboardTeam scoreboardteam) { + super.handleTeamAdded(scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 0)); // CraftBukkit - Internal packet method + this.b(); + } + + public void handleTeamChanged(ScoreboardTeam scoreboardteam) { + super.handleTeamChanged(scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 2)); // CraftBukkit - Internal packet method + this.b(); + } + + public void handleTeamRemoved(ScoreboardTeam scoreboardteam) { + super.handleTeamRemoved(scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 1)); // CraftBukkit - Internal packet method + this.b(); + } + + public void a(PersistentScoreboard persistentscoreboard) { + this.c = persistentscoreboard; + } + + protected void b() { + if (this.c != null) { + this.c.c(); + } + } + + public List getScoreboardScorePacketsForObjective(ScoreboardObjective scoreboardobjective) { + ArrayList arraylist = new ArrayList(); + + arraylist.add(new PacketPlayOutScoreboardObjective(scoreboardobjective, 0)); + + for (int i = 0; i < 3; ++i) { + if (this.getObjectiveForSlot(i) == scoreboardobjective) { + arraylist.add(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); + } + } + + Iterator iterator = this.getScoresForObjective(scoreboardobjective).iterator(); + + while (iterator.hasNext()) { + ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); + + arraylist.add(new PacketPlayOutScoreboardScore(scoreboardscore, 0)); + } + + return arraylist; + } + + public void e(ScoreboardObjective scoreboardobjective) { + List list = this.getScoreboardScorePacketsForObjective(scoreboardobjective); + // MineHQ start + Iterator iterator = viewers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = iterator.next(); + // MineHQ end + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + Packet packet = (Packet) iterator1.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + } + + this.b.add(scoreboardobjective); + } + + public List f(ScoreboardObjective scoreboardobjective) { + ArrayList arraylist = new ArrayList(); + + arraylist.add(new PacketPlayOutScoreboardObjective(scoreboardobjective, 1)); + + for (int i = 0; i < 3; ++i) { + if (this.getObjectiveForSlot(i) == scoreboardobjective) { + arraylist.add(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); + } + } + + return arraylist; + } + + public void g(ScoreboardObjective scoreboardobjective) { + List list = this.f(scoreboardobjective); + // MineHQ start + Iterator iterator = viewers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = iterator.next(); + // MineHQ end + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + Packet packet = (Packet) iterator1.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + } + + this.b.remove(scoreboardobjective); + } + + public int h(ScoreboardObjective scoreboardobjective) { + int i = 0; + + for (int j = 0; j < 3; ++j) { + if (this.getObjectiveForSlot(j) == scoreboardobjective) { + ++i; + } + } + + return i; + } + + // CraftBukkit start - Send to players + private void sendAll(Packet packet) { + if (viewers != Collections.EMPTY_SET) for (EntityPlayer entityPlayer : viewers) entityPlayer.playerConnection.sendPacket(packet); // MineHQ + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/SecondaryWorldServer.java b/vspigot-server/src/main/java/net/minecraft/server/SecondaryWorldServer.java new file mode 100644 index 0000000..5236f81 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/SecondaryWorldServer.java @@ -0,0 +1,14 @@ +package net.minecraft.server; + +public class SecondaryWorldServer extends WorldServer { + // CraftBukkit start - Add Environment and ChunkGenerator arguments + public SecondaryWorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, String s, int i, WorldSettings worldsettings, WorldServer worldserver, MethodProfiler methodprofiler, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { + super(minecraftserver, idatamanager, s, i, worldsettings, methodprofiler, env, gen); + // CraftBukkit end + this.worldMaps = worldserver.worldMaps; + this.scoreboard = worldserver.getScoreboard(); + // this.worldData = new SecondaryWorldData(worldserver.getWorldData()); // CraftBukkit - use unique worlddata + } + + // protected void a() {} // CraftBukkit - save world data! +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ServerConnection.java b/vspigot-server/src/main/java/net/minecraft/server/ServerConnection.java new file mode 100644 index 0000000..7af2dfe --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ServerConnection.java @@ -0,0 +1,126 @@ +package net.minecraft.server; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; + +import net.minecraft.util.com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.minecraft.util.io.netty.bootstrap.ServerBootstrap; +import net.minecraft.util.io.netty.channel.ChannelFuture; +import net.minecraft.util.io.netty.channel.nio.NioEventLoopGroup; +import net.minecraft.util.io.netty.channel.socket.nio.NioServerSocketChannel; +import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ServerConnection { + + private static final Logger b = LogManager.getLogger(); + private static final NioEventLoopGroup c = new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty IO #%d").setDaemon(true).build()); + private final MinecraftServer d; + public volatile boolean a; + private final List e = Collections.synchronizedList(new ArrayList()); + private final List f = Collections.synchronizedList(new ArrayList()); + + // Paper start - prevent blocking on adding a new network manager while the server is ticking + protected final List pending = Collections.synchronizedList(new ArrayList()); + private void addPending() { + synchronized (pending) { + synchronized (this.f) { + this.f.addAll(pending); + pending.clear(); + } + } + } + // Paper end + + public ServerConnection(MinecraftServer minecraftserver) { + this.d = minecraftserver; + this.a = true; + } + + public void a(InetAddress inetaddress, int i) { + List list = this.e; + + synchronized (this.e) { + this.e.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(NioServerSocketChannel.class)).childHandler(new ServerConnectionChannel(this)).group(c).localAddress(inetaddress, i)).bind().syncUninterruptibly()); + } + } + + public void b() { + this.a = false; + Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + ChannelFuture channelfuture = (ChannelFuture) iterator.next(); + + channelfuture.channel().close().syncUninterruptibly(); + } + } + + public void c() { + List list = this.f; + + synchronized (this.f) { + addPending(); // Paper + // Spigot Start + // This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order + if ( org.spigotmc.SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % org.spigotmc.SpigotConfig.playerShuffle == 0 ) + { + Collections.shuffle( this.f ); + } + // Spigot End + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + NetworkManager networkmanager = (NetworkManager) iterator.next(); + + if (!networkmanager.isConnected()) { + // Spigot Start + // Fix a race condition where a NetworkManager could be unregistered just before connection. + if (networkmanager.preparing) continue; + // Spigot End + iterator.remove(); + if (networkmanager.f() != null) { + networkmanager.getPacketListener().a(networkmanager.f()); + } else if (networkmanager.getPacketListener() != null) { + networkmanager.getPacketListener().a(new ChatComponentText("Disconnected")); + } + } else { + try { + networkmanager.a(); + } catch (Exception exception) { + if (networkmanager.c()) { + CrashReport crashreport = CrashReport.a(exception, "Ticking memory connection"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Ticking connection"); + + crashreportsystemdetails.a("Connection",new CrashReportServerConnection(this, networkmanager)); + throw new ReportedException(crashreport); + } + + b.warn("Failed to handle packet for " + networkmanager.getSocketAddress(), exception); + ChatComponentText chatcomponenttext = new ChatComponentText("Internal server error"); + + networkmanager.handle(new PacketPlayOutKickDisconnect(chatcomponenttext),new ServerConnectionFuture(this, networkmanager, chatcomponenttext)); + networkmanager.g(); + } + } + } + } + } + + public MinecraftServer d() { + return this.d; + } + + static List a(ServerConnection serverconnection) { + return serverconnection.f; + } + + static MinecraftServer b(ServerConnection serverconnection) { + return serverconnection.d; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ServerConnectionChannel.java b/vspigot-server/src/main/java/net/minecraft/server/ServerConnectionChannel.java new file mode 100644 index 0000000..a4e6a98 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ServerConnectionChannel.java @@ -0,0 +1,37 @@ +package net.minecraft.server; + +import net.minecraft.util.io.netty.channel.Channel; +import net.minecraft.util.io.netty.channel.ChannelException; +import net.minecraft.util.io.netty.channel.ChannelInitializer; +import net.minecraft.util.io.netty.channel.ChannelOption; +import net.minecraft.util.io.netty.handler.timeout.ReadTimeoutHandler; + +class ServerConnectionChannel extends ChannelInitializer { + + final ServerConnection a; + + ServerConnectionChannel(ServerConnection serverconnection) { + this.a = serverconnection; + } + + protected void initChannel(Channel channel) { + try { + channel.config().setOption(ChannelOption.IP_TOS, Integer.valueOf(24)); + } catch (ChannelException channelexception) { + ; + } + + try { + channel.config().setOption(ChannelOption.TCP_NODELAY, Boolean.valueOf(true)); + } catch (ChannelException channelexception1) { + ; + } + + channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)).addLast("legacy_query", new LegacyPingHandler(this.a)).addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(NetworkManager.h)).addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(NetworkManager.h)); + NetworkManager networkmanager = new NetworkManager(false); + + this.a.pending.add(networkmanager); // Paper + channel.pipeline().addLast("packet_handler", networkmanager); + networkmanager.a((PacketListener) (new HandshakeListener(ServerConnection.b(this.a), networkmanager))); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ServerStatisticManager.java b/vspigot-server/src/main/java/net/minecraft/server/ServerStatisticManager.java new file mode 100644 index 0000000..d14bb51 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ServerStatisticManager.java @@ -0,0 +1,216 @@ +package net.minecraft.server; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import net.minecraft.util.com.google.common.collect.Maps; +import net.minecraft.util.com.google.common.collect.Sets; +import net.minecraft.util.com.google.gson.JsonElement; +import net.minecraft.util.com.google.gson.JsonObject; +import net.minecraft.util.com.google.gson.JsonParseException; +import net.minecraft.util.com.google.gson.JsonParser; +import net.minecraft.util.org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.spigotmc.SpigotConfig; + +public class ServerStatisticManager extends StatisticManager { + + private static final Logger b = LogManager.getLogger(); + private final MinecraftServer c; + private final File d; + private final Set e = Sets.newHashSet(); + private int f = -300; + private boolean g = false; + + public ServerStatisticManager(MinecraftServer minecraftserver, File file1) { + this.c = minecraftserver; + this.d = file1; + // Spigot start + for ( String name : org.spigotmc.SpigotConfig.forcedStats.keySet() ) + { + StatisticWrapper wrapper = new StatisticWrapper(); + wrapper.a( org.spigotmc.SpigotConfig.forcedStats.get( name ) ); + a.put( StatisticList.getStatistic( name ), wrapper ); + } + // Spigot end + } + + public void a() { + if (SpigotConfig.disableSaving) return; + if (this.d.isFile()) { + try { + this.a.clear(); + this.a.putAll(this.a(FileUtils.readFileToString(this.d))); + } catch (IOException ioexception) { + b.error("Couldn\'t read statistics file " + this.d, ioexception); + } catch (JsonParseException jsonparseexception) { + b.error("Couldn\'t parse statistics file " + this.d, jsonparseexception); + } + } + } + + public void b() { + if (SpigotConfig.disableSaving) return; // MineHQ + if ( org.spigotmc.SpigotConfig.disableStatSaving ) return; // Spigot + try { + FileUtils.writeStringToFile(this.d, a(this.a)); + } catch (IOException ioexception) { + b.error("Couldn\'t save stats", ioexception); + } + } + + public void setStatistic(EntityHuman entityhuman, Statistic statistic, int i) { + if (SpigotConfig.disableSaving) return; // MineHQ + if ( org.spigotmc.SpigotConfig.disableStatSaving ) return; // Spigot + int j = statistic.d() ? this.getStatisticValue(statistic) : 0; + + super.setStatistic(entityhuman, statistic, i); + this.e.add(statistic); + if (statistic.d() && j == 0 && i > 0) { + this.g = true; + if (this.c.at()) { + this.c.getPlayerList().sendMessage(new ChatMessage("chat.type.achievement", new Object[] { entityhuman.getScoreboardDisplayName(), statistic.j()})); + } + } + } + + public Set c() { + HashSet hashset = Sets.newHashSet(this.e); + + this.e.clear(); + this.g = false; + return hashset; + } + + public Map a(String s) { + JsonElement jsonelement = (new JsonParser()).parse(s); + + if (!jsonelement.isJsonObject()) { + return Maps.newHashMap(); + } else { + JsonObject jsonobject = jsonelement.getAsJsonObject(); + HashMap hashmap = Maps.newHashMap(); + Iterator iterator = jsonobject.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + Statistic statistic = StatisticList.getStatistic((String) entry.getKey()); + + if (statistic != null) { + StatisticWrapper statisticwrapper = new StatisticWrapper(); + + if (((JsonElement) entry.getValue()).isJsonPrimitive() && ((JsonElement) entry.getValue()).getAsJsonPrimitive().isNumber()) { + statisticwrapper.a(((JsonElement) entry.getValue()).getAsInt()); + } else if (((JsonElement) entry.getValue()).isJsonObject()) { + JsonObject jsonobject1 = ((JsonElement) entry.getValue()).getAsJsonObject(); + + if (jsonobject1.has("value") && jsonobject1.get("value").isJsonPrimitive() && jsonobject1.get("value").getAsJsonPrimitive().isNumber()) { + statisticwrapper.a(jsonobject1.getAsJsonPrimitive("value").getAsInt()); + } + + if (jsonobject1.has("progress") && statistic.l() != null) { + try { + Constructor constructor = statistic.l().getConstructor(new Class[0]); + IJsonStatistic ijsonstatistic = (IJsonStatistic) constructor.newInstance(new Object[0]); + + ijsonstatistic.a(jsonobject1.get("progress")); + statisticwrapper.a(ijsonstatistic); + } catch (Throwable throwable) { + b.warn("Invalid statistic progress in " + this.d, throwable); + } + } + } + + hashmap.put(statistic, statisticwrapper); + } else { + b.warn("Invalid statistic in " + this.d + ": Don\'t know what " + (String) entry.getKey() + " is"); + } + } + + return hashmap; + } + } + + public static String a(Map map) { + JsonObject jsonobject = new JsonObject(); + Iterator iterator = map.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + if (((StatisticWrapper) entry.getValue()).b() != null) { + JsonObject jsonobject1 = new JsonObject(); + + jsonobject1.addProperty("value", Integer.valueOf(((StatisticWrapper) entry.getValue()).a())); + + try { + jsonobject1.add("progress", ((StatisticWrapper) entry.getValue()).b().a()); + } catch (Throwable throwable) { + b.warn("Couldn\'t save statistic " + ((Statistic) entry.getKey()).e() + ": error serializing progress", throwable); + } + + jsonobject.add(((Statistic) entry.getKey()).name, jsonobject1); + } else { + jsonobject.addProperty(((Statistic) entry.getKey()).name, Integer.valueOf(((StatisticWrapper) entry.getValue()).a())); + } + } + + return jsonobject.toString(); + } + + public void d() { + Iterator iterator = this.a.keySet().iterator(); + + while (iterator.hasNext()) { + Statistic statistic = (Statistic) iterator.next(); + + this.e.add(statistic); + } + } + + public void a(EntityPlayer entityplayer) { + int i = this.c.al(); + HashMap hashmap = Maps.newHashMap(); + + if (this.g || i - this.f > 300) { + this.f = i; + Iterator iterator = this.c().iterator(); + + while (iterator.hasNext()) { + Statistic statistic = (Statistic) iterator.next(); + + hashmap.put(statistic, Integer.valueOf(this.getStatisticValue(statistic))); + } + } + + entityplayer.playerConnection.sendPacket(new PacketPlayOutStatistic(hashmap)); + } + + public void updateStatistics(EntityPlayer entityplayer) { + HashMap hashmap = Maps.newHashMap(); + Iterator iterator = AchievementList.e.iterator(); + + while (iterator.hasNext()) { + Achievement achievement = (Achievement) iterator.next(); + + if (this.hasAchievement(achievement)) { + hashmap.put(achievement, Integer.valueOf(this.getStatisticValue(achievement))); + this.e.remove(achievement); + } + } + + entityplayer.playerConnection.sendPacket(new PacketPlayOutStatistic(hashmap)); + } + + public boolean e() { + return this.g; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ShapedRecipes.java b/vspigot-server/src/main/java/net/minecraft/server/ShapedRecipes.java new file mode 100644 index 0000000..867dd07 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ShapedRecipes.java @@ -0,0 +1,166 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; +// CraftBukkit end + +public class ShapedRecipes implements IRecipe { + + private int width; + private int height; + private ItemStack[] items; + public ItemStack result; // Spigot + private boolean e; + + public ShapedRecipes(int i, int j, ItemStack[] aitemstack, ItemStack itemstack) { + this.width = i; + this.height = j; + this.items = aitemstack; + this.result = itemstack; + } + + // CraftBukkit start + public org.bukkit.inventory.ShapedRecipe toBukkitRecipe() { + CraftItemStack result = CraftItemStack.asCraftMirror(this.result); + CraftShapedRecipe recipe = new CraftShapedRecipe(result, this); + switch (this.height) { + case 1: + switch (this.width) { + case 1: + recipe.shape("a"); + break; + case 2: + recipe.shape("ab"); + break; + case 3: + recipe.shape("abc"); + break; + } + break; + case 2: + switch (this.width) { + case 1: + recipe.shape("a","b"); + break; + case 2: + recipe.shape("ab","cd"); + break; + case 3: + recipe.shape("abc","def"); + break; + } + break; + case 3: + switch (this.width) { + case 1: + recipe.shape("a","b","c"); + break; + case 2: + recipe.shape("ab","cd","ef"); + break; + case 3: + recipe.shape("abc","def","ghi"); + break; + } + break; + } + char c = 'a'; + for (ItemStack stack : this.items) { + if (stack != null) { + recipe.setIngredient(c, org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(stack.getItem()), stack.getData()); + } + c++; + } + return recipe; + } + // CraftBukkit end + + public ItemStack b() { + return this.result; + } + + public boolean a(InventoryCrafting inventorycrafting, World world) { + for (int i = 0; i <= 3 - this.width; ++i) { + for (int j = 0; j <= 3 - this.height; ++j) { + if (this.a(inventorycrafting, i, j, true)) { + return true; + } + + if (this.a(inventorycrafting, i, j, false)) { + return true; + } + } + } + + return false; + } + + private boolean a(InventoryCrafting inventorycrafting, int i, int j, boolean flag) { + for (int k = 0; k < 3; ++k) { + for (int l = 0; l < 3; ++l) { + int i1 = k - i; + int j1 = l - j; + ItemStack itemstack = null; + + if (i1 >= 0 && j1 >= 0 && i1 < this.width && j1 < this.height) { + if (flag) { + itemstack = this.items[this.width - i1 - 1 + j1 * this.width]; + } else { + itemstack = this.items[i1 + j1 * this.width]; + } + } + + ItemStack itemstack1 = inventorycrafting.b(k, l); + + if (itemstack1 != null || itemstack != null) { + if (itemstack1 == null && itemstack != null || itemstack1 != null && itemstack == null) { + return false; + } + + if (itemstack.getItem() != itemstack1.getItem()) { + return false; + } + + if (itemstack.getData() != 32767 && itemstack.getData() != itemstack1.getData()) { + return false; + } + } + } + } + + return true; + } + + public ItemStack a(InventoryCrafting inventorycrafting) { + ItemStack itemstack = this.b().cloneItemStack(); + + if (this.e) { + for (int i = 0; i < inventorycrafting.getSize(); ++i) { + ItemStack itemstack1 = inventorycrafting.getItem(i); + + if (itemstack1 != null && itemstack1.hasTag()) { + itemstack.setTag((NBTTagCompound) itemstack1.tag.clone()); + } + } + } + + return itemstack; + } + + public int a() { + return this.width * this.height; + } + + public ShapedRecipes c() { + this.e = true; + return this; + } + + // Spigot start + public java.util.List getIngredients() + { + return java.util.Arrays.asList( items ); + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ShapelessRecipes.java b/vspigot-server/src/main/java/net/minecraft/server/ShapelessRecipes.java new file mode 100644 index 0000000..21181fb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ShapelessRecipes.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; +// CraftBukkit end + +public class ShapelessRecipes implements IRecipe { + + public final ItemStack result; // Spigot + private final List ingredients; + + public ShapelessRecipes(ItemStack itemstack, List list) { + this.result = itemstack; + this.ingredients = list; + } + + // CraftBukkit start + @SuppressWarnings("unchecked") + public org.bukkit.inventory.ShapelessRecipe toBukkitRecipe() { + CraftItemStack result = CraftItemStack.asCraftMirror(this.result); + CraftShapelessRecipe recipe = new CraftShapelessRecipe(result, this); + for (ItemStack stack : (List) this.ingredients) { + if (stack != null) { + recipe.addIngredient(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(stack.getItem()), stack.getData()); + } + } + return recipe; + } + // CraftBukkit end + + public ItemStack b() { + return this.result; + } + + public boolean a(InventoryCrafting inventorycrafting, World world) { + ArrayList arraylist = new ArrayList(this.ingredients); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + ItemStack itemstack = inventorycrafting.b(j, i); + + if (itemstack != null) { + boolean flag = false; + Iterator iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack1 = (ItemStack) iterator.next(); + + if (itemstack.getItem() == itemstack1.getItem() && (itemstack1.getData() == 32767 || itemstack.getData() == itemstack1.getData())) { + flag = true; + arraylist.remove(itemstack1); + break; + } + } + + if (!flag) { + return false; + } + } + } + } + + return arraylist.isEmpty(); + } + + public ItemStack a(InventoryCrafting inventorycrafting) { + return this.result.cloneItemStack(); + } + + public int a() { + return this.ingredients.size(); + } + + // Spigot start + public java.util.List getIngredients() + { + return java.util.Collections.unmodifiableList( ingredients ); + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Slot.java b/vspigot-server/src/main/java/net/minecraft/server/Slot.java new file mode 100644 index 0000000..b86153b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Slot.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +public class Slot { + + public final int index; // CraftBukkit - private -> public + public final IInventory inventory; + public int rawSlotIndex; + public int h; + public int i; + + public Slot(IInventory iinventory, int i, int j, int k) { + this.inventory = iinventory; + this.index = i; + this.h = j; + this.i = k; + } + + public void a(ItemStack itemstack, ItemStack itemstack1) { + if (itemstack != null && itemstack1 != null) { + if (itemstack.getItem() == itemstack1.getItem()) { + int i = itemstack1.count - itemstack.count; + + if (i > 0) { + this.a(itemstack, i); + } + } + } + } + + protected void a(ItemStack itemstack, int i) {} + + protected void b(ItemStack itemstack) {} + + public void a(EntityHuman entityhuman, ItemStack itemstack) { + this.f(); + } + + public boolean isAllowed(ItemStack itemstack) { + return true; + } + + public ItemStack getItem() { + return this.inventory.getItem(this.index); + } + + public boolean hasItem() { + return this.getItem() != null; + } + + public void set(ItemStack itemstack) { + if (itemstack != null && itemstack.count < 0) itemstack.count = 0; // EMC + this.inventory.setItem(this.index, itemstack); + this.f(); + } + + public void f() { + this.inventory.update(); + } + + public int getMaxStackSize() { + return this.inventory.getMaxStackSize(); + } + + public ItemStack a(int i) { + return this.inventory.splitStack(this.index, i); + } + + public boolean a(IInventory iinventory, int i) { + return iinventory == this.inventory && i == this.index; + } + + public boolean isAllowed(EntityHuman entityhuman) { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/SlotFurnaceResult.java b/vspigot-server/src/main/java/net/minecraft/server/SlotFurnaceResult.java new file mode 100644 index 0000000..9b8e39b --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/SlotFurnaceResult.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.FurnaceExtractEvent; +// CraftBukkit end + +public class SlotFurnaceResult extends Slot { + + private EntityHuman a; + private int b; + + public SlotFurnaceResult(EntityHuman entityhuman, IInventory iinventory, int i, int j, int k) { + super(iinventory, i, j, k); + this.a = entityhuman; + } + + public boolean isAllowed(ItemStack itemstack) { + return false; + } + + public ItemStack a(int i) { + if (this.hasItem()) { + this.b += Math.min(i, this.getItem().count); + } + + return super.a(i); + } + + public void a(EntityHuman entityhuman, ItemStack itemstack) { + this.b(itemstack); + super.a(entityhuman, itemstack); + } + + protected void a(ItemStack itemstack, int i) { + this.b += i; + this.b(itemstack); + } + + protected void b(ItemStack itemstack) { + itemstack.a(this.a.world, this.a, this.b); + if (!this.a.world.isStatic) { + int i = this.b; + float f = RecipesFurnace.getInstance().b(itemstack); + int j; + + if (f == 0.0F) { + i = 0; + } else if (f < 1.0F) { + j = MathHelper.d((float) i * f); + if (j < MathHelper.f((float) i * f) && (float) Math.random() < (float) i * f - (float) j) { + ++j; + } + + i = j; + } + + // CraftBukkit start - fire FurnaceExtractEvent + Player player = (Player) a.getBukkitEntity(); + TileEntityFurnace furnace = ((TileEntityFurnace) this.inventory); + org.bukkit.block.Block block = a.world.getWorld().getBlockAt(furnace.x, furnace.y, furnace.z); + + FurnaceExtractEvent event = new FurnaceExtractEvent(player, block, org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(itemstack.getItem()), itemstack.count, i); + a.world.getServer().getPluginManager().callEvent(event); + + i = event.getExpToDrop(); + // CraftBukkit end + + while (i > 0) { + j = EntityExperienceOrb.getOrbValue(i); + i -= j; + this.a.world.addEntity(new EntityExperienceOrb(this.a.world, this.a.locX, this.a.locY + 0.5D, this.a.locZ + 0.5D, j)); + } + } + + this.b = 0; + if (itemstack.getItem() == Items.IRON_INGOT) { + this.a.a((Statistic) AchievementList.k, 1); + } + + if (itemstack.getItem() == Items.COOKED_FISH) { + this.a.a((Statistic) AchievementList.p, 1); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/SpawnerCreature.java b/vspigot-server/src/main/java/net/minecraft/server/SpawnerCreature.java new file mode 100644 index 0000000..19c331a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -0,0 +1,290 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +// CraftBukkit start +import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.craftbukkit.util.LongObjectHashMap; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + +public final class SpawnerCreature { + + private LongObjectHashMap a = new LongObjectHashMap(); // CraftBukkit - HashMap -> LongObjectHashMap + + public SpawnerCreature() {} + + protected static ChunkPosition getRandomPosition(World world, int i, int j) { + Chunk chunk = world.getChunkAt(i, j); + int k = i * 16 + world.random.nextInt(16); + int l = j * 16 + world.random.nextInt(16); + int i1 = world.random.nextInt(chunk == null ? world.S() : chunk.h() + 16 - 1); + + return new ChunkPosition(k, i1, l); + } + + // Spigot start - get entity count only from chunks being processed in b + private int getEntityCount(WorldServer server, Class oClass) + { + int i = 0; + for ( Long coord : this.a.keySet() ) + { + int x = LongHash.msw( coord ); + int z = LongHash.lsw( coord ); + if ( !server.chunkProviderServer.unloadQueue.contains( coord ) && server.isChunkLoaded( x, z ) ) + { + i += server.getChunkAt( x, z ).entityCount.get( oClass ); + } + } + return i; + } + // Spigot end + + public int spawnEntities(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) { + if (!flag && !flag1) { + return 0; + } else { + this.a.clear(); + + int i; + int j; + + for (i = 0; i < worldserver.players.size(); ++i) { + EntityHuman entityhuman = (EntityHuman) worldserver.players.get(i); + // PaperSpigot start - Affects spawning API + if (!entityhuman.affectsSpawning) + continue; + // PaperSpigot end + int k = MathHelper.floor(entityhuman.locX / 16.0D); + + j = MathHelper.floor(entityhuman.locZ / 16.0D); + byte b0 = 8; + // Spigot Start + b0 = worldserver.spigotConfig.mobSpawnRange; + b0 = ( b0 > worldserver.spigotConfig.viewDistance ) ? (byte) worldserver.spigotConfig.viewDistance : b0; + b0 = ( b0 > 8 ) ? 8 : b0; + // Spigot End + + for (int l = -b0; l <= b0; ++l) { + for (int i1 = -b0; i1 <= b0; ++i1) { + boolean flag3 = l == -b0 || l == b0 || i1 == -b0 || i1 == b0; + + // CraftBukkit start - use LongHash and LongObjectHashMap + long chunkCoords = LongHash.toLong(l + k, i1 + j); + + if (!flag3 && worldserver.isChunkLoaded((i1 + l) >> 4, (k + j) >> 4)) { // MineHQ + this.a.put(chunkCoords, false); + } else if (!this.a.containsKey(chunkCoords)) { + this.a.put(chunkCoords, true); + } + // CraftBukkit end + } + } + } + + i = 0; + ChunkCoordinates chunkcoordinates = worldserver.getSpawn(); + EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values(); + + j = aenumcreaturetype.length; + + for (int j1 = 0; j1 < j; ++j1) { + EnumCreatureType enumcreaturetype = aenumcreaturetype[j1]; + + // CraftBukkit start - Use per-world spawn limits + int limit = enumcreaturetype.b(); + switch (enumcreaturetype) { + case MONSTER: + limit = worldserver.getWorld().getMonsterSpawnLimit(); + break; + case CREATURE: + limit = worldserver.getWorld().getAnimalSpawnLimit(); + break; + case WATER_CREATURE: + limit = worldserver.getWorld().getWaterAnimalSpawnLimit(); + break; + case AMBIENT: + limit = worldserver.getWorld().getAmbientSpawnLimit(); + break; + } + + if (limit == 0) { + continue; + } + int mobcnt = 0; + // CraftBukkit end + + if ((!enumcreaturetype.d() || flag1) && (enumcreaturetype.d() || flag) && (!enumcreaturetype.e() || flag2) && (mobcnt = getEntityCount(worldserver, enumcreaturetype.a())) <= limit * this.a.size() / 256) { // Spigot - use per-world limits and use all loaded chunks + Iterator iterator = this.a.keySet().iterator(); + + int moblimit = (limit * this.a.size() / 256) - mobcnt + 1; // Spigot - up to 1 more than limit + label110: + while (iterator.hasNext() && (moblimit > 0)) { // Spigot - while more allowed + // CraftBukkit start = use LongHash and LongObjectHashMap + long key = ((Long) iterator.next()).longValue(); + + if (!this.a.get(key)) { + ChunkPosition chunkposition = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key)); + // CraftBukkit end + int k1 = chunkposition.x; + int l1 = chunkposition.y; + int i2 = chunkposition.z; + + if (!worldserver.getType(k1, l1, i2).r() && worldserver.getType(k1, l1, i2).getMaterial() == enumcreaturetype.c()) { + int j2 = 0; + int k2 = 0; + + while (k2 < 3) { + int l2 = k1; + int i3 = l1; + int j3 = i2; + byte b1 = 6; + BiomeMeta biomemeta = null; + GroupDataEntity groupdataentity = null; + int k3 = 0; + + while (true) { + if (k3 < 4) { + label103: { + l2 += worldserver.random.nextInt(b1) - worldserver.random.nextInt(b1); + i3 += worldserver.random.nextInt(1) - worldserver.random.nextInt(1); + j3 += worldserver.random.nextInt(b1) - worldserver.random.nextInt(b1); + if (a(enumcreaturetype, worldserver, l2, i3, j3)) { + float f = (float) l2 + 0.5F; + float f1 = (float) i3; + float f2 = (float) j3 + 0.5F; + + if (worldserver.findNearbyPlayerWhoAffectsSpawning((double) f, (double) f1, (double) f2, 24.0D) == null) { // PaperSpigot + float f3 = f - (float) chunkcoordinates.x; + float f4 = f1 - (float) chunkcoordinates.y; + float f5 = f2 - (float) chunkcoordinates.z; + float f6 = f3 * f3 + f4 * f4 + f5 * f5; + + if (f6 >= 576.0F) { + if (biomemeta == null) { + biomemeta = worldserver.a(enumcreaturetype, l2, i3, j3); + if (biomemeta == null) { + break label103; + } + } + + EntityInsentient entityinsentient; + + try { + entityinsentient = (EntityInsentient) biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { worldserver}); + } catch (Exception exception) { + exception.printStackTrace(); + return i; + } + + entityinsentient.setPositionRotation((double) f, (double) f1, (double) f2, worldserver.random.nextFloat() * 360.0F, 0.0F); + if (entityinsentient.canSpawn()) { + ++j2; + // CraftBukkit start - Added a reason for spawning this creature, moved entityinsentient.a(groupdataentity) up + groupdataentity = entityinsentient.prepare(groupdataentity); + worldserver.addEntity(entityinsentient, SpawnReason.NATURAL); + // CraftBukkit end + // Spigot start + if ( --moblimit <= 0 ) + { + // If we're past limit, stop spawn + continue label110; + } + // Spigot end + if (j2 >= entityinsentient.bB()) { + continue label110; + } + } + + i += j2; + } + } + } + + ++k3; + continue; + } + } + + ++k2; + break; + } + } + } + } + } + } + } + + return i; + } + } + + public static boolean a(EnumCreatureType enumcreaturetype, World world, int i, int j, int k) { + if (enumcreaturetype.c() == Material.WATER) { + return world.getType(i, j, k).getMaterial().isLiquid() && world.getType(i, j - 1, k).getMaterial().isLiquid() && !world.getType(i, j + 1, k).r(); + } else if (!World.a((IBlockAccess) world, i, j - 1, k)) { + return false; + } else { + Block block = world.getType(i, j - 1, k); + + return block != Blocks.BEDROCK && !world.getType(i, j, k).r() && !world.getType(i, j, k).getMaterial().isLiquid() && !world.getType(i, j + 1, k).r(); + } + } + + public static void a(World world, BiomeBase biomebase, int i, int j, int k, int l, Random random) { + List list = biomebase.getMobs(EnumCreatureType.CREATURE); + + if (!list.isEmpty()) { + while (random.nextFloat() < biomebase.g()) { + BiomeMeta biomemeta = (BiomeMeta) WeightedRandom.a(world.random, (Collection) list); + GroupDataEntity groupdataentity = null; + int i1 = biomemeta.c + random.nextInt(1 + biomemeta.d - biomemeta.c); + int j1 = i + random.nextInt(k); + int k1 = j + random.nextInt(l); + int l1 = j1; + int i2 = k1; + + for (int j2 = 0; j2 < i1; ++j2) { + boolean flag = false; + + for (int k2 = 0; !flag && k2 < 4; ++k2) { + int l2 = world.i(j1, k1); + + if (a(EnumCreatureType.CREATURE, world, j1, l2, k1)) { + float f = (float) j1 + 0.5F; + float f1 = (float) l2; + float f2 = (float) k1 + 0.5F; + + EntityInsentient entityinsentient; + + try { + entityinsentient = (EntityInsentient) biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { world}); + } catch (Exception exception) { + exception.printStackTrace(); + continue; + } + + entityinsentient.setPositionRotation((double) f, (double) f1, (double) f2, random.nextFloat() * 360.0F, 0.0F); + // CraftBukkit start - Added a reason for spawning this creature, moved entityinsentient.a(groupdataentity) up + groupdataentity = entityinsentient.prepare(groupdataentity); + world.addEntity(entityinsentient, SpawnReason.CHUNK_GEN); + // CraftBukkit end + flag = true; + } + + j1 += random.nextInt(5) - random.nextInt(5); + + for (k1 += random.nextInt(5) - random.nextInt(5); j1 < i || j1 >= i + k || k1 < j || k1 >= j + k; k1 = i2 + random.nextInt(5) - random.nextInt(5)) { + j1 = l1 + random.nextInt(5) - random.nextInt(5); + } + } + } + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/StatisticManager.java b/vspigot-server/src/main/java/net/minecraft/server/StatisticManager.java new file mode 100644 index 0000000..d41dc4d --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/StatisticManager.java @@ -0,0 +1,68 @@ +package net.minecraft.server; + +import java.util.Map; + +import net.minecraft.util.com.google.common.collect.Maps; + +public class StatisticManager { + + protected final Map a = Maps.newConcurrentMap(); + + public StatisticManager() { + } + + public boolean hasAchievement(Achievement achievement) { + return this.getStatisticValue((Statistic) achievement) > 0; + } + + public boolean b(Achievement achievement) { + return achievement.c == null || this.hasAchievement(achievement.c); + } + + public void b(EntityHuman entityhuman, Statistic statistic, int i) { + if (!statistic.d() || this.b((Achievement) statistic)) { + // CraftBukkit start - fire Statistic events + org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(entityhuman, statistic, this.getStatisticValue(statistic), i); + if (cancellable != null && cancellable.isCancelled()) { + return; + } + // CraftBukkit end + this.setStatistic(entityhuman, statistic, this.getStatisticValue(statistic) + i); + } + } + + public void setStatistic(EntityHuman entityhuman, Statistic statistic, int i) { + StatisticWrapper statisticwrapper = (StatisticWrapper) this.a.get(statistic); + + if (statisticwrapper == null) { + statisticwrapper = new StatisticWrapper(); + this.a.put(statistic, statisticwrapper); + } + + statisticwrapper.a(i); + } + + public int getStatisticValue(Statistic statistic) { + StatisticWrapper statisticwrapper = (StatisticWrapper) this.a.get(statistic); + + return statisticwrapper == null ? 0 : statisticwrapper.a(); + } + + public IJsonStatistic b(Statistic statistic) { + StatisticWrapper statisticwrapper = (StatisticWrapper) this.a.get(statistic); + + return statisticwrapper != null ? statisticwrapper.b() : null; + } + + public IJsonStatistic a(Statistic statistic, IJsonStatistic ijsonstatistic) { + StatisticWrapper statisticwrapper = (StatisticWrapper) this.a.get(statistic); + + if (statisticwrapper == null) { + statisticwrapper = new StatisticWrapper(); + this.a.put(statistic, statisticwrapper); + } + + statisticwrapper.a(ijsonstatistic); + return ijsonstatistic; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/StructureGenerator.java b/vspigot-server/src/main/java/net/minecraft/server/StructureGenerator.java new file mode 100644 index 0000000..6d0cea8 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/StructureGenerator.java @@ -0,0 +1,229 @@ +package net.minecraft.server; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.Callable; + +public abstract class StructureGenerator extends WorldGenBase { + + private PersistentStructure e; + protected Map d = new HashMap(); + + public StructureGenerator() {} + + public abstract String a(); + + protected final void a(World world, int i, int j, int k, int l, Block[] ablock) { + this.a(world); + if (!this.d.containsKey(Long.valueOf(ChunkCoordIntPair.a(i, j)))) { + this.b.nextInt(); + + try { + if (this.a(i, j)) { + StructureStart structurestart = this.b(i, j); + + this.d.put(Long.valueOf(ChunkCoordIntPair.a(i, j)), structurestart); + this.a(i, j, structurestart); + } + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception preparing structure feature"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Feature being prepared"); + + crashreportsystemdetails.a("Is feature chunk", (Callable) (new CrashReportIsFeatureChunk(this, i, j))); + crashreportsystemdetails.a("Chunk location", String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)})); + crashreportsystemdetails.a("Chunk pos hash", (Callable) (new CrashReportChunkPosHash(this, i, j))); + crashreportsystemdetails.a("Structure type", (Callable) (new CrashReportStructureType(this))); + throw new ReportedException(crashreport); + } + } + } + + public boolean a(World world, Random random, int i, int j) { + this.a(world); + int k = (i << 4) + 8; + int l = (j << 4) + 8; + boolean flag = false; + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { + StructureStart structurestart = (StructureStart) iterator.next(); + + if (structurestart.d() && structurestart.a().a(k, l, k + 15, l + 15)) { + structurestart.a(world, random, new StructureBoundingBox(k, l, k + 15, l + 15)); + flag = true; + this.a(structurestart.e(), structurestart.f(), structurestart); + } + } + + return flag; + } + + public boolean b(int i, int j, int k) { + if (this.c == null) return false; // PaperSpigot + this.a(this.c); + return this.c(i, j, k) != null; + } + + protected StructureStart c(int i, int j, int k) { + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { + StructureStart structurestart = (StructureStart) iterator.next(); + + if (structurestart.d() && structurestart.a().a(i, k, i, k)) { + Iterator iterator1 = structurestart.b().iterator(); + + while (iterator1.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator1.next(); + + if (structurepiece.c().b(i, j, k)) { + return structurestart; + } + } + } + } + + return null; + } + + public boolean d(int i, int j, int k) { + if (this.c == null) return false; // PaperSpigot + this.a(this.c); + Iterator iterator = this.d.values().iterator(); + + StructureStart structurestart; + + do { + if (!iterator.hasNext()) { + return false; + } + + structurestart = (StructureStart) iterator.next(); + } while (!structurestart.d()); + + return structurestart.a().a(i, k, i, k); + } + + public ChunkPosition getNearestGeneratedFeature(World world, int i, int j, int k) { + this.c = world; + this.a(world); + this.b.setSeed(world.getSeed()); + long l = this.b.nextLong(); + long i1 = this.b.nextLong(); + long j1 = (long) (i >> 4) * l; + long k1 = (long) (k >> 4) * i1; + + this.b.setSeed(j1 ^ k1 ^ world.getSeed()); + this.a(world, i >> 4, k >> 4, 0, 0, (Block[]) null); + double d0 = Double.MAX_VALUE; + ChunkPosition chunkposition = null; + Iterator iterator = this.d.values().iterator(); + + ChunkPosition chunkposition1; + int l1; + int i2; + double d1; + int j2; + + while (iterator.hasNext()) { + StructureStart structurestart = (StructureStart) iterator.next(); + + if (structurestart.d()) { + StructurePiece structurepiece = (StructurePiece) structurestart.b().get(0); + + chunkposition1 = structurepiece.a(); + i2 = chunkposition1.x - i; + l1 = chunkposition1.y - j; + j2 = chunkposition1.z - k; + d1 = (double) (i2 * i2 + l1 * l1 + j2 * j2); + if (d1 < d0) { + d0 = d1; + chunkposition = chunkposition1; + } + } + } + + if (chunkposition != null) { + return chunkposition; + } else { + List list = this.o_(); + + if (list != null) { + ChunkPosition chunkposition2 = null; + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + chunkposition1 = (ChunkPosition) iterator1.next(); + i2 = chunkposition1.x - i; + l1 = chunkposition1.y - j; + j2 = chunkposition1.z - k; + d1 = (double) (i2 * i2 + l1 * l1 + j2 * j2); + if (d1 < d0) { + d0 = d1; + chunkposition2 = chunkposition1; + } + } + + return chunkposition2; + } else { + return null; + } + } + } + + protected List o_() { + return null; + } + + private void a(World world) { + if (this.e == null) { + // Spigot Start + if ( world.spigotConfig.saveStructureInfo && !this.a().equals( "Mineshaft" ) ) + { + this.e = (PersistentStructure) world.a(PersistentStructure.class, this.a()); + } else + { + this.e = new PersistentStructure( this.a() ); + } + // Spigot End + if (this.e == null) { + this.e = new PersistentStructure(this.a()); + world.a(this.a(), (PersistentBase) this.e); + } else { + NBTTagCompound nbttagcompound = this.e.a(); + Iterator iterator = nbttagcompound.c().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + NBTBase nbtbase = nbttagcompound.get(s); + + if (nbtbase.getTypeId() == 10) { + NBTTagCompound nbttagcompound1 = (NBTTagCompound) nbtbase; + + if (nbttagcompound1.hasKey("ChunkX") && nbttagcompound1.hasKey("ChunkZ")) { + int i = nbttagcompound1.getInt("ChunkX"); + int j = nbttagcompound1.getInt("ChunkZ"); + StructureStart structurestart = WorldGenFactory.a(nbttagcompound1, world); + + if (structurestart != null) { + this.d.put(Long.valueOf(ChunkCoordIntPair.a(i, j)), structurestart); + } + } + } + } + } + } + } + + private void a(int i, int j, StructureStart structurestart) { + this.e.a(structurestart.a(i, j), i, j); + this.e.c(); + } + + protected abstract boolean a(int i, int j); + + protected abstract StructureStart b(int i, int j); +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ThreadCommandReader.java b/vspigot-server/src/main/java/net/minecraft/server/ThreadCommandReader.java new file mode 100644 index 0000000..a5e8a45 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ThreadCommandReader.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import static org.bukkit.craftbukkit.Main.*; // CraftBukkit + +class ThreadCommandReader extends Thread { + + final DedicatedServer server; + + ThreadCommandReader(DedicatedServer dedicatedserver, String s) { + super(s); + this.server = dedicatedserver; + } + + public void run() { + // CraftBukkit start + if (!useConsole) { + return; + } + // CraftBukkit end + + jline.console.ConsoleReader bufferedreader = this.server.reader; // CraftBukkit + String s; + + try { + // CraftBukkit start - JLine disabling compatibility + while (!this.server.isStopped() && this.server.isRunning()) { + if (useJline) { + s = bufferedreader.readLine(">", null); + } else { + s = bufferedreader.readLine(); + } + if (s != null) { + this.server.issueCommand(s, this.server); + } + // CraftBukkit end + } + } catch (IOException ioexception) { + DedicatedServer.aF().error("Exception handling console input", ioexception); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/ThreadPlayerLookupUUID.java b/vspigot-server/src/main/java/net/minecraft/server/ThreadPlayerLookupUUID.java new file mode 100644 index 0000000..4f120cb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/ThreadPlayerLookupUUID.java @@ -0,0 +1,140 @@ +package net.minecraft.server; + +import java.math.BigInteger; +import java.util.UUID; + +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.exceptions.AuthenticationUnavailableException; + +// CraftBukkit start +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerPreLoginEvent; +// CraftBukkit end + +// Poweruser start +import java.security.PrivateKey; +import java.util.Arrays; +// Poweruser end + +class ThreadPlayerLookupUUID implements Runnable { // Poweruser + + final LoginListener a; + + // Poweruser start + final PacketLoginInEncryptionBegin packetlogininencryptionbegin; + + ThreadPlayerLookupUUID(LoginListener loginlistener, PacketLoginInEncryptionBegin packetlogininencryptionbegin) { // Poweruser + this.a = loginlistener; + this.packetlogininencryptionbegin = packetlogininencryptionbegin; + } + + public ThreadPlayerLookupUUID(LoginListener loginlistener) { + this(loginlistener, null); + } + // Poweruser end + + public void run() { + // Poweruser start + if (this.packetlogininencryptionbegin != null) { + try { + PrivateKey privatekey = MinecraftServer.getServer().K().getPrivate(); + if (this.a.compareRandomConnectionKey(this.packetlogininencryptionbegin.b(privatekey))) { + this.a.setLoginKey(packetlogininencryptionbegin.a(privatekey)); + LoginListener.a(this.a, EnumProtocolState.AUTHENTICATING); + this.a.networkManager.a(LoginListener.d(this.a)); + } else { + throw new IllegalStateException("Invalid nonce!"); + } + } catch (Exception e) { + this.a.caughtAuthenticationException(e); + return; + } + } + // Poweruser end + + GameProfile gameprofile = LoginListener.a(this.a); + + try { + // Spigot Start + if ( !LoginListener.c( this.a ).getOnlineMode() ) + { + a.initUUID(); + fireLoginEvents(); + return; + } + // Spigot End + String s = (new BigInteger(MinecraftEncryption.a(LoginListener.b(this.a), LoginListener.c(this.a).K().getPublic(), LoginListener.d(this.a)))).toString(16); + + LoginListener.a(this.a, LoginListener.c(this.a).av().hasJoinedServer(new GameProfile((UUID) null, gameprofile.getName()), s)); + if (LoginListener.a(this.a) != null) { + fireLoginEvents(); // Spigot + } else if (LoginListener.c(this.a).N()) { + LoginListener.e().warn("Failed to verify username but will let them in anyway!"); + LoginListener.a(this.a, this.a.a(gameprofile)); + LoginListener.a(this.a, EnumProtocolState.READY_TO_ACCEPT); + } else { + this.a.disconnect("Failed to verify username!"); + LoginListener.e().error("Username \'" + LoginListener.a(this.a).getName() + "\' tried to join with an invalid session"); + } + } catch (AuthenticationUnavailableException authenticationunavailableexception) { + if (LoginListener.c(this.a).N()) { + LoginListener.e().warn("Authentication servers are down but will let them in anyway!"); + LoginListener.a(this.a, this.a.a(gameprofile)); + LoginListener.a(this.a, EnumProtocolState.READY_TO_ACCEPT); + } else { + this.a.disconnect("Authentication servers are down. Please try again later, sorry!"); + LoginListener.e().error("Couldn\'t verify username because servers are unavailable"); + } + // CraftBukkit start - catch all exceptions + } catch (Exception exception) { + this.a.disconnect("Failed to verify username!"); + LoginListener.c(this.a).server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + LoginListener.a(this.a).getName(), exception); + // CraftBukkit end + } + } + + private void fireLoginEvents() throws Exception + { + // CraftBukkit start - fire PlayerPreLoginEvent + if (!this.a.networkManager.isConnected()) { + return; + } + + String playerName = LoginListener.a(this.a).getName(); + java.net.InetAddress address = ((java.net.InetSocketAddress) a.networkManager.getSocketAddress()).getAddress(); + java.util.UUID uniqueId = LoginListener.a(this.a).getId(); + final org.bukkit.craftbukkit.CraftServer server = LoginListener.c(this.a).server; + + AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId); + server.getPluginManager().callEvent(asyncEvent); + + if (PlayerPreLoginEvent.getHandlerList().getRegisteredListeners().length != 0) { + final PlayerPreLoginEvent event = new PlayerPreLoginEvent(playerName, address, uniqueId); + if (asyncEvent.getResult() != PlayerPreLoginEvent.Result.ALLOWED) { + event.disallow(asyncEvent.getResult(), asyncEvent.getKickMessage()); + } + Waitable waitable = new Waitable() { + @Override + protected PlayerPreLoginEvent.Result evaluate() { + server.getPluginManager().callEvent(event); + return event.getResult(); + }}; + + LoginListener.c(this.a).processQueue.add(waitable); + if (waitable.get() != PlayerPreLoginEvent.Result.ALLOWED) { + this.a.disconnect(event.getKickMessage()); + return; + } + } else { + if (asyncEvent.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { + this.a.disconnect(asyncEvent.getKickMessage()); + return; + } + } + // CraftBukkit end + + //LoginListener.e().info("UUID of player " + LoginListener.a(this.a).getName() + " is " + LoginListener.a(this.a).getId()); + LoginListener.a(this.a, EnumProtocolState.READY_TO_ACCEPT); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntity.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntity.java new file mode 100644 index 0000000..fd6f9ad --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntity.java @@ -0,0 +1,239 @@ +package net.minecraft.server; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.spigotmc.CustomTimingsHandler; // Spigot +import org.bukkit.inventory.InventoryHolder; // CraftBukkit + +public class TileEntity { + + public CustomTimingsHandler tickTimer = org.bukkit.craftbukkit.SpigotTimings.getTileEntityTimings(this); // Spigot + private static final Logger a = LogManager.getLogger(); + private static Map i = new HashMap(); + private static Map j = new HashMap(); + protected World world; + public int x; + public int y; + public int z; + protected boolean f; + public int g = -1; + public Block h; + + // Spigot start + // Helper method for scheduleTicks. If the hopper at x0, y0, z0 is pointed + // at this tile entity, then make it active. + private void scheduleTick(int x0, int y0, int z0) { + TileEntity tileEntity = this.world.getTileEntity(x0, y0, z0); + if (tileEntity instanceof TileEntityHopper && tileEntity.world != null) { + // i is the metadeta assoiated with the direction the hopper faces. + int i = BlockHopper.b(tileEntity.p()); + // Facing class provides arrays for direction offset. + if (tileEntity.x + Facing.b[i] == this.x && tileEntity.y + Facing.c[i] == this.y && tileEntity.z + Facing.d[i] == this.z) { + ((TileEntityHopper) tileEntity).makeTick(); + } + } + } + + // Called from update when the contents have changed, so hoppers need updates. + // Check all 6 faces. + public void scheduleTicks() { + if (this.world != null && this.world.spigotConfig.altHopperTicking) { + // Check the top + this.scheduleTick(this.x, this.y + 1, this.z); + // Check the sides + for (int i = 2; i < 6; i++) { + this.scheduleTick(this.x + Facing.b[i], this.y, this.z + Facing.d[i]); + } + // Check the bottom. + TileEntity tileEntity = this.world.getTileEntity(this.x, this.y - 1, this.z); + if (tileEntity instanceof TileEntityHopper && tileEntity.world != null) { + ((TileEntityHopper) tileEntity).makeTick(); + } + } + } + + // Optimized TileEntity Tick changes + private static int tileEntityCounter = 0; + public boolean isAdded = false; + public int tileId = tileEntityCounter++; + + // Spigot end + + public TileEntity() {} + + private static void a(Class oclass, String s) { + if (i.containsKey(s)) { + throw new IllegalArgumentException("Duplicate id: " + s); + } else { + i.put(s, oclass); + j.put(oclass, s); + } + } + + public World getWorld() { + return this.world; + } + + public void a(World world) { + this.world = world; + } + + public boolean o() { + return this.world != null; + } + + public void a(NBTTagCompound nbttagcompound) { + this.x = nbttagcompound.getInt("x"); + this.y = nbttagcompound.getInt("y"); + this.z = nbttagcompound.getInt("z"); + } + + public void b(NBTTagCompound nbttagcompound) { + String s = (String) j.get(this.getClass()); + + if (s == null) { + throw new RuntimeException(this.getClass() + " is missing a mapping! This is a bug!"); + } else { + nbttagcompound.setString("id", s); + nbttagcompound.setInt("x", this.x); + nbttagcompound.setInt("y", this.y); + nbttagcompound.setInt("z", this.z); + } + } + + public void h() {} + + public static TileEntity c(NBTTagCompound nbttagcompound) { + TileEntity tileentity = null; + + try { + Class oclass = (Class) i.get(nbttagcompound.getString("id")); + + if (oclass != null) { + tileentity = (TileEntity) oclass.newInstance(); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + + if (tileentity != null) { + tileentity.a(nbttagcompound); + } else { + a.warn("Skipping BlockEntity with id " + nbttagcompound.getString("id")); + } + + return tileentity; + } + + public int p() { + if (this.g == -1) { + this.g = this.world.getData(this.x, this.y, this.z); + } + + return this.g; + } + + public void update() { + if (this.world != null) { + this.g = this.world.getData(this.x, this.y, this.z); + this.world.b(this.x, this.y, this.z, this); + if (this.q() != Blocks.AIR) { + this.world.updateAdjacentComparators(this.x, this.y, this.z, this.q()); + } + // Spigot start - Called when the contents have changed, so hoppers around this + // tile need updating. + this.scheduleTicks(); + // Spigot end + } + } + + public Block q() { + if (this.h == null) { + this.h = this.world.getType(this.x, this.y, this.z); + } + + return this.h; + } + + public Packet getUpdatePacket() { + return null; + } + + public boolean r() { + return this.f; + } + + public void s() { + this.f = true; + } + + public void t() { + this.f = false; + } + + public boolean c(int i, int j) { + return false; + } + + public void u() { + this.h = null; + this.g = -1; + } + + public void a(CrashReportSystemDetails crashreportsystemdetails) { + crashreportsystemdetails.a("Name", (Callable) (new CrashReportTileEntityName(this))); + Block block = this.q(); // PaperSpigot + if (block != null) { // PaperSpigot + CrashReportSystemDetails.a(crashreportsystemdetails, this.x, this.y, this.z, this.q(), this.p()); + } // PaperSpigot + crashreportsystemdetails.a("Actual block type", (Callable) (new CrashReportTileEntityType(this))); + crashreportsystemdetails.a("Actual block data value", (Callable) (new CrashReportTileEntityData(this))); + } + + static Map v() { + return j; + } + + static { + a(TileEntityFurnace.class, "Furnace"); + a(TileEntityChest.class, "Chest"); + a(TileEntityEnderChest.class, "EnderChest"); + a(TileEntityRecordPlayer.class, "RecordPlayer"); + a(TileEntityDispenser.class, "Trap"); + a(TileEntityDropper.class, "Dropper"); + a(TileEntitySign.class, "Sign"); + a(TileEntityMobSpawner.class, "MobSpawner"); + a(TileEntityNote.class, "Music"); + a(TileEntityPiston.class, "Piston"); + a(TileEntityBrewingStand.class, "Cauldron"); + a(TileEntityEnchantTable.class, "EnchantTable"); + a(TileEntityEnderPortal.class, "Airportal"); + a(TileEntityCommand.class, "Control"); + a(TileEntityBeacon.class, "Beacon"); + a(TileEntitySkull.class, "Skull"); + a(TileEntityLightDetector.class, "DLDetector"); + a(TileEntityHopper.class, "Hopper"); + a(TileEntityComparator.class, "Comparator"); + a(TileEntityFlowerPot.class, "FlowerPot"); + } + + // CraftBukkit start - add method + public InventoryHolder getOwner() { + // Spigot start + org.bukkit.block.Block block = world.getWorld().getBlockAt(x, y, z); + if (block == null) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "No block for owner at %s %d %d %d", new Object[]{world.getWorld(), x, y, z}); + return null; + } + // Spigot end + org.bukkit.block.BlockState state = block.getState(); + if (state instanceof InventoryHolder) return (InventoryHolder) state; + return null; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityBeacon.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityBeacon.java new file mode 100644 index 0000000..bdcff4e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityBeacon.java @@ -0,0 +1,301 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +// PaperSpigot start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BeaconEffectEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +// PaperSpigot end + +public class TileEntityBeacon extends TileEntity implements IInventory { + + public static final MobEffectList[][] a = new MobEffectList[][] { { MobEffectList.FASTER_MOVEMENT, MobEffectList.FASTER_DIG}, { MobEffectList.RESISTANCE, MobEffectList.JUMP}, { MobEffectList.INCREASE_DAMAGE}, { MobEffectList.REGENERATION}}; + private boolean k; + private int l = -1; + private int m; + private int n; + private ItemStack inventorySlot; + private String p; + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return new ItemStack[] { this.inventorySlot }; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityBeacon() {} + + public void h() { + if (true || this.world.getTime() % 80L == 0L) { // PaperSpigot - controlled by Improved Tick handling + this.y(); + this.x(); + } + } + + private void x() { + if (this.k && this.l > 0 && !this.world.isStatic && this.m > 0) { + double d0 = (double) (this.l * 10 + 10); + byte b0 = 0; + + if (this.l >= 4 && this.m == this.n) { + b0 = 1; + } + + AxisAlignedBB axisalignedbb = AxisAlignedBB.a((double) this.x, (double) this.y, (double) this.z, (double) (this.x + 1), (double) (this.y + 1), (double) (this.z + 1)).grow(d0, d0, d0); + + axisalignedbb.e = (double) this.world.getHeight(); + List list = this.world.a(EntityHuman.class, axisalignedbb); + Iterator iterator = list.iterator(); + + EntityHuman entityhuman; + // PaperSpigot start + org.bukkit.block.Block block = world.getWorld().getBlockAt(x, y, z); + PotionEffect primaryEffect = new PotionEffect(PotionEffectType.getById(this.m), 180, b0, true); + // PaperSpigot end + + while (iterator.hasNext()) { + entityhuman = (EntityHuman) iterator.next(); + // PaperSpigot start - BeaconEffectEvent + BeaconEffectEvent event = new BeaconEffectEvent(block, primaryEffect, (Player) entityhuman.getBukkitEntity(), true); + if (CraftEventFactory.callEvent(event).isCancelled()) continue; + + PotionEffect effect = event.getEffect(); + entityhuman.addEffect(new MobEffect(effect.getType().getId(), effect.getDuration(), effect.getAmplifier(), effect.isAmbient())); + // PaperSpigot end + } + + if (this.l >= 4 && this.m != this.n && this.n > 0) { + iterator = list.iterator(); + PotionEffect secondaryEffect = new PotionEffect(PotionEffectType.getById(this.n), 180, 0, true); // PaperSpigot + + while (iterator.hasNext()) { + entityhuman = (EntityHuman) iterator.next(); + // PaperSpigot start - BeaconEffectEvent + BeaconEffectEvent event = new BeaconEffectEvent(block, secondaryEffect, (Player) entityhuman.getBukkitEntity(), false); + if (CraftEventFactory.callEvent(event).isCancelled()) continue; + + PotionEffect effect = event.getEffect(); + entityhuman.addEffect(new MobEffect(effect.getType().getId(), effect.getDuration(), effect.getAmplifier(), effect.isAmbient())); + // PaperSpigot end + } + } + } + } + + private void y() { + int i = this.l; + + if (!this.world.i(this.x, this.y + 1, this.z)) { + this.k = false; + this.l = 0; + } else { + this.k = true; + this.l = 0; + + for (int j = 1; j <= 4; this.l = j++) { + int k = this.y - j; + + if (k < 0) { + break; + } + + boolean flag = true; + + for (int l = this.x - j; l <= this.x + j && flag; ++l) { + for (int i1 = this.z - j; i1 <= this.z + j; ++i1) { + Block block = this.world.getType(l, k, i1); + + if (block != Blocks.EMERALD_BLOCK && block != Blocks.GOLD_BLOCK && block != Blocks.DIAMOND_BLOCK && block != Blocks.IRON_BLOCK) { + flag = false; + break; + } + } + } + + if (!flag) { + break; + } + } + + if (this.l == 0) { + this.k = false; + } + } + + if (!this.world.isStatic && this.l == 4 && i < this.l) { + Iterator iterator = this.world.a(EntityHuman.class, AxisAlignedBB.a((double) this.x, (double) this.y, (double) this.z, (double) this.x, (double) (this.y - 4), (double) this.z).grow(10.0D, 5.0D, 10.0D)).iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + entityhuman.a((Statistic) AchievementList.K); + } + } + } + + public int j() { + return this.m; + } + + public int k() { + return this.n; + } + + public int l() { + return this.l; + } + + public void d(int i) { + this.m = 0; + + for (int j = 0; j < this.l && j < 3; ++j) { + MobEffectList[] amobeffectlist = a[j]; + int k = amobeffectlist.length; + + for (int l = 0; l < k; ++l) { + MobEffectList mobeffectlist = amobeffectlist[l]; + + if (mobeffectlist.id == i) { + this.m = i; + return; + } + } + } + } + + public void e(int i) { + this.n = 0; + if (this.l >= 4) { + for (int j = 0; j < 4; ++j) { + MobEffectList[] amobeffectlist = a[j]; + int k = amobeffectlist.length; + + for (int l = 0; l < k; ++l) { + MobEffectList mobeffectlist = amobeffectlist[l]; + + if (mobeffectlist.id == i) { + this.n = i; + return; + } + } + } + } + } + + public Packet getUpdatePacket() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + this.b(nbttagcompound); + return new PacketPlayOutTileEntityData(this.x, this.y, this.z, 3, nbttagcompound); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.m = nbttagcompound.getInt("Primary"); + this.n = nbttagcompound.getInt("Secondary"); + this.l = nbttagcompound.getInt("Levels"); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Primary", this.m); + nbttagcompound.setInt("Secondary", this.n); + nbttagcompound.setInt("Levels", this.l); + } + + public int getSize() { + return 1; + } + + public ItemStack getItem(int i) { + return i == 0 ? this.inventorySlot : null; + } + + public ItemStack splitStack(int i, int j) { + if (i == 0 && this.inventorySlot != null) { + if (j >= this.inventorySlot.count) { + ItemStack itemstack = this.inventorySlot; + + this.inventorySlot = null; + return itemstack; + } else { + this.inventorySlot.count -= j; + return new ItemStack(this.inventorySlot.getItem(), j, this.inventorySlot.getData()); + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (i == 0 && this.inventorySlot != null) { + ItemStack itemstack = this.inventorySlot; + + this.inventorySlot = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + if (i == 0) { + this.inventorySlot = itemstack; + } + } + + public String getInventoryName() { + return this.k_() ? this.p : "container.beacon"; + } + + public boolean k_() { + return this.p != null && this.p.length() > 0; + } + + public void a(String s) { + this.p = s; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.x, this.y, this.z) != this ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return itemstack.getItem() == Items.EMERALD || itemstack.getItem() == Items.DIAMOND || itemstack.getItem() == Items.GOLD_INGOT || itemstack.getItem() == Items.IRON_INGOT; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityBrewingStand.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityBrewingStand.java new file mode 100644 index 0000000..f836e29 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityBrewingStand.java @@ -0,0 +1,320 @@ +package net.minecraft.server; + +import java.util.List; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.BrewEvent; +import io.kohi.kspigot.potion.PotionsConfig; +import org.bukkit.inventory.BrewerInventory; +import org.spigotmc.SpigotConfig; +// CraftBukkit end + +public class TileEntityBrewingStand extends TileEntity implements IWorldInventory { + + private static final int[] a = new int[]{3}; + private static final int[] i = new int[]{0, 1, 2}; + public ItemStack[] items = new ItemStack[4]; // CraftBukkit - private -> public + public int brewTime; // CraftBukkit - private -> public + private int l; + private Item m; + private String n; + private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field + + public TileEntityBrewingStand() { + } + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = 64; + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public ItemStack[] getContents() { + return this.items; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public String getInventoryName() { + return this.k_() ? this.n : "container.brewing"; + } + + public boolean k_() { + return this.n != null && this.n.length() > 0; + } + + public void a(String s) { + this.n = s; + } + + public int getSize() { + return this.items.length; + } + + public void h() { + // CraftBukkit start - Use wall time instead of ticks for brewing + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.lastTick = MinecraftServer.currentTick; + + if (this.brewTime > 0) { + this.brewTime -= elapsedTicks * SpigotConfig.brewingMultiplier; // MineHQ + if (this.brewTime <= 0) { // == -> <= + // CraftBukkit end + this.l(); + this.update(); + } else if (!this.k()) { + this.brewTime = 0; + this.update(); + } else if (this.m != this.items[3].getItem()) { + this.brewTime = 0; + this.update(); + } + } else if (this.k()) { + this.brewTime = 400; + this.m = this.items[3].getItem(); + } + + int i = this.j(); + + if (i != this.l) { + this.l = i; + this.world.setData(this.x, this.y, this.z, i, 2); + } + + super.h(); + } + + public int i() { + return this.brewTime; + } + + private boolean k() { + if (this.items[3] != null && this.items[3].count > 0) { + ItemStack itemstack = this.items[3]; + + if (!itemstack.getItem().m(itemstack)) { + return false; + } else { + boolean flag = false; + + for (int i = 0; i < 3; ++i) { + if (this.items[i] != null && this.items[i].getItem() == Items.POTION) { + int j = this.items[i].getData(); + int k = this.c(j, itemstack); + + if (k > 0 && PotionsConfig.isBrewingDisabled(k)) { + continue; + } + + if (!ItemPotion.g(j) && ItemPotion.g(k)) { + flag = true; + break; + } + + List list = Items.POTION.c(j); + List list1 = Items.POTION.c(k); + + if ((j <= 0 || list != list1) && (list == null || !list.equals(list1) && list1 != null) && j != k) { + flag = true; + break; + } + } + } + + return flag; + } + } else { + return false; + } + } + + + private void l() { + if (this.k()) { + final ItemStack itemstack = this.items[3]; + final ItemStack[] newItems = new ItemStack[this.items.length]; + for (int i = 0; i < this.items.length; ++i) { + final ItemStack next = this.items[i]; + newItems[i] = ((next == null) ? null : next.cloneItemStack()); + } + for (int i = 0; i < 3; ++i) { + if (newItems[i] != null && newItems[i].getItem() == Items.POTION) { + final int j = newItems[i].getData(); + final int k = this.c(j, itemstack); + final List list = Items.POTION.c(j); + final List list2 = Items.POTION.c(k); + if (k > 0 && PotionsConfig.isBrewingDisabled(k)) { + continue; + } + if ((j <= 0 || list != list2) && (list == null || (!list.equals(list2) && list2 != null))) { + if (j != k) { + newItems[i].setData(k); + } + } else if (!ItemPotion.g(j) && ItemPotion.g(k)) { + newItems[i].setData(k); + } + } + } + if (itemstack.getItem().u()) { + newItems[3] = new ItemStack(itemstack.getItem().t()); + } else { + final ItemStack itemStack = newItems[3]; + --itemStack.count; + if (newItems[3].count <= 0) { + newItems[3] = null; + } + } + if (BrewEvent.getHandlerList().getRegisteredListeners().length != 0 && this.getOwner() != null) { + final org.bukkit.inventory.ItemStack[] results = new org.bukkit.inventory.ItemStack[newItems.length]; + for (int l = 0; l < newItems.length; ++l) { + final ItemStack item = newItems[l]; + results[l] = ((item == null) ? null : CraftItemStack.asBukkitCopy(item)); + } + final BrewEvent event = new BrewEvent(this.world.getWorld().getBlockAt(this.x, this.y, this.z), (BrewerInventory) this.getOwner().getInventory(), results); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.brewTime = 400; + return; + } + } + this.items = newItems; + } + } + + private int c(int i, ItemStack itemstack) { + return itemstack == null ? i : (itemstack.getItem().m(itemstack) ? PotionBrewer.a(i, itemstack.getItem().i(itemstack)) : i); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.items = new ItemStack[this.getSize()]; + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + byte b0 = nbttagcompound1.getByte("Slot"); + + if (b0 >= 0 && b0 < this.items.length) { + this.items[b0] = ItemStack.createStack(nbttagcompound1); + } + } + + this.brewTime = nbttagcompound.getShort("BrewTime"); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.n = nbttagcompound.getString("CustomName"); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setShort("BrewTime", (short) this.brewTime); + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + this.items[i].save(nbttagcompound1); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + if (this.k_()) { + nbttagcompound.setString("CustomName", this.n); + } + } + + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.length ? this.items[i] : null; + } + + public ItemStack splitStack(int i, int j) { + if (i >= 0 && i < this.items.length) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (i >= 0 && i < this.items.length) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + if (i >= 0 && i < this.items.length) { + this.items[i] = itemstack; + } + } + + public int getMaxStackSize() { + return this.maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.x, this.y, this.z) != this ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } + + public void startOpen() { + } + + public void closeContainer() { + } + + public boolean b(int i, ItemStack itemstack) { + return i == 3 ? itemstack.getItem().m(itemstack) : itemstack.getItem() == Items.POTION || itemstack.getItem() == Items.GLASS_BOTTLE; + } + + public int j() { + int i = 0; + + for (int j = 0; j < 3; ++j) { + if (this.items[j] != null) { + i |= 1 << j; + } + } + + return i; + } + + public int[] getSlotsForFace(int i) { + return i == 1 ? a : TileEntityBrewingStand.i; // CraftBukkit - decompilation error + } + + public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, int j) { + return this.b(i, itemstack); + } + + public boolean canTakeItemThroughFace(int i, ItemStack itemstack, int j) { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityChest.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityChest.java new file mode 100644 index 0000000..69ffd30 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityChest.java @@ -0,0 +1,455 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class TileEntityChest extends TileEntity implements IInventory { + + private ItemStack[] items = new ItemStack[27]; // CraftBukkit - 36 -> 27 + public boolean a; + public TileEntityChest i; + public TileEntityChest j; + public TileEntityChest k; + public TileEntityChest l; + public float m; + public float n; + public int o; + private int ticks; + private int r = -1; + private String s; + + public TileEntityChest() {} + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public int getSize() { + return 27; + } + + public ItemStack getItem(int i) { + return this.items[i]; + } + + public ItemStack splitStack(int i, int j) { + if (this.items[i] != null) { + ItemStack itemstack; + + if (this.items[i].count <= j) { + itemstack = this.items[i]; + this.items[i] = null; + this.update(); + return itemstack; + } else { + itemstack = this.items[i].a(j); + if (this.items[i].count == 0) { + this.items[i] = null; + } + + this.update(); + return itemstack; + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.items[i] != null) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.items[i] = itemstack; + if (itemstack != null && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + + this.update(); + } + + public String getInventoryName() { + return this.k_() ? this.s : "container.chest"; + } + + public boolean k_() { + return this.s != null && this.s.length() > 0; + } + + public void a(String s) { + this.s = s; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.items = new ItemStack[this.getSize()]; + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.s = nbttagcompound.getString("CustomName"); + } + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + int j = nbttagcompound1.getByte("Slot") & 255; + + if (j >= 0 && j < this.items.length) { + this.items[j] = ItemStack.createStack(nbttagcompound1); + } + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + this.items[i].save(nbttagcompound1); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + if (this.k_()) { + nbttagcompound.setString("CustomName", this.s); + } + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + if (this.world == null) return true; // CraftBukkit + return this.world.getTileEntity(this.x, this.y, this.z) != this ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } + + public void u() { + super.u(); + this.a = false; + } + + private void a(TileEntityChest tileentitychest, int i) { + if (tileentitychest.r()) { + this.a = false; + } else if (this.a) { + switch (i) { + case 0: + if (this.l != tileentitychest) { + this.a = false; + } + break; + + case 1: + if (this.k != tileentitychest) { + this.a = false; + } + break; + + case 2: + if (this.i != tileentitychest) { + this.a = false; + } + break; + + case 3: + if (this.j != tileentitychest) { + this.a = false; + } + } + } + } + + public void i() { + if (!this.a) { + this.a = true; + this.i = null; + this.j = null; + this.k = null; + this.l = null; + if (this.a(this.x - 1, this.y, this.z)) { + this.k = (TileEntityChest) this.world.getTileEntity(this.x - 1, this.y, this.z); + } + + if (this.a(this.x + 1, this.y, this.z)) { + this.j = (TileEntityChest) this.world.getTileEntity(this.x + 1, this.y, this.z); + } + + if (this.a(this.x, this.y, this.z - 1)) { + this.i = (TileEntityChest) this.world.getTileEntity(this.x, this.y, this.z - 1); + } + + if (this.a(this.x, this.y, this.z + 1)) { + this.l = (TileEntityChest) this.world.getTileEntity(this.x, this.y, this.z + 1); + } + + if (this.i != null) { + this.i.a(this, 0); + } + + if (this.l != null) { + this.l.a(this, 2); + } + + if (this.j != null) { + this.j.a(this, 1); + } + + if (this.k != null) { + this.k.a(this, 3); + } + } + } + + private boolean a(int i, int j, int k) { + if (this.world == null) { + return false; + } else { + Block block = this.world.getType(i, j, k); + + return block instanceof BlockChest && ((BlockChest) block).a == this.j(); + } + } + + public void h() { + super.h(); + if (this.world == null) return; // CraftBukkit + this.i(); + ++this.ticks; + float f; + + if (!this.world.isStatic && this.o != 0 && (this.ticks + this.x + this.y + this.z) % 10 == 0) { // PaperSpigot Reduced 200 -> 10 interval due to reduced tick rate from Improved Tick Handling + this.o = 0; + f = 5.0F; + List list = this.world.a(EntityHuman.class, AxisAlignedBB.a((double) ((float) this.x - f), (double) ((float) this.y - f), (double) ((float) this.z - f), (double) ((float) (this.x + 1) + f), (double) ((float) (this.y + 1) + f), (double) ((float) (this.z + 1) + f))); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (entityhuman.activeContainer instanceof ContainerChest) { + IInventory iinventory = ((ContainerChest) entityhuman.activeContainer).e(); + + if (iinventory == this || iinventory instanceof InventoryLargeChest && ((InventoryLargeChest) iinventory).a((IInventory) this)) { + ++this.o; + } + } + } + } + + this.n = this.m; + + // PaperSpigot start - Move chest sound handling out of the tick loop + /* + f = 0.1F; + double d0; + + if (this.o > 0 && this.m == 0.0F && this.i == null && this.k == null) { + double d1 = (double) this.x + 0.5D; + + d0 = (double) this.z + 0.5D; + if (this.l != null) { + d0 += 0.5D; + } + + if (this.j != null) { + d1 += 0.5D; + } + + this.world.makeSound(d1, (double) this.y + 0.5D, d0, "random.chestopen", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + + if (this.o == 0 && this.m > 0.0F || this.o > 0 && this.m < 1.0F) { + float f1 = this.m; + + if (this.o > 0) { + this.m += f; + } else { + this.m -= f; + } + + if (this.m > 1.0F) { + this.m = 1.0F; + } + + float f2 = 0.5F; + + if (this.m < f2 && f1 >= f2 && this.i == null && this.k == null) { + d0 = (double) this.x + 0.5D; + double d2 = (double) this.z + 0.5D; + + if (this.l != null) { + d2 += 0.5D; + } + + if (this.j != null) { + d0 += 0.5D; + } + + this.world.makeSound(d0, (double) this.y + 0.5D, d2, "random.chestclosed", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + + if (this.m < 0.0F) { + this.m = 0.0F; + } + } + */ + // PaperSpigot end + } + + public boolean c(int i, int j) { + if (i == 1) { + this.o = j; + return true; + } else { + return super.c(i, j); + } + } + + public void startOpen() { + if (this.o < 0) { + this.o = 0; + } + + int oldPower = Math.max(0, Math.min(15, this.o)); // CraftBukkit - Get power before new viewer is added + + ++this.o; + if (this.world == null) return; // CraftBukkit + this.world.playBlockAction(this.x, this.y, this.z, this.q(), 1, this.o); + + // PaperSpigot start - Move chest open sound handling down to here + this.i(); + double d0; + + if (this.o > 0 && this.m == 0.0F && this.i == null && this.k == null) { + double d1 = (double) this.x + 0.5D; + + d0 = (double) this.z + 0.5D; + if (this.l != null) { + d0 += 0.5D; + } + + if (this.j != null) { + d1 += 0.5D; + } + + this.world.makeSound(d1, (double) this.y + 0.5D, d0, "random.chestopen", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + // PaperSpigot end + + // CraftBukkit start - Call redstone event + if (this.q() == Blocks.TRAPPED_CHEST) { + int newPower = Math.max(0, Math.min(15, this.o)); + + if (oldPower != newPower) { + org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, this.x, this.y, this.z, oldPower, newPower); + } + } + // CraftBukkit end + + this.world.applyPhysics(this.x, this.y, this.z, this.q()); + this.world.applyPhysics(this.x, this.y - 1, this.z, this.q()); + } + + public void closeContainer() { + if (this.q() instanceof BlockChest) { + int oldPower = Math.max(0, Math.min(15, this.o)); // CraftBukkit - Get power before new viewer is added + + --this.o; + if (this.world == null) return; // CraftBukkit + this.world.playBlockAction(this.x, this.y, this.z, this.q(), 1, this.o); + + // PaperSpigot start - Move chest close sound handling down to here + this.i(); + double d0; + + if (this.o == 0 && this.i == null && this.k == null) { + d0 = (double) this.x + 0.5D; + double d2 = (double) this.z + 0.5D; + + if (this.l != null) { + d2 += 0.5D; + } + + if (this.j != null) { + d0 += 0.5D; + } + + this.world.makeSound(d0, (double) this.y + 0.5D, d2, "random.chestclosed", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + // PaperSpigot end + + // CraftBukkit start - Call redstone event + if (this.q() == Blocks.TRAPPED_CHEST) { + int newPower = Math.max(0, Math.min(15, this.o)); + + if (oldPower != newPower) { + org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, this.x, this.y, this.z, oldPower, newPower); + } + } + // CraftBukkit end + + this.world.applyPhysics(this.x, this.y, this.z, this.q()); + this.world.applyPhysics(this.x, this.y - 1, this.z, this.q()); + } + } + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public void s() { + super.s(); + this.u(); + this.i(); + } + + public int j() { + if (this.r == -1) { + if (this.world == null || !(this.q() instanceof BlockChest)) { + return 0; + } + + this.r = ((BlockChest) this.q()).a; + } + + return this.r; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityCommandListener.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityCommandListener.java new file mode 100644 index 0000000..45cefda --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityCommandListener.java @@ -0,0 +1,29 @@ +package net.minecraft.server; + +// CraftBukkit - package-private -> public +public class TileEntityCommandListener extends CommandBlockListenerAbstract { + + final TileEntityCommand a; + + TileEntityCommandListener(TileEntityCommand tileentitycommand) { + this.a = tileentitycommand; + sender = new org.bukkit.craftbukkit.command.CraftBlockCommandSender(this); // CraftBukkit - add sender + } + + public ChunkCoordinates getChunkCoordinates() { + return new ChunkCoordinates(this.a.x, this.a.y, this.a.z); + } + + public World getWorld() { + return this.a.getWorld(); + } + + public void setCommand(String s) { + super.setCommand(s); + this.a.update(); + } + + public void e() { + this.a.getWorld().notify(this.a.x, this.a.y, this.a.z); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityDispenser.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityDispenser.java new file mode 100644 index 0000000..187cf3e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityDispenser.java @@ -0,0 +1,189 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class TileEntityDispenser extends TileEntity implements IInventory { + + private ItemStack[] items = new ItemStack[9]; + private Random j = new Random(); + protected String a; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityDispenser() {} + + public int getSize() { + return 9; + } + + public ItemStack getItem(int i) { + return this.items[i]; + } + + public ItemStack splitStack(int i, int j) { + if (this.items[i] != null) { + ItemStack itemstack; + + if (this.items[i].count <= j) { + itemstack = this.items[i]; + this.items[i] = null; + this.update(); + return itemstack; + } else { + itemstack = this.items[i].a(j); + if (this.items[i].count == 0) { + this.items[i] = null; + } + + this.update(); + return itemstack; + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.items[i] != null) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public int i() { + int i = -1; + int j = 1; + + for (int k = 0; k < this.items.length; ++k) { + if (this.items[k] != null && this.j.nextInt(j++) == 0) { + if (this.items[k].count == 0) continue; // CraftBukkit + i = k; + } + } + + return i; + } + + public void setItem(int i, ItemStack itemstack) { + this.items[i] = itemstack; + if (itemstack != null && itemstack.count < 0) itemstack.count = 0; // EMC + if (itemstack != null && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + + this.update(); + } + + public int addItem(ItemStack itemstack) { + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] == null || this.items[i].getItem() == null) { + this.setItem(i, itemstack); + return i; + } + } + + return -1; + } + + public String getInventoryName() { + return this.k_() ? this.a : "container.dispenser"; + } + + public void a(String s) { + this.a = s; + } + + public boolean k_() { + return this.a != null; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.items = new ItemStack[this.getSize()]; + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + int j = nbttagcompound1.getByte("Slot") & 255; + + if (j >= 0 && j < this.items.length) { + this.items[j] = ItemStack.createStack(nbttagcompound1); + } + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.a = nbttagcompound.getString("CustomName"); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + this.items[i].save(nbttagcompound1); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + if (this.k_()) { + nbttagcompound.setString("CustomName", this.a); + } + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.x, this.y, this.z) != this ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityEnderChest.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityEnderChest.java new file mode 100644 index 0000000..339e133 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityEnderChest.java @@ -0,0 +1,117 @@ +package net.minecraft.server; + +public class TileEntityEnderChest extends TileEntity { + + public float a; + public float i; + public int j; + private int k; + + public TileEntityEnderChest() {} + + public void h() { + super.h(); + if (++this.k % 4 == 0) { // PaperSpigot Reduced (20 * 4) -> 4 interval due to reduced tick rate from Improved Tick Handling + this.world.playBlockAction(this.x, this.y, this.z, Blocks.ENDER_CHEST, 1, this.j); + } + + this.i = this.a; + + // PaperSpigot start - Move chest sound handling out of the tick loop + /* + float f = 0.1F; + double d0; + + if (this.j > 0 && this.a == 0.0F) { + double d1 = (double) this.x + 0.5D; + + d0 = (double) this.z + 0.5D; + this.world.makeSound(d1, (double) this.y + 0.5D, d0, "random.chestopen", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + + if (this.j == 0 && this.a > 0.0F || this.j > 0 && this.a < 1.0F) { + float f1 = this.a; + + if (this.j > 0) { + this.a += f; + } else { + this.a -= f; + } + + if (this.a > 1.0F) { + this.a = 1.0F; + } + + float f2 = 0.5F; + + if (this.a < f2 && f1 >= f2) { + d0 = (double) this.x + 0.5D; + double d2 = (double) this.z + 0.5D; + + this.world.makeSound(d0, (double) this.y + 0.5D, d2, "random.chestclosed", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + + if (this.a < 0.0F) { + this.a = 0.0F; + } + } + */ + // PaperSpigot end + } + + public boolean c(int i, int j) { + if (i == 1) { + this.j = j; + return true; + } else { + return super.c(i, j); + } + } + + public void s() { + this.u(); + super.s(); + } + + public void a() { + ++this.j; + this.world.playBlockAction(this.x, this.y, this.z, Blocks.ENDER_CHEST, 1, this.j); + + // PaperSpigot start - Move chest open sound handling down to here + double d0; + + if (this.j > 0 && this.a == 0.0F) { + double d1 = (double) this.x + 0.5D; + + d0 = (double) this.z + 0.5D; + this.world.makeSound(d1, (double) this.y + 0.5D, d0, "random.chestopen", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + // PaperSpigot end + } + + public void b() { + --this.j; + this.world.playBlockAction(this.x, this.y, this.z, Blocks.ENDER_CHEST, 1, this.j); + + // PaperSpigot start - Move chest close sound handling down to here + float f = 0.1F; + double d0; + + if (this.j == 0 && this.a == 0.0F || this.j > 0 && this.a < 1.0F) { + float f1 = this.a; + d0 = (double) this.x + 0.5D; + double d2 = (double) this.z + 0.5D; + + this.world.makeSound(d0, (double) this.y + 0.5D, d2, "random.chestclosed", 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + + if (this.a < 0.0F) { + this.a = 0.0F; + } + } + // PaperSpigot end + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.x, this.y, this.z) != this ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityFurnace.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityFurnace.java new file mode 100644 index 0000000..f06ef7e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityFurnace.java @@ -0,0 +1,344 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.FurnaceBurnEvent; +import org.bukkit.event.inventory.FurnaceSmeltEvent; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.spigotmc.SpigotConfig; +// CraftBukkit end + +public class TileEntityFurnace extends TileEntity implements IWorldInventory { + + private static final int[] k = new int[] { 0}; + private static final int[] l = new int[] { 2, 1}; + private static final int[] m = new int[] { 1}; + private ItemStack[] items = new ItemStack[3]; + public int burnTime; + public int ticksForCurrentFuel; + public int cookTime; + private String o; + + // CraftBukkit start - add fields and methods + private int lastTick = MinecraftServer.currentTick; + private int maxStack = MAX_STACK; + public List transaction = new java.util.ArrayList(); + + public ItemStack[] getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityFurnace() {} + + public int getSize() { + return this.items.length; + } + + public ItemStack getItem(int i) { + return this.items[i]; + } + + public ItemStack splitStack(int i, int j) { + if (this.items[i] != null) { + ItemStack itemstack; + + if (this.items[i].count <= j) { + itemstack = this.items[i]; + this.items[i] = null; + return itemstack; + } else { + itemstack = this.items[i].a(j); + if (this.items[i].count == 0) { + this.items[i] = null; + } + + return itemstack; + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.items[i] != null) { + ItemStack itemstack = this.items[i]; + + this.items[i] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.items[i] = itemstack; + if (itemstack != null && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + } + + public String getInventoryName() { + return this.k_() ? this.o : "container.furnace"; + } + + public boolean k_() { + return this.o != null && this.o.length() > 0; + } + + public void a(String s) { + this.o = s; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.items = new ItemStack[this.getSize()]; + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + byte b0 = nbttagcompound1.getByte("Slot"); + + if (b0 >= 0 && b0 < this.items.length) { + this.items[b0] = ItemStack.createStack(nbttagcompound1); + } + } + + this.burnTime = nbttagcompound.getShort("BurnTime"); + this.cookTime = nbttagcompound.getShort("CookTime"); + this.ticksForCurrentFuel = fuelTime(this.items[1]); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.o = nbttagcompound.getString("CustomName"); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setShort("BurnTime", (short) this.burnTime); + nbttagcompound.setShort("CookTime", (short) this.cookTime); + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.items.length; ++i) { + if (this.items[i] != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + this.items[i].save(nbttagcompound1); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + if (this.k_()) { + nbttagcompound.setString("CustomName", this.o); + } + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean isBurning() { + return this.burnTime > 0; + } + + public void h() { + boolean flag = this.burnTime > 0; + boolean flag1 = false; + + // CraftBukkit start - Use wall time instead of ticks for cooking + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.lastTick = MinecraftServer.currentTick; + + // CraftBukkit - moved from below + if (this.isBurning() && this.canBurn()) { + this.cookTime += elapsedTicks * SpigotConfig.smeltingMultiplier; // MineHQ + if (this.cookTime >= 200) { + this.cookTime %= 200; + this.burn(); + flag1 = true; + } + } else { + this.cookTime = 0; + } + // CraftBukkit end + + if (this.burnTime > 0) { + this.burnTime -= elapsedTicks; // CraftBukkit - use elapsedTicks in place of constant + } + + if (!this.world.isStatic) { + if (this.burnTime != 0 || (this.items[1] != null && this.items[1].getItem() != Items.BUCKET) && this.items[0] != null) { // Poweruser - check for an empty bucket + // CraftBukkit start - Handle multiple elapsed ticks + if (this.burnTime <= 0 && this.canBurn()) { // CraftBukkit - == to <= + CraftItemStack fuel = CraftItemStack.asCraftMirror(this.items[1]); + + FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(this.world.getWorld().getBlockAt(this.x, this.y, this.z), fuel, fuelTime(this.items[1])); + this.world.getServer().getPluginManager().callEvent(furnaceBurnEvent); + + if (furnaceBurnEvent.isCancelled()) { + return; + } + + this.ticksForCurrentFuel = furnaceBurnEvent.getBurnTime(); + this.burnTime += this.ticksForCurrentFuel; + if (this.burnTime > 0 && furnaceBurnEvent.isBurning()) { + // CraftBukkit end + flag1 = true; + if (this.items[1] != null) { + --this.items[1].count; + if (this.items[1].count == 0) { + Item item = this.items[1].getItem().t(); + + this.items[1] = item != null ? new ItemStack(item) : null; + } + } + } + } + + /* CraftBukkit start - Moved up + if (this.isBurning() && this.canBurn()) { + ++this.cookTime; + if (this.cookTime == 200) { + this.cookTime = 0; + this.burn(); + flag1 = true; + } + } else { + this.cookTime = 0; + } + */ + } + + if (flag != this.burnTime > 0) { + flag1 = true; + BlockFurnace.a(this.burnTime > 0, this.world, this.x, this.y, this.z); + } + } + + if (flag1) { + this.update(); + } + } + + private boolean canBurn() { + if (this.items[0] == null) { + return false; + } else { + ItemStack itemstack = RecipesFurnace.getInstance().getResult(this.items[0]); + + // CraftBukkit - consider resultant count instead of current count + return itemstack == null ? false : (this.items[2] == null ? true : (!this.items[2].doMaterialsMatch(itemstack) ? false : (this.items[2].count + itemstack.count <= this.getMaxStackSize() && this.items[2].count < this.items[2].getMaxStackSize() ? true : this.items[2].count + itemstack.count <= itemstack.getMaxStackSize()))); + } + } + + public void burn() { + if (this.canBurn()) { + ItemStack itemstack = RecipesFurnace.getInstance().getResult(this.items[0]); + + // CraftBukkit start - fire FurnaceSmeltEvent + CraftItemStack source = CraftItemStack.asCraftMirror(this.items[0]); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack); + + FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(this.world.getWorld().getBlockAt(this.x, this.y, this.z), source, result); + this.world.getServer().getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { + return; + } + + result = furnaceSmeltEvent.getResult(); + itemstack = CraftItemStack.asNMSCopy(result); + + if (itemstack != null) { + if (this.items[2] == null) { + this.items[2] = itemstack; + } else if (CraftItemStack.asCraftMirror(this.items[2]).isSimilar(result)) { + this.items[2].count += itemstack.count; + } else { + return; + } + } + // CraftBukkit end + + --this.items[0].count; + if (this.items[0].count <= 0) { + this.items[0] = null; + } + } + } + + public static int fuelTime(ItemStack itemstack) { + if (itemstack == null) { + return 0; + } else { + Item item = itemstack.getItem(); + + if (item instanceof ItemBlock && Block.a(item) != Blocks.AIR) { + Block block = Block.a(item); + + if (block == Blocks.WOOD_STEP) { + return 150; + } + + if (block.getMaterial() == Material.WOOD) { + return 300; + } + + if (block == Blocks.COAL_BLOCK) { + return 16000; + } + } + + return item instanceof ItemTool && ((ItemTool) item).j().equals("WOOD") ? 200 : (item instanceof ItemSword && ((ItemSword) item).j().equals("WOOD") ? 200 : (item instanceof ItemHoe && ((ItemHoe) item).i().equals("WOOD") ? 200 : (item == Items.STICK ? 100 : (item == Items.COAL ? 1600 : (item == Items.LAVA_BUCKET ? 20000 : (item == Item.getItemOf(Blocks.SAPLING) ? 100 : (item == Items.BLAZE_ROD ? 2400 : 0))))))); + } + } + + public static boolean isFuel(ItemStack itemstack) { + return fuelTime(itemstack) > 0; + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.x, this.y, this.z) != this ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return i == 2 ? false : (i == 1 ? isFuel(itemstack) : true); + } + + public int[] getSlotsForFace(int i) { + return i == 0 ? l : (i == 1 ? k : m); + } + + public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, int j) { + return this.b(i, itemstack); + } + + public boolean canTakeItemThroughFace(int i, ItemStack itemstack, int j) { + return j != 0 || i != 1 || itemstack.getItem() == Items.BUCKET; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityHopper.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityHopper.java new file mode 100644 index 0000000..1513075 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityHopper.java @@ -0,0 +1,774 @@ +package net.minecraft.server; + +import java.util.List; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.event.inventory.InventoryPickupItemEvent; +import org.bukkit.inventory.Inventory; +// CraftBukkit end + +public class TileEntityHopper extends TileEntity implements IHopper { + + private ItemStack[] a = new ItemStack[5]; + private String i; + private int j = -1; + + // Spigot start + private long nextTick = -1; // Next tick this hopper will be ticked. + private long lastTick = -1; // Last tick this hopper was polled. + + // If this hopper is not cooling down, assaign a visible tick for next time. + public void makeTick() { + if (!this.j()) { + this.c(0); + } + } + + // Contents changed, so make this hopper active. + public void scheduleHopperTick() { + if (this.world != null && this.world.spigotConfig.altHopperTicking) { + this.makeTick(); + } + } + + // Called after this hopper is assaigned a world or when altHopperTicking is turned + // on from reload. + public void convertToScheduling() { + // j is the cooldown in ticks + this.c(this.j); + } + + // Called when alt hopper ticking is turned off from the reload command + public void convertToPolling() { + long cooldownDiff; + if (this.lastTick == this.world.getTime()) { + cooldownDiff = this.nextTick - this.world.getTime(); + } else { + cooldownDiff = this.nextTick - this.world.getTime() + 1; + } + this.c((int) Math.max(0, Math.min(cooldownDiff, Integer.MAX_VALUE))); + } + // Spigot end + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() { + return this.a; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityHopper() {} + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.a = new ItemStack[this.getSize()]; + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.i = nbttagcompound.getString("CustomName"); + } + + this.j = nbttagcompound.getInt("TransferCooldown"); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + byte b0 = nbttagcompound1.getByte("Slot"); + + if (b0 >= 0 && b0 < this.a.length) { + this.a[b0] = ItemStack.createStack(nbttagcompound1); + } + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.a.length; ++i) { + if (this.a[i] != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + this.a[i].save(nbttagcompound1); + nbttaglist.add(nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + // Spigot start - Need to write the correct cooldown to disk. We convert from long to int on saving. + if (this.world != null && this.world.spigotConfig.altHopperTicking) { + long cooldownDiff; + if (this.lastTick == this.world.getTime()) { + cooldownDiff = this.nextTick - this.world.getTime(); + } else { + cooldownDiff = this.nextTick - this.world.getTime() + 1; + } + nbttagcompound.setInt("TransferCooldown", (int) Math.max(0, Math.min(cooldownDiff, Integer.MAX_VALUE))); + } else { + // j is the cooldown in ticks. + nbttagcompound.setInt("TransferCooldown", this.j); + } + // Spigot end + if (this.k_()) { + nbttagcompound.setString("CustomName", this.i); + } + } + + public void update() { + super.update(); + // Spigot start - The contents have changed, so make this hopper active. + this.scheduleHopperTick(); + // Spigot end + } + + public int getSize() { + return this.a.length; + } + + public ItemStack getItem(int i) { + return this.a[i]; + } + + public ItemStack splitStack(int i, int j) { + if (this.a[i] != null) { + ItemStack itemstack; + + if (this.a[i].count <= j) { + itemstack = this.a[i]; + this.a[i] = null; + return itemstack; + } else { + itemstack = this.a[i].a(j); + if (this.a[i].count == 0) { + this.a[i] = null; + } + + return itemstack; + } + } else { + return null; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (this.a[i] != null) { + ItemStack itemstack = this.a[i]; + + this.a[i] = null; + return itemstack; + } else { + return null; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.a[i] = itemstack; + if (itemstack != null && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + } + + public String getInventoryName() { + return this.k_() ? this.i : "container.hopper"; + } + + public boolean k_() { + return this.i != null && this.i.length() > 0; + } + + public void a(String s) { + this.i = s; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.x, this.y, this.z) != this ? false : entityhuman.e((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64.0D; + } + + public void startOpen() {} + + public void closeContainer() {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public void h() { + if (this.world != null && !this.world.isStatic) { + // Spigot start + if (this.world.spigotConfig.altHopperTicking) { + this.lastTick = this.world.getTime(); + if (this.nextTick == this.world.getTime()) { + // Method that does the pushing and pulling. + this.i(); + } + } else { + --this.j; + if (!this.j()) { + this.c(0); + this.i(); + } + } + // Spigot end + } + } + + public boolean i() { + if (this.world != null && !this.world.isStatic) { + if (!this.j() && BlockHopper.c(this.p())) { + boolean flag = false; + + if (!this.k()) { + flag = this.y(); + } + + if (!this.l()) { + flag = suckInItems(this) || flag; + } + + if (flag) { + this.c(world.spigotConfig.hopperTransfer); // Spigot + this.update(); + return true; + } + } + + // Spigot start + if ( !world.spigotConfig.altHopperTicking && !this.j() ) + { + this.c( world.spigotConfig.hopperCheck ); + } + // Spigot end + return false; + } else { + return false; + } + } + + private boolean k() { + ItemStack[] aitemstack = this.a; + int i = aitemstack.length; + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = aitemstack[j]; + + if (itemstack != null) { + return false; + } + } + + return true; + } + + private boolean l() { + ItemStack[] aitemstack = this.a; + int i = aitemstack.length; + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = aitemstack[j]; + + if (itemstack == null || itemstack.count != itemstack.getMaxStackSize()) { + return false; + } + } + + return true; + } + + private boolean y() { + IInventory iinventory = this.z(); + + if (iinventory == null) { + return false; + } else { + int i = Facing.OPPOSITE_FACING[BlockHopper.b(this.p())]; + + if (this.a(iinventory, i)) { + return false; + } else { + for (int j = 0; j < this.getSize(); ++j) { + if (this.getItem(j) != null) { + ItemStack itemstack = this.getItem(j).cloneItemStack(); + + // Poweruser start + ItemStack copyOfItemBeingPushed = itemstack.cloneItemStack(); + copyOfItemBeingPushed.count = 1; + int possibleInventorySlot = doesInventoryHaveEnoughSpaceForItem(iinventory, copyOfItemBeingPushed, i); + if(possibleInventorySlot < 0) { + continue; + } + // Poweruser end + + // CraftBukkit start - Call event when pushing items into other inventories + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.splitStack(j, world.spigotConfig.hopperAmount)); // Spigot + + Inventory destinationInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + destinationInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); + this.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.setItem(j, itemstack); + this.c(world.spigotConfig.hopperTransfer); // Spigot + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack1 = addItem(iinventory, possibleInventorySlot, CraftItemStack.asNMSCopy(event.getItem()), i); // Poweruser + if (itemstack1 == null || itemstack1.count == 0) { + if (event.getItem().equals(oitemstack)) { + iinventory.update(); + } else { + this.setItem(j, itemstack); + } + // CraftBukkit end + return true; + } + itemstack.count -= origCount - itemstack1.count; // Spigot + this.setItem(j, itemstack); + } + } + + return false; + } + } + } + + private boolean a(IInventory iinventory, int i) { + if (iinventory instanceof IWorldInventory && i > -1) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(i); + + for (int j = 0; j < aint.length; ++j) { + ItemStack itemstack = iworldinventory.getItem(aint[j]); + + if (itemstack == null || itemstack.count != itemstack.getMaxStackSize()) { + return false; + } + } + } else { + int k = iinventory.getSize(); + + for (int l = 0; l < k; ++l) { + ItemStack itemstack1 = iinventory.getItem(l); + + if (itemstack1 == null || itemstack1.count != itemstack1.getMaxStackSize()) { + return false; + } + } + } + + return true; + } + + private static boolean b(IInventory iinventory, int i) { + if (iinventory instanceof IWorldInventory && i > -1) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(i); + + for (int j = 0; j < aint.length; ++j) { + if (iworldinventory.getItem(aint[j]) != null) { + return false; + } + } + } else { + int k = iinventory.getSize(); + + for (int l = 0; l < k; ++l) { + if (iinventory.getItem(l) != null) { + return false; + } + } + } + + return true; + } + + public static boolean suckInItems(IHopper ihopper) { + IInventory iinventory = getSourceInventory(ihopper); + + if (iinventory != null) { + byte b0 = 0; + + if (b(iinventory, b0)) { + return false; + } + + if (iinventory instanceof IWorldInventory && b0 > -1) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(b0); + + for (int i = 0; i < aint.length; ++i) { + if (tryTakeInItemFromSlot(ihopper, iinventory, aint[i], b0)) { + return true; + } + } + } else { + int j = iinventory.getSize(); + + for (int k = 0; k < j; ++k) { + if (tryTakeInItemFromSlot(ihopper, iinventory, k, b0)) { + return true; + } + } + } + } else { + EntityItem entityitem = getEntityItemAt(ihopper.getWorld(), ihopper.x(), ihopper.aD() + 1.0D, ihopper.aE()); + + if (entityitem != null) { + return addEntityItem(ihopper, entityitem); + } + } + + return false; + } + + private static boolean tryTakeInItemFromSlot(IHopper ihopper, IInventory iinventory, int i, int j) { + ItemStack itemstack = iinventory.getItem(i); + + if (itemstack != null && canTakeItemFromInventory(iinventory, itemstack, i, j)) { + ItemStack itemstack1 = itemstack.cloneItemStack(); + + // Poweruser start + ItemStack copyOfItemBeingSuck = iinventory.getItem(i).cloneItemStack(); + copyOfItemBeingSuck.count = 1; + int facing = -1; + int possibleInventorySlot = doesInventoryHaveEnoughSpaceForItem(ihopper, copyOfItemBeingSuck, facing); + if(possibleInventorySlot < 0) { + return false; + } + // Poweruser end + + // CraftBukkit start - Call event on collection of items from inventories into the hopper + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.splitStack(i, ihopper.getWorld().spigotConfig.hopperAmount)); // Spigot + + Inventory sourceInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + sourceInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false); + + ihopper.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + iinventory.setItem(i, itemstack1); + + if (ihopper instanceof TileEntityHopper) { + ((TileEntityHopper) ihopper).c(ihopper.getWorld().spigotConfig.hopperTransfer); // Spigot + } else if (ihopper instanceof EntityMinecartHopper) { + ((EntityMinecartHopper) ihopper).l(ihopper.getWorld().spigotConfig.hopperTransfer / 2); // Spigot + } + + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack2 = addItem(ihopper, possibleInventorySlot, CraftItemStack.asNMSCopy(event.getItem()), facing); // Poweruser + + if (itemstack2 == null || itemstack2.count == 0) { + if (event.getItem().equals(oitemstack)) { + iinventory.update(); + } else { + iinventory.setItem(i, itemstack1); + } + // CraftBukkit end + + return true; + } + itemstack1.count -= origCount - itemstack2.count; // Spigot + + iinventory.setItem(i, itemstack1); + } + + return false; + } + + public static boolean addEntityItem(IInventory iinventory, EntityItem entityitem) { + boolean flag = false; + + if (entityitem == null) { + return false; + } else { + // Poweruser start + ItemStack copyOfItemBeingAdded = entityitem.getItemStack().cloneItemStack(); + int facing = -1; + int possibleInventorySlot = doesInventoryHaveEnoughSpaceForItem(iinventory, copyOfItemBeingAdded, facing); + if(possibleInventorySlot < 0) { + return false; + } + // Poweruser end + + // CraftBukkit start + InventoryPickupItemEvent event = new InventoryPickupItemEvent(iinventory.getOwner().getInventory(), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); + entityitem.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + + ItemStack itemstack = entityitem.getItemStack().cloneItemStack(); + ItemStack itemstack1 = addItem(iinventory, possibleInventorySlot, itemstack, facing); // Poweruser + + if (itemstack1 != null && itemstack1.count != 0) { + entityitem.setItemStack(itemstack1); + } else { + flag = true; + entityitem.die(); + } + + return flag; + } + } + + public static ItemStack addItem(IInventory iinventory, ItemStack itemstack, int i) { + // Poweruser start + return addItem(iinventory, -1, itemstack, i); + } + + public static ItemStack addItem(IInventory iinventory, int possibleInventorySlot, ItemStack itemstack, int i) { + // Poweruser end + if (iinventory instanceof IWorldInventory && i > -1) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(i); + + // Poweruser start + if(possibleInventorySlot >= 0 && possibleInventorySlot < aint.length) { + itemstack = tryMoveInItem(iinventory, itemstack, possibleInventorySlot, i); + } + // Poweruser end + + for (int j = 0; j < aint.length && itemstack != null && itemstack.count > 0; ++j) { + itemstack = tryMoveInItem(iinventory, itemstack, aint[j], i); + } + } else { + int k = iinventory.getSize(); + + // Poweruser start + if(possibleInventorySlot >= 0 && possibleInventorySlot < k) { + itemstack = tryMoveInItem(iinventory, itemstack, possibleInventorySlot, i); + } + // Poweruser end + + for (int l = 0; l < k && itemstack != null && itemstack.count > 0; ++l) { + itemstack = tryMoveInItem(iinventory, itemstack, l, i); + } + } + + if (itemstack != null && itemstack.count == 0) { + itemstack = null; + } + + return itemstack; + } + + private static boolean canPlaceItemInInventory(IInventory iinventory, ItemStack itemstack, int i, int j) { + return !iinventory.b(i, itemstack) ? false : !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canPlaceItemThroughFace(i, itemstack, j); + } + + private static boolean canTakeItemFromInventory(IInventory iinventory, ItemStack itemstack, int i, int j) { + return !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canTakeItemThroughFace(i, itemstack, j); + } + + private static ItemStack tryMoveInItem(IInventory iinventory, ItemStack itemstack, int i, int j) { + ItemStack itemstack1 = iinventory.getItem(i); + + if (canPlaceItemInInventory(iinventory, itemstack, i, j)) { + boolean flag = false; + + if (itemstack1 == null) { + iinventory.setItem(i, itemstack); + itemstack = null; + flag = true; + } else if (canMergeItems(itemstack1, itemstack)) { + int k = itemstack.getMaxStackSize() - itemstack1.count; + int l = Math.min(itemstack.count, k); + + itemstack.count -= l; + itemstack1.count += l; + flag = l > 0; + } + + if (flag) { + if (iinventory instanceof TileEntityHopper) { + ((TileEntityHopper) iinventory).c(((TileEntityHopper) iinventory).world.spigotConfig.hopperTransfer); // Spigot + iinventory.update(); + } + + iinventory.update(); + } + } + + return itemstack; + } + + private IInventory z() { + int i = BlockHopper.b(this.p()); + + return getInventoryAt(this.getWorld(), (double) (this.x + Facing.b[i]), (double) (this.y + Facing.c[i]), (double) (this.z + Facing.d[i])); + } + + public static IInventory getSourceInventory(IHopper ihopper) { + return getInventoryAt(ihopper.getWorld(), ihopper.x(), ihopper.aD() + 1.0D, ihopper.aE()); + } + + public static EntityItem getEntityItemAt(World world, double d0, double d1, double d2) { + if(!isPositionOfHopperInUse(world, d0, d1, d2)) { return null; } // Poweruser + List list = world.a(EntityItem.class, AxisAlignedBB.a(d0, d1, d2, d0 + 1.0D, d1 + 1.0D, d2 + 1.0D), IEntitySelector.a); + + return list.size() > 0 ? (EntityItem) list.get(0) : null; + } + + public static IInventory getInventoryAt(World world, double d0, double d1, double d2) { + if(!isPositionOfHopperInUse(world, d0, d1, d2)) { return null; } // Poweruser + + IInventory iinventory = null; + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + //if ( !world.isLoaded( i, j, k ) ) return null; // Spigot // Poweruser - already covered at this point + TileEntity tileentity = world.getTileEntity(i, j, k); + + if (tileentity != null && tileentity instanceof IInventory) { + iinventory = (IInventory) tileentity; + if (iinventory instanceof TileEntityChest) { + Block block = world.getType(i, j, k); + + if (block instanceof BlockChest) { + iinventory = ((BlockChest) block).m(world, i, j, k); + } + } + } + + if (iinventory == null) { + List list = world.getEntities((Entity) null, AxisAlignedBB.a(d0, d1, d2, d0 + 1.0D, d1 + 1.0D, d2 + 1.0D), IEntitySelector.c); + + if (list != null && list.size() > 0) { + iinventory = (IInventory) list.get(world.random.nextInt(list.size())); + } + } + + return iinventory; + } + + private static boolean canMergeItems(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.getItem() != itemstack1.getItem() ? false : (itemstack.getData() != itemstack1.getData() ? false : (itemstack.count >= itemstack.getMaxStackSize() ? false : ItemStack.equals(itemstack, itemstack1))); // Poweruser - stacks can not merge when count is greater or equal the max stack size + } + + public double x() { + return (double) this.x; + } + + public double aD() { + return (double) this.y; + } + + public double aE() { + return (double) this.z; + } + + public void c(int i) { + // Spigot start - i is the delay for which this hopper will be ticked next. + // i of 1 or below implies a tick next tick. + if (this.world != null && this.world.spigotConfig.altHopperTicking) { + if (i <= 0) { + i = 1; + } + if (this.lastTick == this.world.getTime()) { + this.nextTick = this.world.getTime() + i; + } else { + this.nextTick = this.world.getTime() + i - 1; + } + } else { + this.j = i; + } + // Spigot end + } + + public boolean j() { + // Spigot start - Return whether this hopper is cooling down. + if (this.world != null && this.world.spigotConfig.altHopperTicking) { + if (this.lastTick == this.world.getTime()) { + return this.nextTick > this.world.getTime(); + } else { + return this.nextTick >= this.world.getTime(); + } + } else { + return this.j > 0; + } + // Spigot end + } + + // Poweruser start + private static int doesInventoryHaveEnoughSpaceForItem(IInventory iinventory, ItemStack itemstack, int facing) { + if (iinventory instanceof IWorldInventory && facing > -1) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] possibleSlots = iworldinventory.getSlotsForFace(facing); + for(int i = 0; i < possibleSlots.length; i++) { + int slotId = possibleSlots[i]; + if(canPlaceItemInInventory(iinventory, itemstack, slotId, facing)) { + ItemStack slot = iinventory.getItem(slotId); + if(slot == null || canMergeItems(slot, itemstack)) { + return slotId; + } + } + } + } else { + int size = iinventory.getSize(); + for(int i = 0; i < size; i++) { + if(canPlaceItemInInventory(iinventory, itemstack, i, facing)) { + ItemStack slot = iinventory.getItem(i); + if(slot == null || canMergeItems(slot, itemstack)) { + return i; + } + } + } + } + return -1; + } + + private static boolean isPositionOfHopperInUse(World world, double d0, double d1, double d2) { + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + return (world.isLoaded(i, j, k) && !unloadQueueContains(world, i, j, k)); + } + + private static boolean unloadQueueContains(World world, int x, int y, int z) { + return world != null && + world.chunkProviderServer != null && + world.chunkProviderServer.unloadQueue != null && + world.chunkProviderServer.unloadQueue.contains(x >> 4, z >> 4); + } + // Poweruser end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityLightDetector.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityLightDetector.java new file mode 100644 index 0000000..de33df0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityLightDetector.java @@ -0,0 +1,15 @@ +package net.minecraft.server; + +public class TileEntityLightDetector extends TileEntity { + + public TileEntityLightDetector() {} + + public void h() { + if (this.world != null && !this.world.isStatic /*&& this.world.getTime() % 20L == 0L*/) { // PaperSpigot - interval controlled by Improved Tick Handling + this.h = this.q(); + if (this.h instanceof BlockDaylightDetector) { + ((BlockDaylightDetector) this.h).e(this.world, this.x, this.y, this.z); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityNote.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityNote.java new file mode 100644 index 0000000..aa02038 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityNote.java @@ -0,0 +1,61 @@ +package net.minecraft.server; + +public class TileEntityNote extends TileEntity { + + public byte note; + public boolean i; + + public TileEntityNote() {} + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setByte("note", this.note); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.note = nbttagcompound.getByte("note"); + if (this.note < 0) { + this.note = 0; + } + + if (this.note > 24) { + this.note = 24; + } + } + + public void a() { + this.note = (byte) ((this.note + 1) % 25); + this.update(); + } + + public void play(World world, int i, int j, int k) { + if (world.getType(i, j + 1, k).getMaterial() == Material.AIR) { + Material material = world.getType(i, j - 1, k).getMaterial(); + byte b0 = 0; + + if (material == Material.STONE) { + b0 = 1; + } + + if (material == Material.SAND) { + b0 = 2; + } + + if (material == Material.SHATTERABLE) { + b0 = 3; + } + + if (material == Material.WOOD) { + b0 = 4; + } + + // CraftBukkit start + org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(this.world, i, j, k, b0, this.note); + if (!event.isCancelled()) { + this.world.playBlockAction(i, j, k, Blocks.NOTE_BLOCK, event.getInstrument().getType(), event.getNote().getId()); + } + // CraftBukkit end + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityPiston.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityPiston.java new file mode 100644 index 0000000..b348055 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityPiston.java @@ -0,0 +1,132 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class TileEntityPiston extends TileEntity { + + private Block a; + private int i; + private int j; + private boolean k; + private boolean l; + private float m; + private float n; + private List o = new ArrayList(); + + public TileEntityPiston() {} + + public TileEntityPiston(Block block, int i, int j, boolean flag, boolean flag1) { + this.a = block; + this.i = i; + this.j = j; + this.k = flag; + this.l = flag1; + } + + public Block a() { + return this.a; + } + + public int p() { + return this.i; + } + + public boolean b() { + return this.k; + } + + public int c() { + return this.j; + } + + public float a(float f) { + if (f > 1.0F) { + f = 1.0F; + } + + return this.n + (this.m - this.n) * f; + } + + private void a(float f, float f1) { + if (this.k) { + f = 1.0F - f; + } else { + --f; + } + + AxisAlignedBB axisalignedbb = Blocks.PISTON_MOVING.a(this.world, this.x, this.y, this.z, this.a, f, this.j); + + if (axisalignedbb != null) { + List list = this.world.getEntities((Entity) null, axisalignedbb); + + if (!list.isEmpty()) { + this.o.addAll(list); + Iterator iterator = this.o.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + entity.inPistonTick = MinecraftServer.currentTick; // Guardian + entity.move((double) (f1 * (float) Facing.b[this.j]), (double) (f1 * (float) Facing.c[this.j]), (double) (f1 * (float) Facing.d[this.j])); + } + + this.o.clear(); + } + } + } + + public void f() { + if (this.n < 1.0F && this.world != null) { + this.n = this.m = 1.0F; + this.world.p(this.x, this.y, this.z); + this.s(); + if (this.world.getType(this.x, this.y, this.z) == Blocks.PISTON_MOVING) { + this.world.setTypeAndData(this.x, this.y, this.z, this.a, this.i, 3); + this.world.e(this.x, this.y, this.z, this.a); + } + } + } + + public void h() { + if (this.world == null) return; // CraftBukkit + + this.n = this.m; + if (this.n >= 1.0F) { + this.a(1.0F, 0.25F); + this.world.p(this.x, this.y, this.z); + this.s(); + if (this.world.getType(this.x, this.y, this.z) == Blocks.PISTON_MOVING) { + this.world.setTypeAndData(this.x, this.y, this.z, this.a, this.i, 3); + this.world.e(this.x, this.y, this.z, this.a); + } + } else { + this.m += 0.5F; + if (this.m >= 1.0F) { + this.m = 1.0F; + } + + if (this.k) { + this.a(this.m, this.m - this.n + 0.0625F); + } + } + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.a = Block.getById(nbttagcompound.getInt("blockId")); + this.i = nbttagcompound.getInt("blockData"); + this.j = nbttagcompound.getInt("facing"); + this.n = this.m = nbttagcompound.getFloat("progress"); + this.k = nbttagcompound.getBoolean("extending"); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("blockId", Block.getId(this.a)); + nbttagcompound.setInt("blockData", this.i); + nbttagcompound.setInt("facing", this.j); + nbttagcompound.setFloat("progress", this.n); + nbttagcompound.setBoolean("extending", this.k); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntityRecordPlayer.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntityRecordPlayer.java new file mode 100644 index 0000000..8abeac4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntityRecordPlayer.java @@ -0,0 +1,40 @@ +package net.minecraft.server; + +public class TileEntityRecordPlayer extends TileEntity { + + private ItemStack record; + + public TileEntityRecordPlayer() {} + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("RecordItem", 10)) { + this.setRecord(ItemStack.createStack(nbttagcompound.getCompound("RecordItem"))); + } else if (nbttagcompound.getInt("Record") > 0) { + this.setRecord(new ItemStack(Item.getById(nbttagcompound.getInt("Record")), 1, 0)); + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.getRecord() != null) { + nbttagcompound.set("RecordItem", this.getRecord().save(new NBTTagCompound())); + nbttagcompound.setInt("Record", Item.getId(this.getRecord().getItem())); + } + } + + public ItemStack getRecord() { + return this.record; + } + + public void setRecord(ItemStack itemstack) { + // CraftBukkit start - There can only be one + if (itemstack != null) { + itemstack.count = 1; + } + // CraftBukkit end + + this.record = itemstack; + this.update(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntitySign.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntitySign.java new file mode 100644 index 0000000..552919c --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntitySign.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +public class TileEntitySign extends TileEntity { + + public String[] lines = new String[] { "", "", "", ""}; + public int i = -1; + public boolean isEditable = true; // CraftBukkit - private -> public + private EntityHuman k; + + public TileEntitySign() {} + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setString("Text1", this.lines[0]); + nbttagcompound.setString("Text2", this.lines[1]); + nbttagcompound.setString("Text3", this.lines[2]); + nbttagcompound.setString("Text4", this.lines[3]); + } + + public void a(NBTTagCompound nbttagcompound) { + this.isEditable = false; + super.a(nbttagcompound); + + for (int i = 0; i < 4; ++i) { + this.lines[i] = nbttagcompound.getString("Text" + (i + 1)); + if (this.lines[i].length() > 15) { + this.lines[i] = this.lines[i].substring(0, 15); + } + } + } + + public Packet getUpdatePacket() { + String[] astring = sanitizeLines(this.lines); // CraftBukkit - call sign line sanitizer to limit line length + + return new PacketPlayOutUpdateSign(this.x, this.y, this.z, astring); + } + + public boolean a() { + return this.isEditable; + } + + public void a(EntityHuman entityhuman) { + this.k = entityhuman; + } + + public EntityHuman b() { + return this.k; + } + + // CraftBukkit start - central method to limit sign text to 15 chars per line + public static String[] sanitizeLines(String[] lines) { + String[] astring = new String[4]; + for (int i = 0; i < 4; ++i) { + astring[i] = lines[i]; + + if (lines[i].length() > 15) { + astring[i] = lines[i].substring(0, 15); + } + } + return astring; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/TileEntitySkull.java b/vspigot-server/src/main/java/net/minecraft/server/TileEntitySkull.java new file mode 100644 index 0000000..19bb413 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/TileEntitySkull.java @@ -0,0 +1,166 @@ +package net.minecraft.server; + +import java.util.UUID; + +import net.minecraft.util.com.google.common.collect.Iterables; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.properties.Property; + +// Spigot start +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; + +import java.util.concurrent.TimeUnit; + +import net.minecraft.util.com.mojang.authlib.Agent; +// Spigot end + +import net.valorhcf.ThreadingManager; // Poweruser + +public class TileEntitySkull extends TileEntity { + + private int a; + private int i; + private GameProfile j = null; + // Spigot start + public static final Cache skinCache = CacheBuilder.newBuilder() + .maximumSize( 5000 ) + .expireAfterAccess( 60, TimeUnit.MINUTES ) + .build( new CacheLoader() + { + @Override + public GameProfile load(String key) throws Exception + { + GameProfile[] profiles = new GameProfile[1]; + GameProfileLookup gameProfileLookup = new GameProfileLookup(profiles); + + MinecraftServer.getServer().getGameProfileRepository().findProfilesByNames(new String[] { key }, Agent.MINECRAFT, gameProfileLookup); + + GameProfile profile = profiles[ 0 ]; + if (profile == null) { + UUID uuid = EntityHuman.a(new GameProfile(null, key)); + profile = new GameProfile(uuid, key); + + gameProfileLookup.onProfileLookupSucceeded(profile); + } else + { + + Property property = Iterables.getFirst( profile.getProperties().get( "textures" ), null ); + + if ( property == null ) + { + profile = MinecraftServer.getServer().av().fillProfileProperties( profile, true ); + } + } + + + return profile; + } + } ); + // Spigot end + + public TileEntitySkull() {} + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setByte("SkullType", (byte) (this.a & 255)); + nbttagcompound.setByte("Rot", (byte) (this.i & 255)); + if (this.j != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound1, this.j); + nbttagcompound.set("Owner", nbttagcompound1); + nbttagcompound.setString("ExtraType", nbttagcompound1.getString("Name")); // Spigot + } + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.a = nbttagcompound.getByte("SkullType"); + this.i = nbttagcompound.getByte("Rot"); + if (this.a == 3) { + if (nbttagcompound.hasKeyOfType("Owner", 10)) { + this.j = GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner")); + } else if (nbttagcompound.hasKeyOfType("ExtraType", 8) && !UtilColor.b(nbttagcompound.getString("ExtraType"))) { + this.j = new GameProfile((UUID) null, nbttagcompound.getString("ExtraType")); + this.d(); + } + } + } + + public GameProfile getGameProfile() { + return this.j; + } + + public Packet getUpdatePacket() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + this.b(nbttagcompound); + return new PacketPlayOutTileEntityData(this.x, this.y, this.z, 4, nbttagcompound); + } + + public void setSkullType(int i) { + this.a = i; + this.j = null; + } + + public void setGameProfile(GameProfile gameprofile) { + this.a = 3; + this.j = gameprofile; + this.d(); + } + + private void d() { + if (this.j != null && !UtilColor.b(this.j.getName())) { + if (!this.j.isComplete() || !this.j.getProperties().containsKey("textures")) { + // Spigot start - Handle async + final String name = this.j.getName(); + setSkullType( 0 ); // Work around a client bug + ThreadingManager.queueHeadConversion(new Runnable() { // Poweruser + @Override + public void run() { + + GameProfile profile = skinCache.getUnchecked( name.toLowerCase() ); + + if (profile != null) { + final GameProfile finalProfile = profile; + MinecraftServer.getServer().processQueue.add(new Runnable() { + @Override + public void run() { + a = 3; + j = finalProfile; + world.notify( x, y, z ); + } + }); + } else { + MinecraftServer.getServer().processQueue.add(new Runnable() { + @Override + public void run() { + a = 3; + j = new GameProfile( null, name ); + world.notify( x, y, z ); + } + }); + } + } + }); + // Spigot end + } + } + } + + public int getSkullType() { + return this.a; + } + + public void setRotation(int i) { + this.i = i; + } + + // CraftBukkit start - add method + public int getRotation() { + return this.i; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/UserCache.java b/vspigot-server/src/main/java/net/minecraft/server/UserCache.java new file mode 100644 index 0000000..f3b3548 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/UserCache.java @@ -0,0 +1,255 @@ +package net.minecraft.server; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + +import org.spigotmc.SpigotConfig; + +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.google.common.collect.Iterators; +import net.minecraft.util.com.google.common.collect.Lists; +import net.minecraft.util.com.google.common.collect.Maps; +import net.minecraft.util.com.google.common.io.Files; +import net.minecraft.util.com.google.gson.Gson; +import net.minecraft.util.com.google.gson.GsonBuilder; +import net.minecraft.util.com.mojang.authlib.Agent; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.org.apache.commons.io.IOUtils; + +public class UserCache { + + public static final SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + private final Map c = Maps.newHashMap(); + private final Map d = Maps.newHashMap(); + private final LinkedList e = Lists.newLinkedList(); + private final MinecraftServer f; + protected final Gson b; + private final File g; + private static final ParameterizedType h = new UserCacheEntryType(); + + public UserCache(MinecraftServer minecraftserver, File file1) { + this.f = minecraftserver; + this.g = file1; + GsonBuilder gsonbuilder = new GsonBuilder(); + + gsonbuilder.registerTypeHierarchyAdapter(UserCacheEntry.class, new BanEntrySerializer(this, (GameProfileLookup) null)); + this.b = gsonbuilder.create(); + this.b(); + } + + private static GameProfile a(MinecraftServer minecraftserver, String s) { + GameProfile[] agameprofile = new GameProfile[1]; + GameProfileLookup gameprofilelookup = new GameProfileLookup(agameprofile); + + minecraftserver.getGameProfileRepository().findProfilesByNames(new String[] { s}, Agent.MINECRAFT, gameprofilelookup); + if (!minecraftserver.getOnlineMode() && agameprofile[0] == null) { + UUID uuid = EntityHuman.a(new GameProfile((UUID) null, s)); + GameProfile gameprofile = new GameProfile(uuid, s); + + gameprofilelookup.onProfileLookupSucceeded(gameprofile); + } + + return agameprofile[0]; + } + + public void a(GameProfile gameprofile) { + this.a(gameprofile, (Date) null); + } + + private void a(GameProfile gameprofile, Date date) { + UUID uuid = gameprofile.getId(); + + if (date == null) { + Calendar calendar = Calendar.getInstance(); + + calendar.setTime(new Date()); + calendar.add(2, 1); + date = calendar.getTime(); + } + + String s = gameprofile.getName().toLowerCase(Locale.ROOT); + UserCacheEntry usercacheentry = new UserCacheEntry(this, gameprofile, date, (GameProfileLookup) null); + LinkedList linkedlist = this.e; + + synchronized (this.e) { + if (this.d.containsKey(uuid)) { + UserCacheEntry usercacheentry1 = (UserCacheEntry) this.d.get(uuid); + + this.c.remove(usercacheentry1.a().getName().toLowerCase(Locale.ROOT)); + this.c.put(gameprofile.getName().toLowerCase(Locale.ROOT), usercacheentry); + this.e.remove(gameprofile); + } else { + this.d.put(uuid, usercacheentry); + this.c.put(s, usercacheentry); + } + + this.e.addFirst(gameprofile); + } + } + + public GameProfile getProfile(String s) { + String s1 = s.toLowerCase(Locale.ROOT); + UserCacheEntry usercacheentry = (UserCacheEntry) this.c.get(s1); + + if (usercacheentry != null && (new Date()).getTime() >= UserCacheEntry.a(usercacheentry).getTime()) { + this.d.remove(usercacheentry.a().getId()); + this.c.remove(usercacheentry.a().getName().toLowerCase(Locale.ROOT)); + LinkedList linkedlist = this.e; + + synchronized (this.e) { + this.e.remove(usercacheentry.a()); + } + + usercacheentry = null; + } + + GameProfile gameprofile; + + if (usercacheentry != null) { + gameprofile = usercacheentry.a(); + LinkedList linkedlist1 = this.e; + + synchronized (this.e) { + this.e.remove(gameprofile); + this.e.addFirst(gameprofile); + } + } else { + gameprofile = a(this.f, s); // Spigot - use correct case for offline players + if (gameprofile != null) { + this.a(gameprofile); + usercacheentry = (UserCacheEntry) this.c.get(s1); + } + } + + if( !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly ) this.c(); // Spigot - skip saving if disabled + return usercacheentry == null ? null : usercacheentry.a(); + } + + public String[] a() { + ArrayList arraylist = Lists.newArrayList(this.c.keySet()); + + return (String[]) arraylist.toArray(new String[arraylist.size()]); + } + + public GameProfile a(UUID uuid) { + UserCacheEntry usercacheentry = (UserCacheEntry) this.d.get(uuid); + + return usercacheentry == null ? null : usercacheentry.a(); + } + + private UserCacheEntry b(UUID uuid) { + UserCacheEntry usercacheentry = (UserCacheEntry) this.d.get(uuid); + + if (usercacheentry != null) { + GameProfile gameprofile = usercacheentry.a(); + LinkedList linkedlist = this.e; + + synchronized (this.e) { + this.e.remove(gameprofile); + this.e.addFirst(gameprofile); + } + } + + return usercacheentry; + } + + public void b() { + List list = null; + BufferedReader bufferedreader = null; + + label81: { + try { + bufferedreader = Files.newReader(this.g, Charsets.UTF_8); + list = (List) this.b.fromJson(bufferedreader, h); + break label81; + } catch (FileNotFoundException filenotfoundexception) { + ; + // Spigot Start + } catch (net.minecraft.util.com.google.gson.JsonSyntaxException ex) { + JsonList.a.warn( "Usercache.json is corrupted or has bad formatting. Deleting it to prevent further issues." ); + this.g.delete(); + // Spigot End + } finally { + IOUtils.closeQuietly(bufferedreader); + } + + return; + } + + if (list != null) { + this.c.clear(); + this.d.clear(); + LinkedList linkedlist = this.e; + + synchronized (this.e) { + this.e.clear(); + } + + list = Lists.reverse(list); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + UserCacheEntry usercacheentry = (UserCacheEntry) iterator.next(); + + if (usercacheentry != null) { + this.a(usercacheentry.a(), usercacheentry.b()); + } + } + } + } + + public void c() { + if (SpigotConfig.disableSaving) return; // MineHQ + String s = this.b.toJson(this.a(org.spigotmc.SpigotConfig.userCacheCap)); + BufferedWriter bufferedwriter = null; + + try { + bufferedwriter = Files.newWriter(this.g, Charsets.UTF_8); + bufferedwriter.write(s); + return; + } catch (FileNotFoundException filenotfoundexception) { + return; + } catch (IOException ioexception) { + ; + } finally { + IOUtils.closeQuietly(bufferedwriter); + } + } + + private List a(int i) { + ArrayList arraylist = Lists.newArrayList(); + LinkedList linkedlist = this.e; + ArrayList arraylist1; + + synchronized (this.e) { + arraylist1 = Lists.newArrayList(Iterators.limit(this.e.iterator(), i)); + } + + Iterator iterator = arraylist1.iterator(); + + while (iterator.hasNext()) { + GameProfile gameprofile = (GameProfile) iterator.next(); + UserCacheEntry usercacheentry = this.b(gameprofile.getId()); + + if (usercacheentry != null) { + arraylist.add(usercacheentry); + } + } + + return arraylist; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/UserCacheEntry.java b/vspigot-server/src/main/java/net/minecraft/server/UserCacheEntry.java new file mode 100644 index 0000000..fe129a5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/UserCacheEntry.java @@ -0,0 +1,34 @@ +package net.minecraft.server; + +import java.util.Date; + +import net.minecraft.util.com.mojang.authlib.GameProfile; + +class UserCacheEntry { + + private final GameProfile b; + private final Date c; + final UserCache a; + + private UserCacheEntry(UserCache usercache, GameProfile gameprofile, Date date) { + this.a = usercache; + this.b = gameprofile; + this.c = date; + } + + public GameProfile a() { + return this.b; + } + + public Date b() { + return this.c; + } + + UserCacheEntry(UserCache usercache, GameProfile gameprofile, Date date, GameProfileLookup gameprofilelookup) { + this(usercache, gameprofile, date); + } + + static Date a(UserCacheEntry usercacheentry) { + return usercacheentry.c; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/Village.java b/vspigot-server/src/main/java/net/minecraft/server/Village.java new file mode 100644 index 0000000..47b87b4 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/Village.java @@ -0,0 +1,471 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.TreeMap; + +public class Village { + + private World world; + private final List doors = new ArrayList(); + private final ChunkCoordinates c = new ChunkCoordinates(0, 0, 0); + private final ChunkCoordinates center = new ChunkCoordinates(0, 0, 0); + private int size; + private int f; + private int time; + private int population; + private int noBreedTicks; + private TreeMap playerStandings = new TreeMap(); + private List aggressors = new ArrayList(); + private int ironGolemCount; + + // Poweruser start + private int[][] positions = null; + + private void calculateNewCheckPositions() { + this.positions = new int[][] { {(this.center.x - this.size) >> 4, (this.center.z - this.size) >> 4}, + {(this.center.x - this.size) >> 4, (this.center.z + this.size) >> 4}, + {(this.center.x + this.size) >> 4, (this.center.z - this.size) >> 4}, + {(this.center.x + this.size) >> 4, (this.center.z + this.size) >> 4}, + {this.center.x >> 4, this.center.z >> 4} }; + } + + public boolean isVillageAreaLoaded() { + for(int i = 0; this.positions != null && i < this.positions.length; i++) { + int[] pos = this.positions[i]; + if(this.world.isChunkLoaded(pos[0], pos[1])) { + return true; + } + } + return false; + } + // Poweruser end + + public Village() {} + + public Village(World world) { + this.world = world; + } + + public void a(World world) { + this.world = world; + } + + public void tick(int i) { + if(!this.isVillageAreaLoaded()) { return; } // Poweruser + this.time = i; + this.m(); + this.l(); + if (i % 20 == 0) { + this.k(); + } + + if (i % 30 == 0) { + this.countPopulation(); + } + + int j = this.population / 10; + + if (this.ironGolemCount < j && this.doors.size() > 20 && this.world.random.nextInt(7000) == 0) { + Vec3D vec3d = this.a(MathHelper.d((float) this.center.x), MathHelper.d((float) this.center.y), MathHelper.d((float) this.center.z), 2, 4, 2); + + if (vec3d != null) { + EntityIronGolem entityirongolem = new EntityIronGolem(this.world); + + entityirongolem.setPosition(vec3d.a, vec3d.b, vec3d.c); + this.world.addEntity(entityirongolem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE); // CraftBukkit + ++this.ironGolemCount; + } + } + } + + private Vec3D a(int i, int j, int k, int l, int i1, int j1) { + for (int k1 = 0; k1 < 10; ++k1) { + int l1 = i + this.world.random.nextInt(16) - 8; + int i2 = j + this.world.random.nextInt(6) - 3; + int j2 = k + this.world.random.nextInt(16) - 8; + + if (this.a(l1, i2, j2) && this.b(l1, i2, j2, l, i1, j1)) { + return Vec3D.a((double) l1, (double) i2, (double) j2); + } + } + + return null; + } + + private boolean b(int i, int j, int k, int l, int i1, int j1) { + if (!World.a((IBlockAccess) this.world, i, j - 1, k)) { + return false; + } else { + int k1 = i - l / 2; + int l1 = k - j1 / 2; + + for (int i2 = k1; i2 < k1 + l; ++i2) { + for (int j2 = j; j2 < j + i1; ++j2) { + for (int k2 = l1; k2 < l1 + j1; ++k2) { + if (this.world.getType(i2, j2, k2).r()) { + return false; + } + } + } + } + + return true; + } + } + + private void countPopulation() { + List list = this.world.a(EntityIronGolem.class, AxisAlignedBB.a((double) (this.center.x - this.size), (double) (this.center.y - 4), (double) (this.center.z - this.size), (double) (this.center.x + this.size), (double) (this.center.y + 4), (double) (this.center.z + this.size))); + + this.ironGolemCount = list.size(); + } + + private void k() { + List list = this.world.a(EntityVillager.class, AxisAlignedBB.a((double) (this.center.x - this.size), (double) (this.center.y - 4), (double) (this.center.z - this.size), (double) (this.center.x + this.size), (double) (this.center.y + 4), (double) (this.center.z + this.size))); + + this.population = list.size(); + if (this.population == 0) { + this.playerStandings.clear(); + } + } + + public ChunkCoordinates getCenter() { + return this.center; + } + + public int getSize() { + return this.size; + } + + public int getDoorCount() { + return this.doors.size(); + } + + public int d() { + return this.time - this.f; + } + + public int getPopulationCount() { + return this.population; + } + + public boolean a(int i, int j, int k) { + return this.center.e(i, j, k) < (float) (this.size * this.size); + } + + public List getDoors() { + return this.doors; + } + + public VillageDoor b(int i, int j, int k) { + VillageDoor villagedoor = null; + int l = Integer.MAX_VALUE; + Iterator iterator = this.doors.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor1 = (VillageDoor) iterator.next(); + int i1 = villagedoor1.b(i, j, k); + + if (i1 < l) { + villagedoor = villagedoor1; + l = i1; + } + } + + return villagedoor; + } + + public VillageDoor c(int i, int j, int k) { + VillageDoor villagedoor = null; + int l = Integer.MAX_VALUE; + Iterator iterator = this.doors.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor1 = (VillageDoor) iterator.next(); + int i1 = villagedoor1.b(i, j, k); + + if (i1 > 256) { + i1 *= 1000; + } else { + i1 = villagedoor1.f(); + } + + if (i1 < l) { + villagedoor = villagedoor1; + l = i1; + } + } + + return villagedoor; + } + + public VillageDoor e(int i, int j, int k) { + if (this.center.e(i, j, k) > (float) (this.size * this.size)) { + return null; + } else { + Iterator iterator = this.doors.iterator(); + + VillageDoor villagedoor; + + do { + if (!iterator.hasNext()) { + return null; + } + + villagedoor = (VillageDoor) iterator.next(); + } while (villagedoor.locX != i || villagedoor.locZ != k || Math.abs(villagedoor.locY - j) > 1); + + return villagedoor; + } + } + + public void addDoor(VillageDoor villagedoor) { + this.doors.add(villagedoor); + this.c.x += villagedoor.locX; + this.c.y += villagedoor.locY; + this.c.z += villagedoor.locZ; + this.n(); + this.f = villagedoor.addedTime; + } + + public boolean isAbandoned() { + return this.doors.isEmpty(); + } + + public void a(EntityLiving entityliving) { + Iterator iterator = this.aggressors.iterator(); + + VillageAggressor villageaggressor; + + do { + if (!iterator.hasNext()) { + this.aggressors.add(new VillageAggressor(this, entityliving, this.time)); + return; + } + + villageaggressor = (VillageAggressor) iterator.next(); + } while (villageaggressor.a != entityliving); + + villageaggressor.b = this.time; + } + + public EntityLiving b(EntityLiving entityliving) { + double d0 = Double.MAX_VALUE; + VillageAggressor villageaggressor = null; + + for (int i = 0; i < this.aggressors.size(); ++i) { + VillageAggressor villageaggressor1 = (VillageAggressor) this.aggressors.get(i); + double d1 = villageaggressor1.a.f(entityliving); + + if (d1 <= d0) { + villageaggressor = villageaggressor1; + d0 = d1; + } + } + + return villageaggressor != null ? villageaggressor.a : null; + } + + public EntityHuman c(EntityLiving entityliving) { + double d0 = Double.MAX_VALUE; + EntityHuman entityhuman = null; + Iterator iterator = this.playerStandings.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + if (this.d(s)) { + EntityHuman entityhuman1 = this.world.a(s); + + if (entityhuman1 != null) { + double d1 = entityhuman1.f(entityliving); + + if (d1 <= d0) { + entityhuman = entityhuman1; + d0 = d1; + } + } + } + } + + return entityhuman; + } + + private void l() { + Iterator iterator = this.aggressors.iterator(); + + while (iterator.hasNext()) { + VillageAggressor villageaggressor = (VillageAggressor) iterator.next(); + + if (!villageaggressor.a.isAlive() || Math.abs(this.time - villageaggressor.b) > 300) { + iterator.remove(); + } + } + } + + private void m() { + boolean flag = false; + boolean flag1 = this.world.random.nextInt(50) == 0; + Iterator iterator = this.doors.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor = (VillageDoor) iterator.next(); + + if (flag1) { + villagedoor.d(); + } + + if (!this.isDoor(villagedoor.locX, villagedoor.locY, villagedoor.locZ) || Math.abs(this.time - villagedoor.addedTime) > 1200) { + this.c.x -= villagedoor.locX; + this.c.y -= villagedoor.locY; + this.c.z -= villagedoor.locZ; + flag = true; + villagedoor.removed = true; + iterator.remove(); + } + } + + if (flag) { + this.n(); + } + } + + private boolean isDoor(int i, int j, int k) { + return this.world.getType(i, j, k) == Blocks.WOODEN_DOOR; + } + + private void n() { + int i = this.doors.size(); + + if (i == 0) { + this.center.b(0, 0, 0); + this.size = 0; + } else { + this.center.b(this.c.x / i, this.c.y / i, this.c.z / i); + int j = 0; + + VillageDoor villagedoor; + + for (Iterator iterator = this.doors.iterator(); iterator.hasNext(); j = Math.max(villagedoor.b(this.center.x, this.center.y, this.center.z), j)) { + villagedoor = (VillageDoor) iterator.next(); + } + + this.size = Math.max(32, (int) Math.sqrt((double) j) + 1); + } + this.calculateNewCheckPositions(); // Poweruser + } + + public int a(String s) { + Integer integer = (Integer) this.playerStandings.get(s); + + return integer != null ? integer.intValue() : 0; + } + + public int a(String s, int i) { + int j = this.a(s); + int k = MathHelper.a(j + i, -30, 10); + + this.playerStandings.put(s, Integer.valueOf(k)); + return k; + } + + public boolean d(String s) { + return this.a(s) <= -15; + } + + public void a(NBTTagCompound nbttagcompound) { + this.population = nbttagcompound.getInt("PopSize"); + this.size = nbttagcompound.getInt("Radius"); + this.ironGolemCount = nbttagcompound.getInt("Golems"); + this.f = nbttagcompound.getInt("Stable"); + this.time = nbttagcompound.getInt("Tick"); + this.noBreedTicks = nbttagcompound.getInt("MTick"); + this.center.x = nbttagcompound.getInt("CX"); + this.center.y = nbttagcompound.getInt("CY"); + this.center.z = nbttagcompound.getInt("CZ"); + this.c.x = nbttagcompound.getInt("ACX"); + this.c.y = nbttagcompound.getInt("ACY"); + this.c.z = nbttagcompound.getInt("ACZ"); + NBTTagList nbttaglist = nbttagcompound.getList("Doors", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.get(i); + VillageDoor villagedoor = new VillageDoor(nbttagcompound1.getInt("X"), nbttagcompound1.getInt("Y"), nbttagcompound1.getInt("Z"), nbttagcompound1.getInt("IDX"), nbttagcompound1.getInt("IDZ"), nbttagcompound1.getInt("TS")); + + this.doors.add(villagedoor); + } + + NBTTagList nbttaglist1 = nbttagcompound.getList("Players", 10); + + for (int j = 0; j < nbttaglist1.size(); ++j) { + NBTTagCompound nbttagcompound2 = nbttaglist1.get(j); + + this.playerStandings.put(nbttagcompound2.getString("Name"), Integer.valueOf(nbttagcompound2.getInt("S"))); + } + this.calculateNewCheckPositions(); // Poweruser + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("PopSize", this.population); + nbttagcompound.setInt("Radius", this.size); + nbttagcompound.setInt("Golems", this.ironGolemCount); + nbttagcompound.setInt("Stable", this.f); + nbttagcompound.setInt("Tick", this.time); + nbttagcompound.setInt("MTick", this.noBreedTicks); + nbttagcompound.setInt("CX", this.center.x); + nbttagcompound.setInt("CY", this.center.y); + nbttagcompound.setInt("CZ", this.center.z); + nbttagcompound.setInt("ACX", this.c.x); + nbttagcompound.setInt("ACY", this.c.y); + nbttagcompound.setInt("ACZ", this.c.z); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.doors.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor = (VillageDoor) iterator.next(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setInt("X", villagedoor.locX); + nbttagcompound1.setInt("Y", villagedoor.locY); + nbttagcompound1.setInt("Z", villagedoor.locZ); + nbttagcompound1.setInt("IDX", villagedoor.d); + nbttagcompound1.setInt("IDZ", villagedoor.e); + nbttagcompound1.setInt("TS", villagedoor.addedTime); + nbttaglist.add(nbttagcompound1); + } + + nbttagcompound.set("Doors", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + Iterator iterator1 = this.playerStandings.keySet().iterator(); + + while (iterator1.hasNext()) { + String s = (String) iterator1.next(); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + + nbttagcompound2.setString("Name", s); + nbttagcompound2.setInt("S", ((Integer) this.playerStandings.get(s)).intValue()); + nbttaglist1.add(nbttagcompound2); + } + + nbttagcompound.set("Players", nbttaglist1); + } + + public void h() { + this.noBreedTicks = this.time; + } + + public boolean i() { + return this.noBreedTicks == 0 || this.time - this.noBreedTicks >= 3600; + } + + public void b(int i) { + Iterator iterator = this.playerStandings.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + this.a(s, i); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/VillageSiege.java b/vspigot-server/src/main/java/net/minecraft/server/VillageSiege.java new file mode 100644 index 0000000..549d390 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/VillageSiege.java @@ -0,0 +1,182 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +public class VillageSiege { + + private World world; + private boolean b; + private int c = -1; + private int d; + private int e; + private Village f; + private int g; + private int h; + private int i; + + public VillageSiege(World world) { + this.world = world; + } + + public void a() { + boolean flag = false; + + if (flag) { + if (this.c == 2) { + this.d = 100; + return; + } + } else { + if (this.world.w()) { + this.c = 0; + return; + } + + if (this.c == 2) { + return; + } + + if (this.c == 0) { + float f = this.world.c(0.0F); + + if ((double) f < 0.5D || (double) f > 0.501D) { + return; + } + + this.c = this.world.random.nextInt(10) == 0 ? 1 : 2; + this.b = false; + if (this.c == 2) { + return; + } + } + + // PaperSpigot start - Siege manager initial state is -1 + if (this.c == -1) { + return; + } + // PaperSpigot end + } + + if (!this.b) { + if (!this.b()) { + return; + } + + this.b = true; + } + + if (this.e > 0) { + --this.e; + } else { + this.e = 2; + if (this.d > 0) { + this.c(); + --this.d; + } else { + this.c = 2; + } + } + } + + private boolean b() { + List list = this.world.players; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + this.f = this.world.villages.getClosestVillage((int) entityhuman.locX, (int) entityhuman.locY, (int) entityhuman.locZ, 1); + if (this.f != null && this.f.getDoorCount() >= 10 && this.f.d() >= 20 && this.f.getPopulationCount() >= 20) { + ChunkCoordinates chunkcoordinates = this.f.getCenter(); + float f = (float) this.f.getSize(); + boolean flag = false; + int i = 0; + + while (true) { + if (i < 10) { + // PaperSpigot start - Zombies should spawn near the perimeter of the village not in the center of it + float angle = this.world.random.nextFloat() * (float) Math.PI * 2.0F; + this.g = chunkcoordinates.x + (int) ((double) (MathHelper.cos(angle) * f) * 0.9D); + this.h = chunkcoordinates.y; + this.i = chunkcoordinates.z + (int) ((double) (MathHelper.sin(angle) * f) * 0.9D); + // PaperSpigot end + flag = false; + Iterator iterator1 = this.world.villages.getVillages().iterator(); + + while (iterator1.hasNext()) { + Village village = (Village) iterator1.next(); + + if (village != this.f && village.a(this.g, this.h, this.i)) { + flag = true; + break; + } + } + + if (flag) { + ++i; + continue; + } + } + + if (flag) { + return false; + } + + Vec3D vec3d = this.a(this.g, this.h, this.i); + + if (vec3d != null) { + this.e = 0; + this.d = 20; + return true; + } + break; + } + } + } + + return false; + } + + private boolean c() { + Vec3D vec3d = this.a(this.g, this.h, this.i); + + if (vec3d == null) { + return false; + } else { + EntityZombie entityzombie; + + try { + entityzombie = new EntityZombie(this.world); + entityzombie.prepare((GroupDataEntity) null); + entityzombie.setVillager(false); + } catch (Exception exception) { + exception.printStackTrace(); + return false; + } + + entityzombie.setPositionRotation(vec3d.a, vec3d.b, vec3d.c, this.world.random.nextFloat() * 360.0F, 0.0F); + this.world.addEntity(entityzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_INVASION); // CraftBukkit + ChunkCoordinates chunkcoordinates = this.f.getCenter(); + + entityzombie.a(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z, this.f.getSize()); + return true; + } + } + + private Vec3D a(int i, int j, int k) { + for (int l = 0; l < 10; ++l) { + int i1 = i + this.world.random.nextInt(16) - 8; + int j1 = j + this.world.random.nextInt(6) - 3; + int k1 = k + this.world.random.nextInt(16) - 8; + + if (this.f.a(i1, j1, k1) && SpawnerCreature.a(EnumCreatureType.MONSTER, this.world, i1, j1, k1)) { + // CraftBukkit - add Return + return Vec3D.a((double) i1, (double) j1, (double) k1); + } + + } + + return null; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WatchableObject.java b/vspigot-server/src/main/java/net/minecraft/server/WatchableObject.java new file mode 100644 index 0000000..2811f21 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WatchableObject.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +public class WatchableObject { + + private final int a; + private final int b; + private Object c; + private boolean d; + + public WatchableObject(int i, int j, Object object) { + this.b = j; + this.c = object; + this.a = i; + this.d = true; + } + + public int a() { + return this.b; + } + + public void a(Object object) { + this.c = object; + } + + public Object b() { + return this.c; + } + + public int c() { + return this.a; + } + + public boolean d() { + return this.d; + } + + public void a(boolean flag) { + this.d = flag; + } + + static boolean a(WatchableObject watchableobject, boolean flag) { + return watchableobject.d = flag; + } + + // MineHQ start + public WatchableObject clone() { + WatchableObject watchableObject = new WatchableObject(this.a, this.b, this.c); + watchableObject.a(this.d); + return watchableObject; + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/World.java b/vspigot-server/src/main/java/net/minecraft/server/World.java new file mode 100644 index 0000000..e971996 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/World.java @@ -0,0 +1,3511 @@ +package net.minecraft.server; + +import com.google.common.base.Function; +import net.valorhcf.LightingUpdater; +import net.valorhcf.PlayerMap; +import net.valorhcf.ThreadingManager; +import net.valorhcf.ThreadingManager.TaskQueueWorker; +import net.valorhcf.WeakChunkCache; +import net.valorhcf.autosave.AutoSaveWorldData; +import net.valorhcf.generator.GeneratorConfig; +import org.bukkit.Bukkit; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.SpigotTimings; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.event.block.BlockCanBuildEvent; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; +import org.bukkit.generator.ChunkGenerator; + +import java.util.*; +import java.util.concurrent.Callable; +// Poweruser end + +public abstract class World implements IBlockAccess { + + public boolean d; + // Spigot start - guard entity list from removals + public List entityList = new ArrayList() { + @Override + public Object remove(int index) { + guard(); + return super.remove(index); + } + + @Override + public boolean remove(Object o) { + guard(); + return super.remove(o); + } + + private void guard() { + if (guardEntityList) { + throw new java.util.ConcurrentModificationException(); + } + } + }; + // Spigot end + protected List f = new ArrayList(); + public Set tileEntityList = new org.spigotmc.WorldTileEntityList(this); // CraftBukkit - ArrayList -> HashSet + private List a = new ArrayList(); + private Set b = new HashSet<>(); + public List players = new ArrayList(); + public List i = new ArrayList(); + private long c = 16777215L; + public int j; + protected int k = (new Random()).nextInt(); + protected final int l = 1013904223; + protected float m; + protected float n; + protected float o; + protected float p; + public int q; + public EnumDifficulty difficulty; + public Random random = new Random(); + public WorldProvider worldProvider; // CraftBukkit - remove final + protected List u = new ArrayList(); + public IChunkProvider chunkProvider; // CraftBukkit - public + protected final IDataManager dataManager; + public WorldData worldData; // CraftBukkit - public + public boolean isLoading; + public PersistentCollection worldMaps; + public final PersistentVillage villages; + protected final VillageSiege siegeManager = new VillageSiege(this); + public final MethodProfiler methodProfiler; + private final Calendar J = Calendar.getInstance(); + public Scoreboard scoreboard = new Scoreboard(); // CraftBukkit - protected -> public + public boolean isStatic; + // CraftBukkit start - public, longhashset + // protected LongHashSet chunkTickList = new LongHashSet(); // Spigot + private int K; + public boolean allowMonsters; + public boolean allowAnimals; + // Added the following + public boolean captureBlockStates = false; + public boolean captureTreeGeneration = false; + public ArrayList capturedBlockStates = new ArrayList(); + public long ticksPerAnimalSpawns; + public long ticksPerMonsterSpawns; + public boolean populating; + private int tickPosition; + // CraftBukkit end + private ArrayList L; + private boolean M; + int[] I; + + // Spigot start + private boolean guardEntityList; + protected final net.minecraft.util.gnu.trove.map.hash.TLongShortHashMap chunkTickList; + protected float growthOdds = 100; + protected float modifiedOdds = 100; + private final byte chunkTickRadius; + public static boolean haveWeSilencedAPhysicsCrash; + public static String blockLocation; + public List triggerHoppersList = new ArrayList(); // Spigot, When altHopperTicking, tile entities being added go through here. + // Poweruser start + private LightingUpdater lightingUpdater = new LightingUpdater(); + private TaskQueueWorker lightingQueue = ThreadingManager.createTaskQueue(); + // Poweruser end + public final Map explosionDensityCache = new HashMap(); // PaperSpigot - Optimize explosions + + public double hardDespawnDistance = -1D; // MineHQ + + public static long chunkToKey(int x, int z) { + long k = ((((long) x) & 0xFFFF0000L) << 16) | ((((long) x) & 0x0000FFFFL) << 0); + k |= ((((long) z) & 0xFFFF0000L) << 32) | ((((long) z) & 0x0000FFFFL) << 16); + return k; + } + + public static int keyToX(long k) { + return (int) (((k >> 16) & 0xFFFF0000) | (k & 0x0000FFFF)); + } + + public static int keyToZ(long k) { + return (int) (((k >> 32) & 0xFFFF0000L) | ((k >> 16) & 0x0000FFFF)); + } + + // Spigot Start - Hoppers need to be born ticking. + private void initializeHoppers() { + if (this.spigotConfig.altHopperTicking) { + for (TileEntity o : this.triggerHoppersList) { + o.scheduleTicks(); + if (o instanceof TileEntityHopper) { + ((TileEntityHopper) o).convertToScheduling(); + ((TileEntityHopper) o).scheduleHopperTick(); + } + } + } + triggerHoppersList.clear(); + } + + // Helper method for altHopperTicking. Updates chests at the specified location, + // accounting for double chests. Updating the chest will update adjacent hoppers. + public void updateChestAndHoppers(int a, int b, int c) { + Block block = this.getType(a, b, c); + if (block instanceof BlockChest) { + TileEntity tile = this.getTileEntity(a, b, c); + if (tile instanceof TileEntityChest) { + tile.scheduleTicks(); + } + for (int i = 2; i < 6; i++) { + // Facing class provides arrays for direction offset. + if (this.getType(a + Facing.b[i], b, c + Facing.d[i]) == block) { + tile = this.getTileEntity(a + Facing.b[i], b, c + Facing.d[i]); + if (tile instanceof TileEntityChest) { + tile.scheduleTicks(); + } + break; + } + } + } + } + // Spigot end + + // MineHQ start + public final PlayerMap playerMap = new PlayerMap(); + // MineHQ end + + public BiomeBase getBiome(int i, int j) { + if (this.isLoaded(i, 0, j)) { + Chunk chunk = this.getChunkAtWorldCoords(i, j); + + try { + return chunk.getBiome(i & 15, j & 15, this.worldProvider.e); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Getting biome"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Coordinates of biome request"); + + crashreportsystemdetails.a("Location", (Callable) (new CrashReportWorldLocation(this, i, j))); + throw new ReportedException(crashreport); + } + } else { + return this.worldProvider.e.getBiome(i, j); + } + } + + public WorldChunkManager getWorldChunkManager() { + return this.worldProvider.e; + } + + + // Poweruser start + public ChunkProviderServer chunkProviderServer; // moved here from WorldServer + private final AutoSaveWorldData autoSaveWorldData; + + public AutoSaveWorldData getAutoSaveWorldData() { + return this.autoSaveWorldData; + } + // Poweruser end + + // CraftBukkit start + private final CraftWorld world; + public boolean pvpMode; + public boolean keepSpawnInMemory = true; + public ChunkGenerator generator; + public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot + public final org.github.paperspigot.PaperSpigotWorldConfig paperSpigotConfig; // PaperSpigot + + public final SpigotTimings.WorldTimingsHandler timings; // Spigot + + public CraftWorld getWorld() { + return this.world; + } + + public CraftServer getServer() { + return (CraftServer) Bukkit.getServer(); + } + + public Chunk getChunkIfLoaded(int x, int z) { + return ((ChunkProviderServer) this.chunkProvider).getChunkIfLoaded(x, z); + } + + public final net.valorhcf.generator.GeneratorConfig generatorConfig;// MineHQ + + // Changed signature - added gen and env + public World(IDataManager idatamanager, String s, WorldSettings worldsettings, WorldProvider worldprovider, MethodProfiler methodprofiler, ChunkGenerator gen, org.bukkit.World.Environment env) { + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(s); // Spigot + this.paperSpigotConfig = new org.github.paperspigot.PaperSpigotWorldConfig(s); // PaperSpigot + this.generatorConfig = new GeneratorConfig(s); // MineHQ + this.generator = gen; + this.world = new CraftWorld((WorldServer) this, gen, env); + this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit + this.ticksPerMonsterSpawns = this.getServer().getTicksPerMonsterSpawns(); // CraftBukkit + // CraftBukkit end + this.keepSpawnInMemory = this.paperSpigotConfig.keepSpawnInMemory; // PaperSpigot + // Spigot start + this.chunkTickRadius = (byte) ((this.getServer().getViewDistance() < 7) ? this.getServer().getViewDistance() : 7); + this.chunkTickList = new net.minecraft.util.gnu.trove.map.hash.TLongShortHashMap(spigotConfig.chunksPerTick * 5, 0.7f, Long.MIN_VALUE, Short.MIN_VALUE); + this.chunkTickList.setAutoCompactionFactor(0); + // Spigot end + + this.K = this.random.nextInt(12000); + this.allowMonsters = true; + this.allowAnimals = true; + this.L = new ArrayList(); + this.I = new int['\u8000']; + this.dataManager = idatamanager; + this.methodProfiler = methodprofiler; + this.worldMaps = new PersistentCollection(idatamanager); + this.worldData = idatamanager.getWorldData(); + if (worldprovider != null) { + this.worldProvider = worldprovider; + } else if (this.worldData != null && this.worldData.j() != 0) { + this.worldProvider = WorldProvider.byDimension(this.worldData.j()); + } else { + this.worldProvider = WorldProvider.byDimension(0); + } + + if (this.worldData == null) { + this.worldData = new WorldData(worldsettings, s); + } else { + this.worldData.setName(s); + } + + this.worldProvider.a(this); + this.chunkProvider = this.j(); + timings = new SpigotTimings.WorldTimingsHandler(this); // Spigot - code below can generate new world and access timings + if (!this.worldData.isInitialized()) { + try { + this.a(worldsettings); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception initializing level"); + + try { + this.a(crashreport); + } catch (Throwable throwable1) { + ; + } + + throw new ReportedException(crashreport); + } + + this.worldData.d(true); + } + + PersistentVillage persistentvillage = (PersistentVillage) this.worldMaps.get(PersistentVillage.class, "villages"); + + if (persistentvillage == null) { + this.villages = new PersistentVillage(this); + this.worldMaps.a("villages", this.villages); + } else { + this.villages = persistentvillage; + this.villages.a(this); + } + + this.B(); + this.a(); + + this.getServer().addWorld(this.world); // CraftBukkit + + // MineHQ - Set spawn flags based on mobsEnabled. + if (!spigotConfig.mobsEnabled) { + this.world.setSpawnFlags(false, false); + } + + this.autoSaveWorldData = new AutoSaveWorldData(this); // Poweruser + } + + protected abstract IChunkProvider j(); + + protected void a(WorldSettings worldsettings) { + this.worldData.d(true); + } + + public Block b(int i, int j) { + int k; + + for (k = 63; !this.isEmpty(i, k + 1, j); ++k) { + ; + } + + return this.getType(i, k, j); + } + + // Spigot start + public Block getType(int i, int j, int k) { + return getType(i, j, k, true); + } + + public Block getType(int i, int j, int k, boolean useCaptured) { + // CraftBukkit start - tree generation + if (captureTreeGeneration && useCaptured) { + // Spigot end + Iterator it = capturedBlockStates.iterator(); + while (it.hasNext()) { + BlockState previous = it.next(); + if (previous.getX() == i && previous.getY() == j && previous.getZ() == k) { + return CraftMagicNumbers.getBlock(previous.getTypeId()); + } + } + } + // CraftBukkit end + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000 && j >= 0 && j < 256) { + Chunk chunk = null; + + try { + chunk = this.getChunkAt(i >> 4, k >> 4); + return chunk.getType(i & 15, j, k & 15); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception getting block type in world"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Requested block coordinates"); + + crashreportsystemdetails.a("Found chunk", Boolean.valueOf(chunk == null)); + crashreportsystemdetails.a("Location", CrashReportSystemDetails.a(i, j, k)); + throw new ReportedException(crashreport); + } + } else { + return Blocks.AIR; + } + } + + public boolean isEmpty(int i, int j, int k) { + return this.getType(i, j, k).getMaterial() == Material.AIR; + } + + public boolean isLoaded(int i, int j, int k) { + return j >= 0 && j < 256 ? this.isChunkLoaded(i >> 4, k >> 4) : false; + } + + public boolean areChunksLoaded(int i, int j, int k, int l) { + return this.b(i - l, j - l, k - l, i + l, j + l, k + l); + } + + public boolean b(int i, int j, int k, int l, int i1, int j1) { + if (i1 >= 0 && j < 256) { + i >>= 4; + k >>= 4; + l >>= 4; + j1 >>= 4; + + for (int k1 = i; k1 <= l; ++k1) { + for (int l1 = k; l1 <= j1; ++l1) { + if (!this.isChunkLoaded(k1, l1)) { + return false; + } + } + } + + return true; + } else { + return false; + } + } + + protected boolean isChunkLoaded(int i, int j) { + return this.chunkProvider.isChunkLoaded(i, j); + } + + public Chunk getChunkAtWorldCoords(int i, int j) { + return this.getChunkAt(i >> 4, j >> 4); + } + + public Chunk getChunkAt(int i, int j) { + return this.chunkProvider.getOrCreateChunk(i, j); + } + + public boolean setTypeAndData(int i, int j, int k, Block block, int l, int i1) { + // CraftBukkit start - tree generation + if (this.captureTreeGeneration) { + BlockState blockstate = null; + Iterator it = capturedBlockStates.iterator(); + while (it.hasNext()) { + BlockState previous = it.next(); + if (previous.getX() == i && previous.getY() == j && previous.getZ() == k) { + blockstate = previous; + it.remove(); + break; + } + } + if (blockstate == null) { + blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, i, j, k, i1); + } + blockstate.setTypeId(CraftMagicNumbers.getId(block)); + blockstate.setRawData((byte) l); + this.capturedBlockStates.add(blockstate); + return true; + } + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + if (j < 0) { + return false; + } else if (j >= 256) { + return false; + } else { + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + Block block1 = null; + + if ((i1 & 1) != 0) { + block1 = chunk.getType(i & 15, j, k & 15); + } + + // CraftBukkit start - capture blockstates + BlockState blockstate = null; + if (this.captureBlockStates) { + blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, i, j, k, i1); + this.capturedBlockStates.add(blockstate); + } + // CraftBukkit end + + boolean flag = chunk.a(i & 15, j, k & 15, block, l); + + // CraftBukkit start - remove blockstate if failed + if (!flag && this.captureBlockStates) { + this.capturedBlockStates.remove(blockstate); + } + // CraftBukkit end + + this.methodProfiler.a("checkLight"); + this.t(i, j, k); + this.methodProfiler.b(); + // CraftBukkit start + if (flag && !this.captureBlockStates) { // Don't notify clients or update physics while capturing blockstates + // Modularize client and physic updates + this.notifyAndUpdatePhysics(i, j, k, chunk, block1, block, i1); + // CraftBukkit end + } + // Spigot start - If this block is changing to that which a chest beneath it + // becomes able to be opened, then the chest must be updated. + // block1 is the old block. block is the new block. r returns true if the block type + // prevents access to a chest. + if (this.spigotConfig.altHopperTicking && block1 != null && block1.r() && !block.r()) { + this.updateChestAndHoppers(i, j - 1, k); + } + // Spigot end + + return flag; + } + } else { + return false; + } + } + + // CraftBukkit start - Split off from original setTypeAndData(int i, int j, int k, Block block, int l, int i1) method in order to directly send client and physic updates + public void notifyAndUpdatePhysics(int i, int j, int k, Chunk chunk, Block oldBlock, Block newBlock, int flag) { + // should be isReady() + if ((flag & 2) != 0 && (chunk == null || chunk.isReady())) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement + this.notify(i, j, k); + } + + if ((flag & 1) != 0) { + this.update(i, j, k, oldBlock); + if (newBlock.isComplexRedstone()) { + this.updateAdjacentComparators(i, j, k, newBlock); + } + } + } + // CraftBukkit end + + public int getData(int i, int j, int k) { + // CraftBukkit start - tree generation + if (captureTreeGeneration) { + Iterator it = capturedBlockStates.iterator(); + while (it.hasNext()) { + BlockState previous = it.next(); + if (previous.getX() == i && previous.getY() == j && previous.getZ() == k) { + return previous.getRawData(); + } + } + } + // CraftBukkit end + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + if (j < 0) { + return 0; + } else if (j >= 256) { + return 0; + } else { + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + + i &= 15; + k &= 15; + return chunk.getData(i, j, k); + } + } else { + return 0; + } + } + + public boolean setData(int i, int j, int k, int l, int i1) { + // CraftBukkit start - tree generation + if (this.captureTreeGeneration) { + BlockState blockstate = null; + Iterator it = capturedBlockStates.iterator(); + while (it.hasNext()) { + BlockState previous = it.next(); + if (previous.getX() == i && previous.getY() == j && previous.getZ() == k) { + blockstate = previous; + it.remove(); + break; + } + } + if (blockstate == null) { + blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, i, j, k, i1); + } + blockstate.setRawData((byte) l); + this.capturedBlockStates.add(blockstate); + return true; + } + // CraftBukkit end + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + if (j < 0) { + return false; + } else if (j >= 256) { + return false; + } else { + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + int j1 = i & 15; + int k1 = k & 15; + boolean flag = chunk.a(j1, j, k1, l); + + if (flag) { + Block block = chunk.getType(j1, j, k1); + + if ((i1 & 2) != 0 && (!this.isStatic || (i1 & 4) == 0) && chunk.isReady()) { + this.notify(i, j, k); + } + + if (!this.isStatic && (i1 & 1) != 0) { + this.update(i, j, k, block); + if (block.isComplexRedstone()) { + this.updateAdjacentComparators(i, j, k, block); + } + } + } + + return flag; + } + } else { + return false; + } + } + + public boolean setAir(int i, int j, int k) { + return this.setTypeAndData(i, j, k, Blocks.AIR, 0, 3); + } + + public boolean setAir(int i, int j, int k, boolean flag) { + Block block = this.getType(i, j, k); + + if (block.getMaterial() == Material.AIR) { + return false; + } else { + int l = this.getData(i, j, k); + + this.triggerEffect(2001, i, j, k, Block.getId(block) + (l << 12)); + if (flag) { + block.b(this, i, j, k, l, 0); + } + + return this.setTypeAndData(i, j, k, Blocks.AIR, 0, 3); + } + } + + public boolean setTypeUpdate(int i, int j, int k, Block block) { + return this.setTypeAndData(i, j, k, block, 0, 3); + } + + public void notify(int i, int j, int k) { + for (int l = 0; l < this.u.size(); ++l) { + ((IWorldAccess) this.u.get(l)).a(i, j, k); + } + } + + public void update(int i, int j, int k, Block block) { + // CraftBukkit start + if (this.populating) { + return; + } + // CraftBukkit end + this.applyPhysics(i, j, k, block); + } + + public void b(int i, int j, int k, int l) { + int i1; + + if (k > l) { + i1 = l; + l = k; + k = i1; + } + + if (!this.worldProvider.g) { + for (i1 = k; i1 <= l; ++i1) { + this.updateLight(EnumSkyBlock.SKY, i, i1, j); // PaperSpigot - Asynchronous lighting updates + } + } + + this.c(i, k, j, i, l, j); + } + + public void c(int i, int j, int k, int l, int i1, int j1) { + for (int k1 = 0; k1 < this.u.size(); ++k1) { + ((IWorldAccess) this.u.get(k1)).a(i, j, k, l, i1, j1); + } + } + + public void applyPhysics(int i, int j, int k, Block block) { + this.e(i - 1, j, k, block); + this.e(i + 1, j, k, block); + this.e(i, j - 1, k, block); + this.e(i, j + 1, k, block); + this.e(i, j, k - 1, block); + this.e(i, j, k + 1, block); + spigotConfig.antiXrayInstance.updateNearbyBlocks(this, i, j, k); // Spigot + } + + public void b(int i, int j, int k, Block block, int l) { + if (l != 4) { + this.e(i - 1, j, k, block); + } + + if (l != 5) { + this.e(i + 1, j, k, block); + } + + if (l != 0) { + this.e(i, j - 1, k, block); + } + + if (l != 1) { + this.e(i, j + 1, k, block); + } + + if (l != 2) { + this.e(i, j, k - 1, block); + } + + if (l != 3) { + this.e(i, j, k + 1, block); + } + } + + public void e(int i, int j, int k, Block block) { + if (!this.isStatic) { + Block block1 = this.getType(i, j, k); + + try { + // CraftBukkit start + CraftWorld world = ((WorldServer) this).getWorld(); + if (world != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(i, j, k), CraftMagicNumbers.getId(block)); + this.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end + + block1.doPhysics(this, i, j, k, block); + } catch (StackOverflowError stackoverflowerror) { // Spigot Start + haveWeSilencedAPhysicsCrash = true; + blockLocation = i + ", " + j + ", " + k; // Spigot End + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception while updating neighbours"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being updated"); + + int l; + + try { + l = this.getData(i, j, k); + } catch (Throwable throwable1) { + l = -1; + } + + crashreportsystemdetails.a("Source block type", (Callable) (new CrashReportSourceBlockType(this, block))); + CrashReportSystemDetails.a(crashreportsystemdetails, i, j, k, block1, l); + throw new ReportedException(crashreport); + } + } + } + + public boolean a(int i, int j, int k, Block block) { + return false; + } + + public boolean i(int i, int j, int k) { + return this.getChunkAt(i >> 4, k >> 4).d(i & 15, j, k & 15); + } + + public int j(int i, int j, int k) { + if (j < 0) { + return 0; + } else { + if (j >= 256) { + j = 255; + } + + return this.getChunkAt(i >> 4, k >> 4).b(i & 15, j, k & 15, 0); + } + } + + public int getLightLevel(int i, int j, int k) { + return this.b(i, j, k, true); + } + + public int b(int i, int j, int k, boolean flag) { + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + if (flag && this.getType(i, j, k).n()) { + int l = this.b(i, j + 1, k, false); + int i1 = this.b(i + 1, j, k, false); + int j1 = this.b(i - 1, j, k, false); + int k1 = this.b(i, j, k + 1, false); + int l1 = this.b(i, j, k - 1, false); + + if (i1 > l) { + l = i1; + } + + if (j1 > l) { + l = j1; + } + + if (k1 > l) { + l = k1; + } + + if (l1 > l) { + l = l1; + } + + return l; + } else if (j < 0) { + return 0; + } else { + if (j >= 256) { + j = 255; + } + + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + + i &= 15; + k &= 15; + return chunk.b(i, j, k, this.j); + } + } else { + return 15; + } + } + + // MineHQ start + public boolean isLightLevel(int i, int j, int k, int level) { + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + if (this.getType(i, j, k).n()) { + if (this.b(i, j + 1, k, false) >= level) { + return true; + } + if (this.b(i + 1, j, k, false) >= level) { + return true; + } + if (this.b(i - 1, j, k, false) >= level) { + return true; + } + if (this.b(i, j, k + 1, false) >= level) { + return true; + } + if (this.b(i, j, k - 1, false) >= level) { + return true; + } + return false; + } else { + if (j >= 256) { + j = 255; + } + + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + + i &= 15; + k &= 15; + return chunk.b(i, j, k, this.j) >= level; + } + } else { + return true; + } + } + // MineHQ end + + public int getHighestBlockYAt(int i, int j) { + // Poweruser start + return this.getHighestBlockYAt(i, j, false); + } + + public int getHighestBlockYAt(int i, int j, boolean chunksHaveAlreadyBeenChecked) { + // Poweruser end + if (i >= -30000000 && j >= -30000000 && i < 30000000 && j < 30000000) { + if (!chunksHaveAlreadyBeenChecked && !this.isChunkLoaded(i >> 4, j >> 4)) { // Poweruser + return 0; + } else { + Chunk chunk = this.getChunkAt(i >> 4, j >> 4); + + return chunk.b(i & 15, j & 15); + } + } else { + return 64; + } + } + + public int g(int i, int j) { + // Poweruser start + return this.g(i, j, false); + } + + public int g(int i, int j, boolean chunksHaveAlreadyBeenChecked) { + // Poweruser end + if (i >= -30000000 && j >= -30000000 && i < 30000000 && j < 30000000) { + if (!chunksHaveAlreadyBeenChecked && !this.isChunkLoaded(i >> 4, j >> 4)) { // Poweruser + return 0; + } else { + Chunk chunk = this.getChunkAt(i >> 4, j >> 4); + + return chunk.r; + } + } else { + return 64; + } + } + + public int b(EnumSkyBlock enumskyblock, int i, int j, int k) { + if (j < 0) { + j = 0; + } + + if (j >= 256) { + j = 255; + } + + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + int l = i >> 4; + int i1 = k >> 4; + + if (!this.isChunkLoaded(l, i1)) { + return enumskyblock.c; + } else { + Chunk chunk = this.getChunkAt(l, i1); + + return chunk.getBrightness(enumskyblock, i & 15, j, k & 15); + } + } else { + return enumskyblock.c; + } + } + + public void b(EnumSkyBlock enumskyblock, int i, int j, int k, int l) { + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + if (j >= 0) { + if (j < 256) { + if (this.isChunkLoaded(i >> 4, k >> 4)) { + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + + chunk.a(enumskyblock, i & 15, j, k & 15, l); + + for (int i1 = 0; i1 < this.u.size(); ++i1) { + ((IWorldAccess) this.u.get(i1)).b(i, j, k); + } + } + } + } + } + } + + public void m(int i, int j, int k) { + for (int l = 0; l < this.u.size(); ++l) { + ((IWorldAccess) this.u.get(l)).b(i, j, k); + } + } + + public float n(int i, int j, int k) { + return this.worldProvider.h[this.getLightLevel(i, j, k)]; + } + + public boolean w() { + return this.j < 4; + } + + public MovingObjectPosition a(Vec3D vec3d, Vec3D vec3d1) { + return this.rayTrace(vec3d, vec3d1, false, false, false); + } + + public MovingObjectPosition rayTrace(Vec3D vec3d, Vec3D vec3d1, boolean flag) { + return this.rayTrace(vec3d, vec3d1, flag, false, false); + } + + public MovingObjectPosition rayTrace(Vec3D position, Vec3D motion, boolean flag, boolean flag1, boolean flag2) { + if (!Double.isNaN(position.a) && !Double.isNaN(position.b) && !Double.isNaN(position.c)) { + if (!Double.isNaN(motion.a) && !Double.isNaN(motion.b) && !Double.isNaN(motion.c)) { + int i = MathHelper.floor(motion.a); + int j = MathHelper.floor(motion.b); + int k = MathHelper.floor(motion.c); + int l = MathHelper.floor(position.a); + int i1 = MathHelper.floor(position.b); + int j1 = MathHelper.floor(position.c); + Block block = this.getType(l, i1, j1); + int k1 = this.getData(l, i1, j1); + + if ((!flag1 || block.a(this, l, i1, j1) != null) && block.a(k1, flag)) { + MovingObjectPosition movingobjectposition = block.a(this, l, i1, j1, position, motion); + + if (movingobjectposition != null) { + return movingobjectposition; + } + } + + MovingObjectPosition movingobjectposition1 = null; + + k1 = 200; + + while (k1-- >= 0) { + if (Double.isNaN(position.a) || Double.isNaN(position.b) || Double.isNaN(position.c)) { + return null; + } + + if (l == i && i1 == j && j1 == k) { + return flag2 ? movingobjectposition1 : null; + } + + boolean flag3 = true; + boolean flag4 = true; + boolean flag5 = true; + double d0 = 999.0D; + double d1 = 999.0D; + double d2 = 999.0D; + + if (i > l) { + d0 = (double) l + 1.0D; + } else if (i < l) { + d0 = (double) l + 0.0D; + } else { + flag3 = false; + } + + if (j > i1) { + d1 = (double) i1 + 1.0D; + } else if (j < i1) { + d1 = (double) i1 + 0.0D; + } else { + flag4 = false; + } + + if (k > j1) { + d2 = (double) j1 + 1.0D; + } else if (k < j1) { + d2 = (double) j1 + 0.0D; + } else { + flag5 = false; + } + + double d3 = 999.0D; + double d4 = 999.0D; + double d5 = 999.0D; + double d6 = motion.a - position.a; + double d7 = motion.b - position.b; + double d8 = motion.c - position.c; + + if (flag3) { + d3 = (d0 - position.a) / d6; + } + + if (flag4) { + d4 = (d1 - position.b) / d7; + } + + if (flag5) { + d5 = (d2 - position.c) / d8; + } + + boolean flag6 = false; + byte b0; + + if (d3 < d4 && d3 < d5) { + if (i > l) { + b0 = 4; + } else { + b0 = 5; + } + + position.a = d0; + position.b += d7 * d3; + position.c += d8 * d3; + } else if (d4 < d5) { + if (j > i1) { + b0 = 0; + } else { + b0 = 1; + } + + position.a += d6 * d4; + position.b = d1; + position.c += d8 * d4; + } else { + if (k > j1) { + b0 = 2; + } else { + b0 = 3; + } + + position.a += d6 * d5; + position.b += d7 * d5; + position.c = d2; + } + + Vec3D vec3d2 = Vec3D.a(position.a, position.b, position.c); + + l = (int) (vec3d2.a = (double) MathHelper.floor(position.a)); + if (b0 == 5) { + --l; + ++vec3d2.a; + } + + i1 = (int) (vec3d2.b = (double) MathHelper.floor(position.b)); + if (b0 == 1) { + --i1; + ++vec3d2.b; + } + + j1 = (int) (vec3d2.c = (double) MathHelper.floor(position.c)); + if (b0 == 3) { + --j1; + ++vec3d2.c; + } + + Block block1 = this.getType(l, i1, j1); + int l1 = this.getData(l, i1, j1); + + if (!flag1 || block1.a(this, l, i1, j1) != null) { + if (block1.a(l1, flag)) { + MovingObjectPosition movingobjectposition2 = block1.a(this, l, i1, j1, position, motion); + + if (movingobjectposition2 != null) { + return movingobjectposition2; + } + } else { + movingobjectposition1 = new MovingObjectPosition(l, i1, j1, b0, position, false); + } + } + } + + return flag2 ? movingobjectposition1 : null; + } else { + return null; + } + } else { + return null; + } + } + + public void makeSound(Entity entity, String s, float f, float f1) { + for (int i = 0; i < this.u.size(); ++i) { + ((IWorldAccess) this.u.get(i)).a(s, entity.locX, entity.locY - (double) entity.height, entity.locZ, f, f1); + } + } + + public void a(EntityHuman entityhuman, String s, float f, float f1) { + for (int i = 0; i < this.u.size(); ++i) { + ((IWorldAccess) this.u.get(i)).a(entityhuman, s, entityhuman.locX, entityhuman.locY - (double) entityhuman.height, entityhuman.locZ, f, f1); + } + } + + // MineHQ start - hack to silence sounds from cancelled block place + private boolean interceptSounds = false; + private final List interceptedSounds = new ArrayList(); + + public void interceptSounds() { + interceptSounds = true; + } + + public void sendInterceptedSounds() { + for (Runnable r : interceptedSounds) { + r.run(); + } + interceptedSounds.clear(); + interceptSounds = false; + } + + public void clearInterceptedSounds() { + interceptedSounds.clear(); + interceptSounds = false; + } + + public void makeSound(final double d0, final double d1, final double d2, final String s, final float f, final float f1) { + if (interceptSounds && org.bukkit.Bukkit.isPrimaryThread()) { + interceptedSounds.add(new Runnable() { + @Override + public void run() { + for (int i = 0; i < World.this.u.size(); ++i) { + ((IWorldAccess) World.this.u.get(i)).a(s, d0, d1, d2, f, f1); + } + } + }); + return; + } + // MineHQ end + for (int i = 0; i < this.u.size(); ++i) { + ((IWorldAccess) this.u.get(i)).a(s, d0, d1, d2, f, f1); + } + } + + public void a(double d0, double d1, double d2, String s, float f, float f1, boolean flag) { + } + + public void a(String s, int i, int j, int k) { + for (int l = 0; l < this.u.size(); ++l) { + ((IWorldAccess) this.u.get(l)).a(s, i, j, k); + } + } + + public void addParticle(String s, double d0, double d1, double d2, double d3, double d4, double d5) { + for (int i = 0; i < this.u.size(); ++i) { + ((IWorldAccess) this.u.get(i)).a(s, d0, d1, d2, d3, d4, d5); + } + } + + public boolean strikeLightning(Entity entity) { + this.i.add(entity); + return true; + } + + public boolean addEntity(Entity entity) { + // CraftBukkit start - Used for entities other than creatures + return this.addEntity(entity, SpawnReason.DEFAULT); // Set reason as DEFAULT + } + + public boolean addEntity(Entity entity, SpawnReason spawnReason) { // Changed signature, added SpawnReason + org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot + if (entity == null) return false; + // CraftBukkit end + + int i = MathHelper.floor(entity.locX / 16.0D); + int j = MathHelper.floor(entity.locZ / 16.0D); + boolean flag = entity.attachedToPlayer; + + if (entity instanceof EntityHuman) { + flag = true; + } + + // CraftBukkit start + org.bukkit.event.Cancellable event = null; + if (entity instanceof EntityLiving && !(entity instanceof EntityPlayer)) { + boolean isAnimal = entity instanceof EntityAnimal || entity instanceof EntityWaterAnimal || entity instanceof EntityGolem; + boolean isMonster = entity instanceof EntityMonster || entity instanceof EntityGhast || entity instanceof EntitySlime; + + if (spawnReason != SpawnReason.CUSTOM) { + if (isAnimal && !allowAnimals || isMonster && !allowMonsters) { + entity.dead = true; + return false; + } + } + + event = CraftEventFactory.callCreatureSpawnEvent((EntityLiving) entity, spawnReason); + } else if (entity instanceof EntityItem) { + event = CraftEventFactory.callItemSpawnEvent((EntityItem) entity); + } else if (entity.getBukkitEntity() instanceof org.bukkit.entity.Projectile) { + // Not all projectiles extend EntityProjectile, so check for Bukkit interface instead + event = CraftEventFactory.callProjectileLaunchEvent(entity); + } + // Spigot start + else if (entity instanceof EntityExperienceOrb) { + EntityExperienceOrb xp = (EntityExperienceOrb) entity; + double radius = spigotConfig.expMerge; + if (radius > 0) { + List entities = this.getEntities(entity, entity.boundingBox.grow(radius, radius, radius)); + for (Entity e : entities) { + if (e instanceof EntityExperienceOrb) { + EntityExperienceOrb loopItem = (EntityExperienceOrb) e; + if (!loopItem.dead) { + xp.value += loopItem.value; + loopItem.die(); + } + } + } + } + } // Spigot end + + if (event != null && (event.isCancelled() || entity.dead)) { + entity.dead = true; + return false; + } + // CraftBukkit end + + if (!flag && !this.isChunkLoaded(i, j)) { + entity.dead = true; // CraftBukkit + return false; + } else { + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + this.players.add(entityhuman); + this.playerMap.add((EntityPlayer) entityhuman); // MineHQ + this.everyoneSleeping(); + this.b(entity); + } + + this.getChunkAt(i, j).a(entity); + this.entityList.add(entity); + this.a(entity); + return true; + } + } + + protected void a(Entity entity) { + for (int i = 0; i < this.u.size(); ++i) { + ((IWorldAccess) this.u.get(i)).a(entity); + } + + entity.valid = true; // CraftBukkit + } + + protected void b(Entity entity) { + for (int i = 0; i < this.u.size(); ++i) { + ((IWorldAccess) this.u.get(i)).b(entity); + } + + entity.valid = false; // CraftBukkit + } + + public void kill(Entity entity) { + if (entity.passenger != null) { + entity.passenger.mount((Entity) null); + } + + if (entity.vehicle != null) { + entity.mount((Entity) null); + } + + entity.die(); + if (entity instanceof EntityHuman) { + this.players.remove(entity); + this.playerMap.remove((EntityPlayer) entity); // MineHQ + // Spigot start + for (Object o : worldMaps.c) { + if (o instanceof WorldMap) { + WorldMap map = (WorldMap) o; + map.i.remove(entity); + for (Iterator iter = (Iterator) map.f.iterator(); iter.hasNext(); ) { + if (iter.next().trackee == entity) { + iter.remove(); + } + } + } + } + // Spigot end + this.everyoneSleeping(); + } + } + + public void removeEntity(Entity entity) { + org.spigotmc.AsyncCatcher.catchOp("entity remove"); // Spigot + entity.die(); + if (entity instanceof EntityHuman) { + this.players.remove(entity); + this.playerMap.remove((EntityPlayer) entity); // MineHQ + this.everyoneSleeping(); + } + // Spigot start + if (!guardEntityList) { // It will get removed after the tick if we are ticking + int i = entity.ah; + int j = entity.aj; + if (entity.ag && this.isChunkLoaded(i, j)) { + this.getChunkAt(i, j).b(entity); + } + // CraftBukkit start - Decrement loop variable field if we've already ticked this entity + int index = this.entityList.indexOf(entity); + if (index != -1) { + if (index <= this.tickPosition) { + this.tickPosition--; + } + this.entityList.remove(index); + } + // CraftBukkit end + } + // Spigot end + + this.b(entity); + } + + public void addIWorldAccess(IWorldAccess iworldaccess) { + this.u.add(iworldaccess); + } + + public List getCubes(Entity entity, AxisAlignedBB axisalignedbb) { + this.L.clear(); + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + // Spigot start + int ystart = ((k - 1) < 0) ? 0 : (k - 1); + for (int chunkx = (i >> 4); chunkx <= ((j - 1) >> 4); chunkx++) { + int cx = chunkx << 4; + for (int chunkz = (i1 >> 4); chunkz <= ((j1 - 1) >> 4); chunkz++) { + if (!this.isChunkLoaded(chunkx, chunkz)) { + entity.inUnloadedChunk = true; // PaperSpigot - Remove entities in unloaded chunks + continue; + } + int cz = chunkz << 4; + Chunk chunk = this.getChunkAt(chunkx, chunkz); + // Compute ranges within chunk + int xstart = (i < cx) ? cx : i; + int xend = (j < (cx + 16)) ? j : (cx + 16); + int zstart = (i1 < cz) ? cz : i1; + int zend = (j1 < (cz + 16)) ? j1 : (cz + 16); + // Loop through blocks within chunk + for (int x = xstart; x < xend; x++) { + for (int z = zstart; z < zend; z++) { + for (int y = ystart; y < l; y++) { + Block block = chunk.getType(x - cx, y, z - cz); + if (block != null && block != Blocks.AIR) // MineHQ + { + // PaperSpigot start - FallingBlocks and TNT collide with specific non-collidable blocks + if (entity.world.paperSpigotConfig.fallingBlocksCollideWithSigns && (entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && + (block instanceof BlockSign || block instanceof BlockFenceGate || block instanceof BlockTorch || block instanceof BlockButtonAbstract || block instanceof BlockLever || block instanceof BlockTripwireHook || block instanceof BlockTripwire)) { + AxisAlignedBB aabb = AxisAlignedBB.a(x, y, z, x + 1.0, y + 1.0, z + 1.0); + if (axisalignedbb.b(aabb)) this.L.add(aabb); + } else { + block.a(this, x, y, z, axisalignedbb, this.L, entity); + } + // PaperSpigot end + } + } + } + } + } + } + // Spigot end + + /*double d0 = 0.25D; + List list = this.getEntities(entity, axisalignedbb.grow(d0, d0, d0)); + + for (int j2 = 0; j2 < list.size(); ++j2) { + AxisAlignedBB axisalignedbb1 = ((Entity) list.get(j2)).J(); + + if (axisalignedbb1 != null && axisalignedbb1.b(axisalignedbb)) { + this.L.add(axisalignedbb1); + } + + axisalignedbb1 = entity.h((Entity) list.get(j2)); + if (axisalignedbb1 != null && axisalignedbb1.b(axisalignedbb)) { + this.L.add(axisalignedbb1); + } + }*/ + + return this.L; + } + + public List a(AxisAlignedBB axisalignedbb) { + this.L.clear(); + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = i1; l1 < j1; ++l1) { + if (this.isLoaded(k1, 64, l1)) { + for (int i2 = k - 1; i2 < l; ++i2) { + Block block; + + if (k1 >= -30000000 && k1 < 30000000 && l1 >= -30000000 && l1 < 30000000) { + block = this.getType(k1, i2, l1); + } else { + block = Blocks.BEDROCK; + } + + if (block != Blocks.AIR) block.a(this, k1, i2, l1, axisalignedbb, this.L, (Entity) null); + } + } + } + } + + return this.L; + } + + public int a(float f) { + float f1 = this.c(f); + float f2 = 1.0F - (MathHelper.cos(f1 * 3.1415927F * 2.0F) * 2.0F + 0.5F); + + if (f2 < 0.0F) { + f2 = 0.0F; + } + + if (f2 > 1.0F) { + f2 = 1.0F; + } + + f2 = 1.0F - f2; + f2 = (float) ((double) f2 * (1.0D - (double) (this.j(f) * 5.0F) / 16.0D)); + f2 = (float) ((double) f2 * (1.0D - (double) (this.h(f) * 5.0F) / 16.0D)); + f2 = 1.0F - f2; + return (int) (f2 * 11.0F); + } + + public float c(float f) { + return this.worldProvider.a(this.worldData.getDayTime(), f); + } + + public float y() { + return WorldProvider.a[this.worldProvider.a(this.worldData.getDayTime())]; + } + + public float d(float f) { + float f1 = this.c(f); + + return f1 * 3.1415927F * 2.0F; + } + + public int h(int i, int j) { + return this.getChunkAtWorldCoords(i, j).d(i & 15, j & 15); + } + + public int i(int i, int j) { + Chunk chunk = this.getChunkAtWorldCoords(i, j); + int k = chunk.h() + 15; + + i &= 15; + + for (j &= 15; k > 0; --k) { + Block block = chunk.getType(i, k, j); + + if (block.getMaterial().isSolid() && block.getMaterial() != Material.LEAVES) { + return k + 1; + } + } + + return -1; + } + + public void a(int i, int j, int k, Block block, int l) { + } + + public void a(int i, int j, int k, Block block, int l, int i1) { + } + + public void b(int i, int j, int k, Block block, int l, int i1) { + } + + public void tickEntities() { + this.methodProfiler.a("entities"); + this.methodProfiler.a("global"); + + int i; + Entity entity; + CrashReport crashreport; + CrashReportSystemDetails crashreportsystemdetails; + + for (i = 0; i < this.i.size(); ++i) { + entity = (Entity) this.i.get(i); + // CraftBukkit start - Fixed an NPE + if (entity == null) { + continue; + } + // CraftBukkit end + + try { + ++entity.ticksLived; + entity.h(); + } catch (Throwable throwable) { + crashreport = CrashReport.a(throwable, "Ticking entity"); + crashreportsystemdetails = crashreport.a("Entity being ticked"); + if (entity == null) { + crashreportsystemdetails.a("Entity", "~~NULL~~"); + } else { + entity.a(crashreportsystemdetails); + } + + throw new ReportedException(crashreport); + } + + if (entity.dead) { + this.i.remove(i--); + } + } + + this.methodProfiler.c("remove"); + this.entityList.removeAll(this.f); + + int j; + int k; + + for (i = 0; i < this.f.size(); ++i) { + entity = (Entity) this.f.get(i); + j = entity.ah; + k = entity.aj; + if (entity.ag && this.isChunkLoaded(j, k)) { + this.getChunkAt(j, k).b(entity); + } + } + + for (i = 0; i < this.f.size(); ++i) { + this.b((Entity) this.f.get(i)); + } + + this.f.clear(); + this.methodProfiler.c("regular"); + + org.spigotmc.ActivationRange.activateEntities(this); // Spigot + timings.entityTick.startTiming(); // Spigot + guardEntityList = true; // Spigot + // CraftBukkit start - Use field for loop variable + for (this.tickPosition = 0; this.tickPosition < this.entityList.size(); ++this.tickPosition) { + entity = (Entity) this.entityList.get(this.tickPosition); + if (entity.vehicle != null) { + if (!entity.vehicle.dead && entity.vehicle.passenger == entity) { + continue; + } + + entity.vehicle.passenger = null; + entity.vehicle = null; + } + + this.methodProfiler.a("tick"); + if (!entity.dead) { + try { + SpigotTimings.tickEntityTimer.startTiming(); // Spigot + this.playerJoinedWorld(entity); + SpigotTimings.tickEntityTimer.stopTiming(); // Spigot + } catch (Throwable throwable1) { + // PaperSpigot start + SpigotTimings.tickEntityTimer.stopTiming(); // Spigot + System.err.println("Entity threw exception at " + entity.world.getWorld().getName() + ":" + entity.locX + "," + entity.locY + "," + entity.locZ); + throwable1.printStackTrace(); + entity.dead = true; + continue; + /* + crashreport = CrashReport.a(throwable1, "Ticking entity"); + crashreportsystemdetails = crashreport.a("Entity being ticked"); + entity.a(crashreportsystemdetails); + throw new ReportedException(crashreport); + */ + // PaperSpigot end + } + } + + this.methodProfiler.b(); + this.methodProfiler.a("remove"); + if (entity.dead) { + j = entity.ah; + k = entity.aj; + if (entity.ag && this.isChunkLoaded(j, k)) { + this.getChunkAt(j, k).b(entity); + } + + guardEntityList = false; // Spigot + this.entityList.remove(this.tickPosition--); // CraftBukkit - Use field for loop variable + guardEntityList = true; // Spigot + this.b(entity); + } + + this.methodProfiler.b(); + } + guardEntityList = false; // Spigot + + timings.entityTick.stopTiming(); // Spigot + this.methodProfiler.c("blockEntities"); + timings.tileEntityTick.startTiming(); // Spigot + this.M = true; + // CraftBukkit start - From below, clean up tile entities before ticking them + if (!this.b.isEmpty()) { + long aaa = tileEntityList.size(); + long bbb = this.b.size(); + long time = System.nanoTime(); + + this.tileEntityList.removeAll(this.b); + + long elapsed = System.nanoTime() - time; + long ms = elapsed / 1000000; + + if (ms > 50) { + getServer().getLogger().warning(String.format( + "tileEntityList.removeAll took %s ms: %s, %s", + ms, aaa, bbb + )); + } + + this.b.clear(); + } + // Spigot End + + this.initializeHoppers(); // Spigot - Initializes hoppers which have been added recently. + Iterator iterator = this.tileEntityList.iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + // Spigot start + if (tileentity == null) { + getServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash"); + iterator.remove(); + continue; + } + // Spigot end + + if (!tileentity.r() && tileentity.o() && this.isLoaded(tileentity.x, tileentity.y, tileentity.z)) { + try { + tileentity.tickTimer.startTiming(); // Spigot + tileentity.h(); + tileentity.tickTimer.stopTiming(); // Spigot + } catch (Throwable throwable2) { + // PaperSpigot start + tileentity.tickTimer.stopTiming(); // Spigot + System.err.println("TileEntity threw exception at " + tileentity.world.getWorld().getName() + ":" + tileentity.x + "," + tileentity.y + "," + tileentity.z); + throwable2.printStackTrace(); + iterator.remove(); + continue; + /* + crashreport = CrashReport.a(throwable2, "Ticking block entity"); + crashreportsystemdetails = crashreport.a("Block entity being ticked"); + tileentity.a(crashreportsystemdetails); + throw new ReportedException(crashreport); + */ + // PaperSpigot end + } + } + + if (tileentity.r()) { + iterator.remove(); + if (this.isChunkLoaded(tileentity.x >> 4, tileentity.z >> 4)) { + Chunk chunk = this.getChunkAt(tileentity.x >> 4, tileentity.z >> 4); + + if (chunk != null) { + chunk.f(tileentity.x & 15, tileentity.y, tileentity.z & 15); + } + } + } + } + + timings.tileEntityTick.stopTiming(); // Spigot + timings.tileEntityPending.startTiming(); // Spigot + this.M = false; + /* CraftBukkit start - Moved up + if (!this.b.isEmpty()) { + this.tileEntityList.removeAll(this.b); + this.b.clear(); + } + */ // CraftBukkit end + + this.methodProfiler.c("pendingBlockEntities"); + if (!this.a.isEmpty()) { + for (int l = 0; l < this.a.size(); ++l) { + TileEntity tileentity1 = (TileEntity) this.a.get(l); + + if (!tileentity1.r()) { + /* CraftBukkit start - Order matters, moved down + if (!this.tileEntityList.contains(tileentity1)) { + this.tileEntityList.add(tileentity1); + } + // CraftBukkit end */ + + if (this.isChunkLoaded(tileentity1.x >> 4, tileentity1.z >> 4)) { + Chunk chunk1 = this.getChunkAt(tileentity1.x >> 4, tileentity1.z >> 4); + + if (chunk1 != null) { + chunk1.a(tileentity1.x & 15, tileentity1.y, tileentity1.z & 15, tileentity1); + // CraftBukkit start - Moved down from above + if (!this.tileEntityList.contains(tileentity1)) { + this.tileEntityList.add(tileentity1); + } + // CraftBukkit end + } + } + + this.notify(tileentity1.x, tileentity1.y, tileentity1.z); + } + } + + this.a.clear(); + } + + timings.tileEntityPending.stopTiming(); // Spigot + this.methodProfiler.b(); + this.methodProfiler.b(); + } + + public void a(Collection collection) { + if (this.M) { + this.a.addAll(collection); + } else { + this.tileEntityList.addAll(collection); + } + } + + public void playerJoinedWorld(Entity entity) { + this.entityJoinedWorld(entity, true); + } + + public void entityJoinedWorld(Entity entity, boolean flag) { + int i = MathHelper.floor(entity.locX); + int j = MathHelper.floor(entity.locZ); + byte b0 = 32; + + MinecraftServer.getServer().entities++; // Kohi + + // Spigot start + if (!org.spigotmc.ActivationRange.checkIfActive(entity)) { + entity.ticksLived++; + entity.inactiveTick(); + // PaperSpigot start - Remove entities in unloaded chunks + if (!this.isChunkLoaded(i, j) && ((entity instanceof EntityEnderPearl && this.paperSpigotConfig.removeUnloadedEnderPearls) || + (entity instanceof EntityFallingBlock && this.paperSpigotConfig.removeUnloadedFallingBlocks) || + (entity instanceof EntityTNTPrimed && this.paperSpigotConfig.removeUnloadedTNTEntities))) { + entity.inUnloadedChunk = true; + entity.die(); + } + // PaperSpigot end + } else { + MinecraftServer.getServer().activeEntities++; // Kohi + entity.tickTimer.startTiming(); // Spigot + // CraftBukkit end + entity.S = entity.locX; + entity.T = entity.locY; + entity.U = entity.locZ; + entity.lastYaw = entity.yaw; + entity.lastPitch = entity.pitch; + if (flag && entity.ag) { + ++entity.ticksLived; + if (entity.vehicle != null) { + entity.ab(); + } else { + entity.h(); + } + } + + this.methodProfiler.a("chunkCheck"); + if (Double.isNaN(entity.locX) || Double.isInfinite(entity.locX)) { + entity.locX = entity.S; + } + + if (Double.isNaN(entity.locY) || Double.isInfinite(entity.locY)) { + entity.locY = entity.T; + } + + if (Double.isNaN(entity.locZ) || Double.isInfinite(entity.locZ)) { + entity.locZ = entity.U; + } + + if (Double.isNaN((double) entity.pitch) || Double.isInfinite((double) entity.pitch)) { + entity.pitch = entity.lastPitch; + } + + if (Double.isNaN((double) entity.yaw) || Double.isInfinite((double) entity.yaw)) { + entity.yaw = entity.lastYaw; + } + + int k = MathHelper.floor(entity.locX / 16.0D); + int l = MathHelper.floor(entity.locY / 16.0D); + int i1 = MathHelper.floor(entity.locZ / 16.0D); + + if (!entity.ag || entity.ah != k || entity.ai != l || entity.aj != i1) { + if (entity.loadChunks) entity.loadChunks(); // PaperSpigot - Force load chunks + if (entity.ag && this.isChunkLoaded(entity.ah, entity.aj)) { + this.getChunkAt(entity.ah, entity.aj).a(entity, entity.ai); + } + + if (this.isChunkLoaded(k, i1)) { + entity.ag = true; + this.getChunkAt(k, i1).a(entity); + } else { + entity.ag = false; + } + } + + this.methodProfiler.b(); + if (flag && entity.ag && entity.passenger != null) { + if (!entity.passenger.dead && entity.passenger.vehicle == entity) { + this.playerJoinedWorld(entity.passenger); + } else { + entity.passenger.vehicle = null; + entity.passenger = null; + } + } + entity.tickTimer.stopTiming(); // Spigot + } + } + + public boolean b(AxisAlignedBB axisalignedbb) { + return this.a(axisalignedbb, (Entity) null); + } + + public boolean a(AxisAlignedBB axisalignedbb, Entity entity) { + List list = this.getEntities((Entity) null, axisalignedbb); + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + // PaperSpigot start - Allow block placement if the placer cannot see the blocker + if (entity instanceof EntityPlayer && entity1 instanceof EntityPlayer) { + if (!((EntityPlayer) entity).getBukkitEntity().canSee(((EntityPlayer) entity1).getBukkitEntity())) { + continue; + } + } + // PaperSpigot end + + if (!entity1.dead && entity1.k && entity1 != entity) { + return false; + } + } + + return true; + } + + public boolean c(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + if (axisalignedbb.a < 0.0D) { + --i; + } + + if (axisalignedbb.b < 0.0D) { + --k; + } + + if (axisalignedbb.c < 0.0D) { + --i1; + } + + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + Block block = this.getType(k1, l1, i2); + + if (block.getMaterial() != Material.AIR) { + return true; + } + } + } + } + + return false; + } + + public boolean containsLiquid(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + if (axisalignedbb.a < 0.0D) { + --i; + } + + if (axisalignedbb.b < 0.0D) { + --k; + } + + if (axisalignedbb.c < 0.0D) { + --i1; + } + + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + Block block = this.getType(k1, l1, i2); + + if (block.getMaterial().isLiquid()) { + return true; + } + } + } + } + + return false; + } + + public boolean e(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + if (this.b(i, k, i1, j, l, j1)) { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + Block block = this.getType(k1, l1, i2); + + if (block == Blocks.FIRE || block == Blocks.LAVA || block == Blocks.STATIONARY_LAVA) { + return true; + } + } + } + } + } + + return false; + } + + public boolean a(AxisAlignedBB axisalignedbb, Material material, Entity entity) { + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + if (!this.b(i, k, i1, j, l, j1)) { + return false; + } else { + boolean flag = false; + Vec3D vec3d = Vec3D.a(0.0D, 0.0D, 0.0D); + + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + Block block = this.getType(k1, l1, i2); + + if (block.getMaterial() == material) { + double d0 = (double) ((float) (l1 + 1) - BlockFluids.b(this.getData(k1, l1, i2))); + + if ((double) l >= d0) { + flag = true; + block.a(this, k1, l1, i2, entity, vec3d); + } + } + } + } + } + + if (vec3d.b() > 0.0D && entity.aC()) { + vec3d = vec3d.a(); + double d1 = 0.014D; + + entity.motX += vec3d.a * d1; + entity.motY += vec3d.b * d1; + entity.motZ += vec3d.c * d1; + } + + return flag; + } + } + + public boolean a(AxisAlignedBB axisalignedbb, Material material) { + // Poweruser start + return this.a(axisalignedbb, material, this); + } + + public boolean a(AxisAlignedBB axisalignedbb, Material material, IBlockAccess iblockaccess) { + // Poweruser end + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + if (iblockaccess.getType(k1, l1, i2).getMaterial() == material) { // Poweruser + return true; + } + } + } + } + + return false; + } + + // Alfie start + public boolean boundingBoxContainsMaterials(AxisAlignedBB boundingBox, Set matching) { + int i = MathHelper.floor(boundingBox.a); + int j = MathHelper.floor(boundingBox.d + 1.0D); + int k = MathHelper.floor(boundingBox.b); + int l = MathHelper.floor(boundingBox.e + 1.0D); + int i1 = MathHelper.floor(boundingBox.c); + int j1 = MathHelper.floor(boundingBox.f + 1.0D); + + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + if (matching.contains(getType(k1, l1, i2))) { + return true; + } + } + } + } + + return false; + } + // Alfie end + + public boolean b(AxisAlignedBB axisalignedbb, Material material) { + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + Block block = this.getType(k1, l1, i2); + + if (block.getMaterial() == material) { + int j2 = this.getData(k1, l1, i2); + double d0 = (double) (l1 + 1); + + if (j2 < 8) { + d0 = (double) (l1 + 1) - (double) j2 / 8.0D; + } + + if (d0 >= axisalignedbb.b) { + return true; + } + } + } + } + } + + return false; + } + + public Explosion explode(Entity entity, double d0, double d1, double d2, float f, boolean flag) { + return this.createExplosion(entity, d0, d1, d2, f, false, flag); + } + + public Explosion createExplosion(Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { + Explosion explosion = new Explosion(this, entity, d0, d1, d2, f); + + explosion.a = flag; + explosion.b = flag1; + explosion.a(); + explosion.a(true); + return explosion; + } + + public float a(Vec3D vec3d, AxisAlignedBB axisalignedbb) { + double d0 = 1.0D / ((axisalignedbb.d - axisalignedbb.a) * 2.0D + 1.0D); + double d1 = 1.0D / ((axisalignedbb.e - axisalignedbb.b) * 2.0D + 1.0D); + double d2 = 1.0D / ((axisalignedbb.f - axisalignedbb.c) * 2.0D + 1.0D); + + // PaperSpigot start - Center TNT sample points for more accurate calculations + // Shift the sample points so they are centered on the BB + double xOffset = (1.0 - Math.floor(1.0 / d0) * d0) / 2.0; + double zOffset = (1.0 - Math.floor(1.0 / d2) * d2) / 2.0; + // PaperSpigot end + + if (d0 >= 0.0D && d1 >= 0.0D && d2 >= 0.0D) { + int i = 0; + int j = 0; + + Vec3D vec3d2 = vec3d.a(0, 0, 0); // CraftBukkit + for (float f = 0.0F; f <= 1.0F; f = (float) ((double) f + d0)) { + for (float f1 = 0.0F; f1 <= 1.0F; f1 = (float) ((double) f1 + d1)) { + for (float f2 = 0.0F; f2 <= 1.0F; f2 = (float) ((double) f2 + d2)) { + double d3 = axisalignedbb.a + (axisalignedbb.d - axisalignedbb.a) * (double) f; + double d4 = axisalignedbb.b + (axisalignedbb.e - axisalignedbb.b) * (double) f1; + double d5 = axisalignedbb.c + (axisalignedbb.f - axisalignedbb.c) * (double) f2; + + if (this.a(vec3d2.b(xOffset + d3, d4, zOffset + d5), vec3d) == null) { // CraftBukkit // PaperSpigot + ++i; + } + + ++j; + } + } + } + + return (float) i / (float) j; + } else { + return 0.0F; + } + } + + public boolean douseFire(EntityHuman entityhuman, int i, int j, int k, int l) { + if (l == 0) { + --j; + } + + if (l == 1) { + ++j; + } + + if (l == 2) { + --k; + } + + if (l == 3) { + ++k; + } + + if (l == 4) { + --i; + } + + if (l == 5) { + ++i; + } + + if (this.getType(i, j, k) == Blocks.FIRE) { + this.a(entityhuman, 1004, i, j, k, 0); + this.setAir(i, j, k); + return true; + } else { + return false; + } + } + + public TileEntity getTileEntity(int i, int j, int k) { + if (j >= 0 && j < 256) { + TileEntity tileentity = null; + int l; + TileEntity tileentity1; + + if (this.M) { + for (l = 0; l < this.a.size(); ++l) { + tileentity1 = (TileEntity) this.a.get(l); + if (!tileentity1.r() && tileentity1.x == i && tileentity1.y == j && tileentity1.z == k) { + tileentity = tileentity1; + break; + } + } + } + + if (tileentity == null) { + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + + if (chunk != null) { + tileentity = chunk.e(i & 15, j, k & 15); + } + } + + if (tileentity == null) { + for (l = 0; l < this.a.size(); ++l) { + tileentity1 = (TileEntity) this.a.get(l); + if (!tileentity1.r() && tileentity1.x == i && tileentity1.y == j && tileentity1.z == k) { + tileentity = tileentity1; + break; + } + } + } + + return tileentity; + } else { + return null; + } + } + + public void setTileEntity(int i, int j, int k, TileEntity tileentity) { + if (tileentity != null && !tileentity.r()) { + if (this.M) { + tileentity.x = i; + tileentity.y = j; + tileentity.z = k; + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity1 = (TileEntity) iterator.next(); + + if (tileentity1.x == i && tileentity1.y == j && tileentity1.z == k) { + tileentity1.s(); + iterator.remove(); + } + } + + tileentity.a(this); // Spigot - No null worlds + this.a.add(tileentity); + } else { + this.tileEntityList.add(tileentity); + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + + if (chunk != null) { + chunk.a(i & 15, j, k & 15, tileentity); + } + } + } + } + + public void p(int i, int j, int k) { + TileEntity tileentity = this.getTileEntity(i, j, k); + + if (tileentity != null && this.M) { + tileentity.s(); + this.a.remove(tileentity); + } else { + if (tileentity != null) { + this.a.remove(tileentity); + this.tileEntityList.remove(tileentity); + } + + Chunk chunk = this.getChunkAt(i >> 4, k >> 4); + + if (chunk != null) { + chunk.f(i & 15, j, k & 15); + } + } + } + + public void a(TileEntity tileentity) { + this.b.add(tileentity); + } + + public boolean q(int i, int j, int k) { + AxisAlignedBB axisalignedbb = this.getType(i, j, k).a(this, i, j, k); + + return axisalignedbb != null && axisalignedbb.a() >= 1.0D; + } + + public static boolean a(IBlockAccess iblockaccess, int i, int j, int k) { + Block block = iblockaccess.getType(i, j, k); + int l = iblockaccess.getData(i, j, k); + + return block.getMaterial().k() && block.d() ? true : (block instanceof BlockStairs ? (l & 4) == 4 : (block instanceof BlockStepAbstract ? (l & 8) == 8 : (block instanceof BlockHopper ? true : (block instanceof BlockSnow ? (l & 7) == 7 : false)))); + } + + public boolean c(int i, int j, int k, boolean flag) { + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + Chunk chunk = this.chunkProvider.getOrCreateChunk(i >> 4, k >> 4); + + if (chunk != null && !chunk.isEmpty()) { + Block block = this.getType(i, j, k); + + return block.getMaterial().k() && block.d(); + } else { + return flag; + } + } else { + return flag; + } + } + + public void B() { + int i = this.a(1.0F); + + if (i != this.j) { + this.j = i; + } + } + + public void setSpawnFlags(boolean flag, boolean flag1) { + this.allowMonsters = flag; + this.allowAnimals = flag1; + } + + public void doTick() { + this.o(); + } + + private void a() { + if (this.worldData.hasStorm()) { + this.n = 1.0F; + if (this.worldData.isThundering()) { + this.p = 1.0F; + } + } + } + + protected void o() { + if (!this.worldProvider.g) { + if (!this.isStatic) { + int i = this.worldData.getThunderDuration(); + + if (i <= 0) { + if (this.worldData.isThundering()) { + this.worldData.setThunderDuration(this.random.nextInt(12000) + 3600); + } else { + this.worldData.setThunderDuration(this.random.nextInt(168000) + 12000); + } + } else { + --i; + this.worldData.setThunderDuration(i); + if (i <= 0) { + // CraftBukkit start + ThunderChangeEvent thunder = new ThunderChangeEvent(this.getWorld(), !this.worldData.isThundering()); + this.getServer().getPluginManager().callEvent(thunder); + if (!thunder.isCancelled()) { + this.worldData.setThundering(!this.worldData.isThundering()); + } + // CraftBukkit end + } + } + + this.o = this.p; + if (this.worldData.isThundering()) { + this.p = (float) ((double) this.p + 0.01D); + } else { + this.p = (float) ((double) this.p - 0.01D); + } + + this.p = MathHelper.a(this.p, 0.0F, 1.0F); + int j = this.worldData.getWeatherDuration(); + + if (j <= 0) { + if (this.worldData.hasStorm()) { + this.worldData.setWeatherDuration(this.random.nextInt(12000) + 12000); + } else { + this.worldData.setWeatherDuration(this.random.nextInt(168000) + 12000); + } + } else { + --j; + this.worldData.setWeatherDuration(j); + if (j <= 0) { + // CraftBukkit start + WeatherChangeEvent weather = new WeatherChangeEvent(this.getWorld(), !this.worldData.hasStorm()); + this.getServer().getPluginManager().callEvent(weather); + + if (!weather.isCancelled()) { + this.worldData.setStorm(!this.worldData.hasStorm()); + } + // CraftBukkit end + } + } + + this.m = this.n; + if (this.worldData.hasStorm()) { + this.n = (float) ((double) this.n + 0.01D); + } else { + this.n = (float) ((double) this.n - 0.01D); + } + + this.n = MathHelper.a(this.n, 0.0F, 1.0F); + } + } + } + + protected void C() { + // this.chunkTickList.clear(); // CraftBukkit - removed + this.methodProfiler.a("buildList"); + + int i; + EntityHuman entityhuman; + int j; + int k; + int l; + + // Spigot start + int optimalChunks = spigotConfig.chunksPerTick; + // Quick conditions to allow us to exist early + if (optimalChunks <= 0 || players.isEmpty()) { + return; + } + this.timings.doTickTiles_buildList.startTiming(); // Poweruser + // Keep chunks with growth inside of the optimal chunk range + int chunksPerPlayer = Math.min(200, Math.max(1, (int) (((optimalChunks - players.size()) / (double) players.size()) + 0.5))); + int randRange = 3 + chunksPerPlayer / 30; + // Limit to normal tick radius - including view distance + randRange = (randRange > chunkTickRadius) ? chunkTickRadius : randRange; + // odds of growth happening vs growth happening in vanilla + this.growthOdds = this.modifiedOdds = Math.max(35, Math.min(100, ((chunksPerPlayer + 1) * 100F) / 15F)); + // Spigot end + + Chunk chunkObj = null; // Poweruser + + for (i = 0; i < this.players.size(); ++i) { + entityhuman = (EntityHuman) this.players.get(i); + j = MathHelper.floor(entityhuman.locX / 16.0D); + k = MathHelper.floor(entityhuman.locZ / 16.0D); + l = this.p(); + + // Spigot start - Always update the chunk the player is on + long key = chunkToKey(j, k); + int existingPlayers = Math.max(0, chunkTickList.get(key)); // filter out -1 + chunkTickList.put(key, (short) (existingPlayers + 1)); + + // Check and see if we update the chunks surrounding the player this tick + for (int chunk = 0; chunk < chunksPerPlayer; chunk++) { + int dx = (random.nextBoolean() ? 1 : -1) * random.nextInt(randRange); + int dz = (random.nextBoolean() ? 1 : -1) * random.nextInt(randRange); + long hash = chunkToKey(dx + j, dz + k); + if (!chunkTickList.contains(hash) && ((chunkObj = this.getChunkIfLoaded(dx + j, dz + k)) != null) && chunkObj.areNeighborsLoaded(1)) // Poweruser + { + chunkTickList.put(hash, (short) -1); // no players + } + } + // Spigot End + } + + this.methodProfiler.b(); + if (this.K > 0) { + --this.K; + } + + this.methodProfiler.a("playerCheckLight"); + if (spigotConfig.randomLightUpdates && !this.players.isEmpty()) { // Spigot + i = this.random.nextInt(this.players.size()); + entityhuman = (EntityHuman) this.players.get(i); + j = MathHelper.floor(entityhuman.locX) + this.random.nextInt(11) - 5; + k = MathHelper.floor(entityhuman.locY) + this.random.nextInt(11) - 5; + l = MathHelper.floor(entityhuman.locZ) + this.random.nextInt(11) - 5; + this.t(j, k, l); + } + + this.methodProfiler.b(); + this.timings.doTickTiles_buildList.stopTiming(); // Poweruser + } + + protected abstract int p(); + + protected void a(int i, int j, Chunk chunk) { + this.methodProfiler.c("moodSound"); + if (this.K == 0 && !this.isStatic) { + this.k = this.k * 3 + 1013904223; + int k = this.k >> 2; + int l = k & 15; + int i1 = k >> 8 & 15; + int j1 = k >> 16 & 255; + Block block = chunk.getType(l, j1, i1); + + l += i; + i1 += j; + if (block.getMaterial() == Material.AIR && this.j(l, j1, i1) <= this.random.nextInt(8) && this.b(EnumSkyBlock.SKY, l, j1, i1) <= 0) { + EntityHuman entityhuman = this.findNearbyPlayer((double) l + 0.5D, (double) j1 + 0.5D, (double) i1 + 0.5D, 8.0D); + + if (entityhuman != null && entityhuman.e((double) l + 0.5D, (double) j1 + 0.5D, (double) i1 + 0.5D) > 4.0D) { + this.makeSound((double) l + 0.5D, (double) j1 + 0.5D, (double) i1 + 0.5D, "ambient.cave.cave", 0.7F, 0.8F + this.random.nextFloat() * 0.2F); + this.K = this.random.nextInt(12000) + 6000; + } + } + } + + this.methodProfiler.c("checkLight"); + chunk.o(); + } + + protected void g() { + this.C(); + } + + public boolean r(int i, int j, int k) { + return this.d(i, j, k, false); + } + + public boolean s(int i, int j, int k) { + return this.d(i, j, k, true); + } + + public boolean d(int i, int j, int k, boolean flag) { + BiomeBase biomebase = this.getBiome(i, k); + float f = biomebase.a(i, j, k); + + if (f > 0.15F) { + return false; + } else { + if (j >= 0 && j < 256 && this.b(EnumSkyBlock.BLOCK, i, j, k) < 10) { + Block block = this.getType(i, j, k); + + if ((block == Blocks.STATIONARY_WATER || block == Blocks.WATER) && this.getData(i, j, k) == 0) { + if (!flag) { + return true; + } + + boolean flag1 = true; + + if (flag1 && this.getType(i - 1, j, k).getMaterial() != Material.WATER) { + flag1 = false; + } + + if (flag1 && this.getType(i + 1, j, k).getMaterial() != Material.WATER) { + flag1 = false; + } + + if (flag1 && this.getType(i, j, k - 1).getMaterial() != Material.WATER) { + flag1 = false; + } + + if (flag1 && this.getType(i, j, k + 1).getMaterial() != Material.WATER) { + flag1 = false; + } + + if (!flag1) { + return true; + } + } + } + + return false; + } + } + + public boolean e(int i, int j, int k, boolean flag) { + BiomeBase biomebase = this.getBiome(i, k); + float f = biomebase.a(i, j, k); + + if (f > 0.15F) { + return false; + } else if (!flag) { + return true; + } else { + if (j >= 0 && j < 256 && this.b(EnumSkyBlock.BLOCK, i, j, k) < 10) { + Block block = this.getType(i, j, k); + + if (block.getMaterial() == Material.AIR && Blocks.SNOW.canPlace(this, i, j, k)) { + return true; + } + } + + return false; + } + } + + public boolean t(int i, int j, int k) { + boolean flag = false; + + if (!this.worldProvider.g) { + flag |= this.updateLight(EnumSkyBlock.SKY, i, j, k); // PaperSpigot - Asynchronous lighting updates + } + + flag |= this.updateLight(EnumSkyBlock.BLOCK, i, j, k); // PaperSpigot - Asynchronous lighting updates + return flag; + } + + private int a(int i, int j, int k, EnumSkyBlock enumskyblock) { + if (enumskyblock == EnumSkyBlock.SKY && this.i(i, j, k)) { + return 15; + } else { + Block block = this.getType(i, j, k); + int l = enumskyblock == EnumSkyBlock.SKY ? 0 : block.m(); + int i1 = block.k(); + + if (i1 >= 15 && block.m() > 0) { + i1 = 1; + } + + if (i1 < 1) { + i1 = 1; + } + + if (i1 >= 15) { + return 0; + } else if (l >= 14) { + return l; + } else { + for (int j1 = 0; j1 < 6; ++j1) { + int k1 = i + Facing.b[j1]; + int l1 = j + Facing.c[j1]; + int i2 = k + Facing.d[j1]; + int j2 = this.b(enumskyblock, k1, l1, i2) - i1; + + if (j2 > l) { + l = j2; + } + + if (l >= 14) { + return l; + } + } + + return l; + } + } + } + + public boolean c(EnumSkyBlock enumskyblock, int i, int j, int k, Chunk chunk, List neighbors) { // PaperSpigot + // CraftBukkit start - Use neighbor cache instead of looking up + //Chunk chunk = this.getChunkIfLoaded(i >> 4, k >> 4); + if (chunk == null /*|| !chunk.areNeighborsLoaded(1)*/ /* !this.areChunksLoaded(i, j, k, 17)*/) { + // CraftBukkit end + return false; + } else { + int l = 0; + int i1 = 0; + + this.methodProfiler.a("getBrightness"); + int j1 = this.b(enumskyblock, i, j, k); + int k1 = this.a(i, j, k, enumskyblock); + int l1; + int i2; + int j2; + int k2; + int l2; + int i3; + int j3; + int k3; + int l3; + + if (k1 > j1) { + this.I[i1++] = 133152; + } else if (k1 < j1) { + this.I[i1++] = 133152 | j1 << 18; + + while (l < i1) { + l1 = this.I[l++]; + i2 = (l1 & 63) - 32 + i; + j2 = (l1 >> 6 & 63) - 32 + j; + k2 = (l1 >> 12 & 63) - 32 + k; + l2 = l1 >> 18 & 15; + i3 = this.b(enumskyblock, i2, j2, k2); + if (i3 == l2) { + this.b(enumskyblock, i2, j2, k2, 0); + if (l2 > 0) { + j3 = MathHelper.a(i2 - i); + l3 = MathHelper.a(j2 - j); + k3 = MathHelper.a(k2 - k); + if (j3 + l3 + k3 < 17) { + for (int i4 = 0; i4 < 6; ++i4) { + int j4 = i2 + Facing.b[i4]; + int k4 = j2 + Facing.c[i4]; + int l4 = k2 + Facing.d[i4]; + int i5 = Math.max(1, this.getType(j4, k4, l4).k()); + + i3 = this.b(enumskyblock, j4, k4, l4); + if (i3 == l2 - i5 && i1 < this.I.length) { + this.I[i1++] = j4 - i + 32 | k4 - j + 32 << 6 | l4 - k + 32 << 12 | l2 - i5 << 18; + } + } + } + } + } + } + + l = 0; + } + + this.methodProfiler.b(); + this.methodProfiler.a("checkedPosition < toCheckCount"); + + while (l < i1) { + l1 = this.I[l++]; + i2 = (l1 & 63) - 32 + i; + j2 = (l1 >> 6 & 63) - 32 + j; + k2 = (l1 >> 12 & 63) - 32 + k; + l2 = this.b(enumskyblock, i2, j2, k2); + i3 = this.a(i2, j2, k2, enumskyblock); + if (i3 != l2) { + this.b(enumskyblock, i2, j2, k2, i3); + if (i3 > l2) { + j3 = Math.abs(i2 - i); + l3 = Math.abs(j2 - j); + k3 = Math.abs(k2 - k); + boolean flag = i1 < this.I.length - 6; + + if (j3 + l3 + k3 < 17 && flag) { + if (this.b(enumskyblock, i2 - 1, j2, k2) < i3) { + this.I[i1++] = i2 - 1 - i + 32 + (j2 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2 + 1, j2, k2) < i3) { + this.I[i1++] = i2 + 1 - i + 32 + (j2 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2 - 1, k2) < i3) { + this.I[i1++] = i2 - i + 32 + (j2 - 1 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2 + 1, k2) < i3) { + this.I[i1++] = i2 - i + 32 + (j2 + 1 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2, k2 - 1) < i3) { + this.I[i1++] = i2 - i + 32 + (j2 - j + 32 << 6) + (k2 - 1 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2, k2 + 1) < i3) { + this.I[i1++] = i2 - i + 32 + (j2 - j + 32 << 6) + (k2 + 1 - k + 32 << 12); + } + } + } + } + } + + // PaperSpigot start - Asynchronous light updates + if (chunk.world.paperSpigotConfig.useAsyncLighting) { + chunk.pendingLightUpdates.decrementAndGet(); + if (neighbors != null) { + for (Chunk neighbor : neighbors) { + neighbor.pendingLightUpdates.decrementAndGet(); + } + } + } + // PaperSpigot end + this.methodProfiler.b(); + return true; + } + } + + // PaperSpigot start - Asynchronous lighting updates + public boolean updateLight(final EnumSkyBlock enumskyblock, final int x, final int y, final int z) { + final Chunk chunk = this.getChunkIfLoaded(x >> 4, z >> 4); + if (chunk == null || !chunk.areNeighborsLoaded(2)) { // Poweruser - radius 2 + return false; + } + + if (!chunk.world.paperSpigotConfig.useAsyncLighting) { + return this.c(enumskyblock, x, y, z, chunk, null); + } + + chunk.pendingLightUpdates.incrementAndGet(); + chunk.lightUpdateTime = chunk.world.getTime(); + + final List neighbors = new ArrayList(); + // Poweruser start + int chunkx = chunk.locX; + int chunkz = chunk.locZ; + int radius = 2; + for (int cx = chunkx - radius; cx <= chunkx + radius; ++cx) { + for (int cz = chunkz - radius; cz <= chunkz + radius; ++cz) { + if (cx != chunkx || cz != chunkz) { + // Poweruser end + Chunk neighbor = this.getChunkIfLoaded(cx, cz); + if (neighbor != null) { + neighbor.pendingLightUpdates.incrementAndGet(); + neighbor.lightUpdateTime = chunk.world.getTime(); + neighbors.add(neighbor); + } + } + } + } + + if (!Bukkit.isPrimaryThread()) { + return this.c(enumskyblock, x, y, z, chunk, neighbors); + } + + // Poweruser start + this.lightingQueue.queueTask(new Runnable() { + @Override + public void run() { + try { + World.this.lightingUpdater.c(enumskyblock, x, y, z, chunk, neighbors); + } catch (Exception e) { + MinecraftServer.getLogger().error("Thread " + Thread.currentThread().getName() + " encountered an exception: " + e.getMessage(), e); + } + } + }); + // Poweruser end + return true; + } + // PaperSpigot end + + public boolean a(boolean flag) { + return false; + } + + public List a(Chunk chunk, boolean flag) { + return null; + } + + public List getEntities(Entity entity, AxisAlignedBB axisalignedbb) { + return this.getEntities(entity, axisalignedbb, (IEntitySelector) null); + } + + public List getEntities(Entity entity, AxisAlignedBB axisalignedbb, IEntitySelector ientityselector) { + ArrayList arraylist = new ArrayList(); + int i = MathHelper.floor((axisalignedbb.a - 2.0D) / 16.0D); + int j = MathHelper.floor((axisalignedbb.d + 2.0D) / 16.0D); + int k = MathHelper.floor((axisalignedbb.c - 2.0D) / 16.0D); + int l = MathHelper.floor((axisalignedbb.f + 2.0D) / 16.0D); + + for (int i1 = i; i1 <= j; ++i1) { + for (int j1 = k; j1 <= l; ++j1) { + if (this.isChunkLoaded(i1, j1)) { + this.getChunkAt(i1, j1).a(entity, axisalignedbb, arraylist, ientityselector); + } + } + } + + return arraylist; + } + + public List a(Class oclass, AxisAlignedBB axisalignedbb) { + return this.a(oclass, axisalignedbb, (IEntitySelector) null); + } + + public List a(Class oclass, AxisAlignedBB axisalignedbb, IEntitySelector ientityselector) { + int i = MathHelper.floor((axisalignedbb.a - 2.0D) / 16.0D); + int j = MathHelper.floor((axisalignedbb.d + 2.0D) / 16.0D); + int k = MathHelper.floor((axisalignedbb.c - 2.0D) / 16.0D); + int l = MathHelper.floor((axisalignedbb.f + 2.0D) / 16.0D); + ArrayList arraylist = new ArrayList(); + + for (int i1 = i; i1 <= j; ++i1) { + for (int j1 = k; j1 <= l; ++j1) { + if (this.isChunkLoaded(i1, j1)) { + this.getChunkAt(i1, j1).a(oclass, axisalignedbb, arraylist, ientityselector); + } + } + } + + return arraylist; + } + + // Guardian start + public boolean containsLiquidOrClimbable(AxisAlignedBB axisalignedbb) { + try { + int i = MathHelper.floor(axisalignedbb.a); + int j = MathHelper.floor(axisalignedbb.d + 1.0D); + int k = MathHelper.floor(axisalignedbb.b); + int l = MathHelper.floor(axisalignedbb.e + 1.0D); + int i1 = MathHelper.floor(axisalignedbb.c); + int j1 = MathHelper.floor(axisalignedbb.f + 1.0D); + if (axisalignedbb.a < 0.0D) { + i--; + } + if (axisalignedbb.b < 0.0D) { + k--; + } + if (axisalignedbb.c < 0.0D) { + i1--; + } + Chunk chunk = null; + for (int k1 = i; k1 < j; k1++) { + for (int i2 = i1; i2 < j1; i2++) { + int chunkX = k1 >> 4; + int chunkZ = i2 >> 4; + if (chunk == null) { + chunk = getChunkAt(chunkX, chunkZ); + } else if ((chunkX != chunk.locX) || (chunkZ != chunk.locZ)) { + chunk = getChunkAt(chunkX, chunkZ); + } + for (int l1 = k; l1 < l; l1++) { + Block block = chunk.getType(k1 & 0xF, l1, i2 & 0xF); + if ((block.getMaterial().isLiquid()) || (block == Blocks.LADDER) || (block == Blocks.VINE)) { + return true; + } + } + } + } + + return false; + } catch (Exception e) { + return false; + } + } + // Guardian end + + public Entity a(Class oclass, AxisAlignedBB axisalignedbb, Entity entity) { + List list = this.a(oclass, axisalignedbb); + Entity entity1 = null; + double d0 = Double.MAX_VALUE; + + for (int i = 0; i < list.size(); ++i) { + Entity entity2 = (Entity) list.get(i); + + if (entity2 != entity) { + double d1 = entity.f(entity2); + + if (d1 <= d0) { + entity1 = entity2; + d0 = d1; + } + } + } + + return entity1; + } + + public abstract Entity getEntity(int i); + + public void b(int i, int j, int k, TileEntity tileentity) { + if (this.isLoaded(i, j, k)) { + this.getChunkAtWorldCoords(i, k).e(); + } + } + + public int a(Class oclass) { + int i = 0; + + for (int j = 0; j < this.entityList.size(); ++j) { + Entity entity = (Entity) this.entityList.get(j); + + // CraftBukkit start - Split out persistent check, don't apply it to special persistent mobs + if (entity instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) { + continue; + } + } + + if (oclass.isAssignableFrom(entity.getClass())) { + // if ((!(entity instanceof EntityInsentient) || !((EntityInsentient) entity).isPersistent()) && oclass.isAssignableFrom(entity.getClass())) { + // CraftBukkit end + ++i; + } + } + + return i; + } + + public void a(List list) { + org.spigotmc.AsyncCatcher.catchOp("entity world add"); // Spigot + // CraftBukkit start + // this.entityList.addAll(list); + Entity entity = null; + + for (int i = 0; i < list.size(); ++i) { + entity = (Entity) list.get(i); + if (entity == null) { + continue; + } + this.entityList.add(entity); + // CraftBukkit end + this.a((Entity) list.get(i)); + } + } + + public void b(List list) { + this.f.addAll(list); + } + + public boolean mayPlace(Block block, int i, int j, int k, boolean flag, int l, Entity entity, ItemStack itemstack) { + Block block1 = this.getType(i, j, k); + AxisAlignedBB axisalignedbb = flag ? null : block.a(this, i, j, k); + + // CraftBukkit start - store default return + boolean defaultReturn = axisalignedbb != null && !this.a(axisalignedbb, entity) ? false : (block1.getMaterial() == Material.ORIENTABLE && block == Blocks.ANVIL ? true : block1.getMaterial().isReplaceable() && block.canPlace(this, i, j, k, l, itemstack)); + + // CraftBukkit start + BlockCanBuildEvent event = new BlockCanBuildEvent(this.getWorld().getBlockAt(i, j, k), CraftMagicNumbers.getId(block), defaultReturn); + this.getServer().getPluginManager().callEvent(event); + + return event.isBuildable(); + // CraftBukkit end + } + + public PathEntity findPath(Entity entity, Entity entity1, float f, boolean flag, boolean flag1, boolean flag2, boolean flag3) { + this.methodProfiler.a("pathfind"); + int i = MathHelper.floor(entity.locX); + int j = MathHelper.floor(entity.locY + 1.0D); + int k = MathHelper.floor(entity.locZ); + int l = (int) (f + 16.0F); + int i1 = i - l; + int j1 = j - l; + int k1 = k - l; + int l1 = i + l; + int i2 = j + l; + int j2 = k + l; + WeakChunkCache chunkcache = new WeakChunkCache(this, i1, j1, k1, l1, i2, j2, 0); // Poweruser + PathEntity pathentity = (new Pathfinder(chunkcache, flag, flag1, flag2, flag3)).a(entity, entity1, f); + + this.methodProfiler.b(); + return pathentity; + } + + public PathEntity a(Entity entity, int i, int j, int k, float f, boolean flag, boolean flag1, boolean flag2, boolean flag3) { + this.methodProfiler.a("pathfind"); + int l = MathHelper.floor(entity.locX); + int i1 = MathHelper.floor(entity.locY); + int j1 = MathHelper.floor(entity.locZ); + int k1 = (int) (f + 8.0F); + int l1 = l - k1; + int i2 = i1 - k1; + int j2 = j1 - k1; + int k2 = l + k1; + int l2 = i1 + k1; + int i3 = j1 + k1; + WeakChunkCache chunkcache = new WeakChunkCache(this, l1, i2, j2, k2, l2, i3, 0); // Poweruser + PathEntity pathentity = (new Pathfinder(chunkcache, flag, flag1, flag2, flag3)).a(entity, i, j, k, f); + + this.methodProfiler.b(); + return pathentity; + } + + public int getBlockPower(int i, int j, int k, int l) { + return this.getType(i, j, k).c(this, i, j, k, l); + } + + public int getBlockPower(int i, int j, int k) { + byte b0 = 0; + int l = Math.max(b0, this.getBlockPower(i, j - 1, k, 0)); + + if (l >= 15) { + return l; + } else { + l = Math.max(l, this.getBlockPower(i, j + 1, k, 1)); + if (l >= 15) { + return l; + } else { + l = Math.max(l, this.getBlockPower(i, j, k - 1, 2)); + if (l >= 15) { + return l; + } else { + l = Math.max(l, this.getBlockPower(i, j, k + 1, 3)); + if (l >= 15) { + return l; + } else { + l = Math.max(l, this.getBlockPower(i - 1, j, k, 4)); + if (l >= 15) { + return l; + } else { + l = Math.max(l, this.getBlockPower(i + 1, j, k, 5)); + return l >= 15 ? l : l; + } + } + } + } + } + } + + public boolean isBlockFacePowered(int i, int j, int k, int l) { + return this.getBlockFacePower(i, j, k, l) > 0; + } + + public int getBlockFacePower(int i, int j, int k, int l) { + return this.getType(i, j, k).r() ? this.getBlockPower(i, j, k) : this.getType(i, j, k).b(this, i, j, k, l); + } + + public boolean isBlockIndirectlyPowered(int i, int j, int k) { + return this.getBlockFacePower(i, j - 1, k, 0) > 0 ? true : (this.getBlockFacePower(i, j + 1, k, 1) > 0 ? true : (this.getBlockFacePower(i, j, k - 1, 2) > 0 ? true : (this.getBlockFacePower(i, j, k + 1, 3) > 0 ? true : (this.getBlockFacePower(i - 1, j, k, 4) > 0 ? true : this.getBlockFacePower(i + 1, j, k, 5) > 0)))); + } + + public int getHighestNeighborSignal(int i, int j, int k) { + int l = 0; + + for (int i1 = 0; i1 < 6; ++i1) { + int j1 = this.getBlockFacePower(i + Facing.b[i1], j + Facing.c[i1], k + Facing.d[i1], i1); + + if (j1 >= 15) { + return 15; + } + + if (j1 > l) { + l = j1; + } + } + + return l; + } + + public EntityHuman findNearbyPlayer(Entity entity, double d0) { + return this.findNearbyPlayer(entity.locX, entity.locY, entity.locZ, d0); + } + + public EntityHuman findNearbyPlayer(double d0, double d1, double d2, double d3) { + // MineHQ start + if (0 <= d3 && d3 <= 64) { + return this.playerMap.getNearestPlayer(d0, d1, d2, d3); + } + // MineHQ end + double d4 = -1.0D; + EntityHuman entityhuman = null; + + for (Object o : a(EntityHuman.class, AxisAlignedBB.a(d0 - d3, d1 - d3, d2 - d3, d0 + d3, d1 + d3, d2 + d3))) { + EntityHuman entityhuman1 = (EntityHuman) o; + // CraftBukkit start - Fixed an NPE + if (entityhuman1 == null || entityhuman1.dead) { + continue; + } + // CraftBukkit end + double d5 = entityhuman1.e(d0, d1, d2); + + if ((d3 < 0.0D || d5 < d3 * d3) && (d4 == -1.0D || d5 < d4)) { + d4 = d5; + entityhuman = entityhuman1; + } + } + + return entityhuman; + } + + public EntityHuman findNearbyVulnerablePlayer(Entity entity, double d0) { + return this.findNearbyVulnerablePlayer(entity.locX, entity.locY, entity.locZ, d0); + } + + // MineHQ start + private static final Function invisibilityFunction = new Function() { + @Override + public Double apply(EntityHuman entityHuman) { + + if (entityHuman.isInvisible()) { + float f = entityHuman.bE(); + + if (f < 0.1F) { + f = 0.1F; + } + + return (double) (0.7F * f); + } + + return null; + } + }; + // MineHQ end + + public EntityHuman findNearbyVulnerablePlayer(double d0, double d1, double d2, double d3) { + // MineHQ start + if (0 <= d3 && d3 <= 64.0D) { + return this.playerMap.getNearestAttackablePlayer(d0, d1, d2, d3, d3, invisibilityFunction); + } + // MineHQ end + double d4 = -1.0D; + EntityHuman entityhuman = null; + + for (Object o : a(EntityHuman.class, AxisAlignedBB.a(d0 - d3, d1 - d3, d2 - d3, d0 + d3, d1 + d3, d2 + d3))) { + EntityHuman entityhuman1 = (EntityHuman) o; + // CraftBukkit start - Fixed an NPE + if (entityhuman1 == null || entityhuman1.dead) { + continue; + } + // CraftBukkit end + + if (!entityhuman1.abilities.isInvulnerable && entityhuman1.isAlive()) { + double d5 = entityhuman1.e(d0, d1, d2); + double d6 = d3; + + if (entityhuman1.isSneaking()) { + d6 = d3 * 0.800000011920929D; + } + + if (entityhuman1.isInvisible()) { + float f = entityhuman1.bE(); + + if (f < 0.1F) { + f = 0.1F; + } + + d6 *= (double) (0.7F * f); + } + + if ((d3 < 0.0D || d5 < d6 * d6) && (d4 == -1.0D || d5 < d4)) { + d4 = d5; + entityhuman = entityhuman1; + } + } + } + + return entityhuman; + } + + // PaperSpigot start - Find players with the spawning flag + public EntityHuman findNearbyPlayerWhoAffectsSpawning(Entity entity, double radius) { + return this.findNearbyPlayerWhoAffectsSpawning(entity.locX, entity.locY, entity.locZ, radius); + } + + public EntityHuman findNearbyPlayerWhoAffectsSpawning(double x, double y, double z, double radius) { + // MineHQ start + if (0 <= radius && radius <= 64.0) { + return this.playerMap.getNearbyPlayer(x, y, z, radius, true); + } + // MineHQ end + double nearestRadius = -1.0D; + EntityHuman entityHuman = null; + + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman nearestPlayer = (EntityHuman) this.players.get(i); + + if (nearestPlayer == null || nearestPlayer.dead || !nearestPlayer.affectsSpawning) { + continue; + } + + double distance = nearestPlayer.e(x, y, z); + + if ((radius < 0.0D || distance < radius * radius) && (nearestRadius == -1.0D || distance < nearestRadius)) { + nearestRadius = distance; + entityHuman = nearestPlayer; + } + } + + return entityHuman; + } + // PaperSpigot end + + public EntityHuman a(String s) { + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman = (EntityHuman) this.players.get(i); + + if (s.equals(entityhuman.getName())) { + return entityhuman; + } + } + + return null; + } + + public EntityHuman a(UUID uuid) { + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman = (EntityHuman) this.players.get(i); + + if (uuid.equals(entityhuman.getUniqueID())) { + return entityhuman; + } + } + + return null; + } + + public void G() throws ExceptionWorldConflict { // CraftBukkit - added throws + this.dataManager.checkSession(); + } + + public long getSeed() { + return this.worldData.getSeed(); + } + + public long getTime() { + return this.worldData.getTime(); + } + + public long getDayTime() { + return this.worldData.getDayTime(); + } + + public void setDayTime(long i) { + this.worldData.setDayTime(i); + } + + public ChunkCoordinates getSpawn() { + return new ChunkCoordinates(this.worldData.c(), this.worldData.d(), this.worldData.e()); + } + + public void x(int i, int j, int k) { + this.worldData.setSpawn(i, j, k); + } + + // Poweruser start + public void setSpawn(int i, int j, int k, float yaw, float pitch) { + this.worldData.setSpawn(i, j, k, yaw, pitch); + } + // Poweruser end + + public boolean a(EntityHuman entityhuman, int i, int j, int k) { + return true; + } + + public void broadcastEntityEffect(Entity entity, byte b0) { + } + + public IChunkProvider L() { + return this.chunkProvider; + } + + public void playBlockAction(int i, int j, int k, Block block, int l, int i1) { + block.a(this, i, j, k, l, i1); + } + + public IDataManager getDataManager() { + return this.dataManager; + } + + public WorldData getWorldData() { + return this.worldData; + } + + public GameRules getGameRules() { + return this.worldData.getGameRules(); + } + + public void everyoneSleeping() { + } + + // CraftBukkit start + // Calls the method that checks to see if players are sleeping + // Called by CraftPlayer.setPermanentSleeping() + public void checkSleepStatus() { + if (!this.isStatic) { + this.everyoneSleeping(); + } + } + // CraftBukkit end + + public float h(float f) { + return (this.o + (this.p - this.o) * f) * this.j(f); + } + + public float j(float f) { + return this.m + (this.n - this.m) * f; + } + + public boolean P() { + return (double) this.h(1.0F) > 0.9D; + } + + public boolean Q() { + return (double) this.j(1.0F) > 0.2D; + } + + public boolean isRainingAt(int i, int j, int k) { + if (!this.Q()) { + return false; + } else if (!this.i(i, j, k)) { + return false; + } else if (this.h(i, k) > j) { + return false; + } else { + BiomeBase biomebase = this.getBiome(i, k); + + return biomebase.d() ? false : (this.e(i, j, k, false) ? false : biomebase.e()); + } + } + + public boolean z(int i, int j, int k) { + BiomeBase biomebase = this.getBiome(i, k); + + return biomebase.f(); + } + + public void a(String s, PersistentBase persistentbase) { + this.worldMaps.a(s, persistentbase); + } + + public PersistentBase a(Class oclass, String s) { + return this.worldMaps.get(oclass, s); + } + + public int b(String s) { + return this.worldMaps.a(s); + } + + public void b(int i, int j, int k, int l, int i1) { + for (int j1 = 0; j1 < this.u.size(); ++j1) { + ((IWorldAccess) this.u.get(j1)).a(i, j, k, l, i1); + } + } + + public void triggerEffect(int i, int j, int k, int l, int i1) { + this.a((EntityHuman) null, i, j, k, l, i1); + } + + public void a(EntityHuman entityhuman, int i, int j, int k, int l, int i1) { + try { + for (int j1 = 0; j1 < this.u.size(); ++j1) { + ((IWorldAccess) this.u.get(j1)).a(entityhuman, i, j, k, l, i1); + } + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Playing level event"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Level event being played"); + + crashreportsystemdetails.a("Block coordinates", CrashReportSystemDetails.a(j, k, l)); + crashreportsystemdetails.a("Event source", entityhuman); + crashreportsystemdetails.a("Event type", Integer.valueOf(i)); + crashreportsystemdetails.a("Event data", Integer.valueOf(i1)); + throw new ReportedException(crashreport); + } + } + + public int getHeight() { + return 256; + } + + public int S() { + return this.worldProvider.g ? 128 : 256; + } + + public Random A(int i, int j, int k) { + long l = (long) i * 341873128712L + (long) j * 132897987541L + this.getWorldData().getSeed() + (long) k; + + this.random.setSeed(l); + return this.random; + } + + public ChunkPosition b(String s, int i, int j, int k) { + return this.L().findNearestMapFeature(this, s, i, j, k); + } + + public CrashReportSystemDetails a(CrashReport crashreport) { + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Affected level", 1); + + crashreportsystemdetails.a("Level name", (this.worldData == null ? "????" : this.worldData.getName())); + crashreportsystemdetails.a("All players", (Callable) (new CrashReportPlayers(this))); + crashreportsystemdetails.a("Chunk stats", (Callable) (new CrashReportChunkStats(this))); + + try { + this.worldData.a(crashreportsystemdetails); + } catch (Throwable throwable) { + crashreportsystemdetails.a("Level Data Unobtainable", throwable); + } + + return crashreportsystemdetails; + } + + public void d(int i, int j, int k, int l, int i1) { + for (int j1 = 0; j1 < this.u.size(); ++j1) { + IWorldAccess iworldaccess = (IWorldAccess) this.u.get(j1); + + iworldaccess.b(i, j, k, l, i1); + } + } + + public Calendar V() { + if (this.getTime() % 600L == 0L) { + this.J.setTimeInMillis(MinecraftServer.ar()); + } + + return this.J; + } + + public Scoreboard getScoreboard() { + return this.scoreboard; + } + + public void updateAdjacentComparators(int i, int j, int k, Block block) { + for (int l = 0; l < 4; ++l) { + int i1 = i + Direction.a[l]; + int j1 = k + Direction.b[l]; + Block block1 = this.getType(i1, j, j1); + + if (Blocks.REDSTONE_COMPARATOR_OFF.e(block1)) { + block1.doPhysics(this, i1, j, j1, block); + } else if (block1.r()) { + i1 += Direction.a[l]; + j1 += Direction.b[l]; + Block block2 = this.getType(i1, j, j1); + + if (Blocks.REDSTONE_COMPARATOR_OFF.e(block2)) { + block2.doPhysics(this, i1, j, j1, block); + } + } + } + } + + public float b(double d0, double d1, double d2) { + return this.B(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2)); + } + + public float B(int i, int j, int k) { + float f = 0.0F; + boolean flag = this.difficulty == EnumDifficulty.HARD; + + if (this.isLoaded(i, j, k)) { + float f1 = this.y(); + + f += MathHelper.a((float) this.getChunkAtWorldCoords(i, k).s / 3600000.0F, 0.0F, 1.0F) * (flag ? 1.0F : 0.75F); + f += f1 * 0.25F; + } + + if (this.difficulty == EnumDifficulty.EASY || this.difficulty == EnumDifficulty.PEACEFUL) { + f *= (float) this.difficulty.a() / 2.0F; + } + + return MathHelper.a(f, 0.0F, flag ? 1.5F : 1.0F); + } + + public void X() { + Iterator iterator = this.u.iterator(); + + while (iterator.hasNext()) { + IWorldAccess iworldaccess = (IWorldAccess) iterator.next(); + + iworldaccess.b(); + } + } + + // MineHQ start - chunk unload queue slowness + public long obtainLock, pendingSavesPut, fileIOThreadAddition, writeStartNBT, writeSections, writeBiomes, writeEntities, writeTileEntities, writeTileTicks; + + public void printTimings() { + MinecraftServer.getLogger().warn("Obtain lock: " + obtainLock); + MinecraftServer.getLogger().warn("Pending saves put: " + pendingSavesPut); + MinecraftServer.getLogger().warn("File IO thread addition: " + fileIOThreadAddition); + MinecraftServer.getLogger().warn("Write start NBT: " + writeStartNBT); + MinecraftServer.getLogger().warn("Write sections: " + writeSections); + MinecraftServer.getLogger().warn("Write biomes: " + writeBiomes); + MinecraftServer.getLogger().warn("Write entities: " + writeEntities); + MinecraftServer.getLogger().warn("Write tile entities: " + writeTileEntities); + MinecraftServer.getLogger().warn("Write tile ticks: " + writeTileTicks); + + } + + public void clearTimings() { + this.obtainLock = 0; + this.pendingSavesPut = 0; + this.fileIOThreadAddition = 0; + this.writeStartNBT = 0; + this.writeSections = 0; + this.writeBiomes = 0; + this.writeEntities = 0; + this.writeTileEntities = 0; + this.writeTileTicks = 0; + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldChunkManager.java b/vspigot-server/src/main/java/net/minecraft/server/WorldChunkManager.java new file mode 100644 index 0000000..2e29bd0 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldChunkManager.java @@ -0,0 +1,195 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class WorldChunkManager { + + private GenLayer c; + private GenLayer d; + private BiomeCache e; + private List f; + + protected WorldChunkManager() { + this.e = new BiomeCache(this); + this.f = new ArrayList(); + this.f.add(BiomeBase.FOREST); + this.f.add(BiomeBase.PLAINS); + this.f.add(BiomeBase.TAIGA); + this.f.add(BiomeBase.TAIGA_HILLS); + this.f.add(BiomeBase.FOREST_HILLS); + this.f.add(BiomeBase.JUNGLE); + this.f.add(BiomeBase.JUNGLE_HILLS); + } + + public WorldChunkManager(long i, WorldType worldtype, World world) { // MineHQ - add world + this(); + GenLayer[] agenlayer = GenLayer.a(i, worldtype, world); // MineHQ - add world + + this.c = agenlayer[0]; + this.d = agenlayer[1]; + } + + public WorldChunkManager(World world) { + this(world.getSeed(), world.getWorldData().getType(), world); // MineHQ - add world + } + + public List a() { + return this.f; + } + + public BiomeBase getBiome(int i, int j) { + return this.e.b(i, j); + } + + public float[] getWetness(float[] afloat, int i, int j, int k, int l) { + IntCache.a(); + if (afloat == null || afloat.length < k * l) { + afloat = new float[k * l]; + } + + int[] aint = this.d.a(i, j, k, l); + + for (int i1 = 0; i1 < k * l; ++i1) { + try { + float f = (float) BiomeBase.getBiome(aint[i1]).h() / 65536.0F; + + if (f > 1.0F) { + f = 1.0F; + } + + afloat[i1] = f; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Invalid Biome id"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("DownfallBlock"); + + crashreportsystemdetails.a("biome id", Integer.valueOf(i1)); + crashreportsystemdetails.a("downfalls[] size", Integer.valueOf(afloat.length)); + crashreportsystemdetails.a("x", Integer.valueOf(i)); + crashreportsystemdetails.a("z", Integer.valueOf(j)); + crashreportsystemdetails.a("w", Integer.valueOf(k)); + crashreportsystemdetails.a("h", Integer.valueOf(l)); + throw new ReportedException(crashreport); + } + } + + return afloat; + } + + public BiomeBase[] getBiomes(BiomeBase[] abiomebase, int i, int j, int k, int l) { + IntCache.a(); + if (abiomebase == null || abiomebase.length < k * l) { + abiomebase = new BiomeBase[k * l]; + } + + int[] aint = this.c.a(i, j, k, l); + + try { + for (int i1 = 0; i1 < k * l; ++i1) { + abiomebase[i1] = BiomeBase.getBiome(aint[i1]); + } + + return abiomebase; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Invalid Biome id"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("RawBiomeBlock"); + + crashreportsystemdetails.a("biomes[] size", Integer.valueOf(abiomebase.length)); + crashreportsystemdetails.a("x", Integer.valueOf(i)); + crashreportsystemdetails.a("z", Integer.valueOf(j)); + crashreportsystemdetails.a("w", Integer.valueOf(k)); + crashreportsystemdetails.a("h", Integer.valueOf(l)); + throw new ReportedException(crashreport); + } + } + + public BiomeBase[] getBiomeBlock(BiomeBase[] abiomebase, int i, int j, int k, int l) { + return this.a(abiomebase, i, j, k, l, true); + } + + public BiomeBase[] a(BiomeBase[] abiomebase, int i, int j, int k, int l, boolean flag) { + IntCache.a(); + if (abiomebase == null || abiomebase.length < k * l) { + abiomebase = new BiomeBase[k * l]; + } + + if (flag && k == 16 && l == 16 && (i & 15) == 0 && (j & 15) == 0) { + BiomeBase[] abiomebase1 = this.e.d(i, j); + + System.arraycopy(abiomebase1, 0, abiomebase, 0, k * l); + return abiomebase; + } else { + int[] aint = this.d.a(i, j, k, l); + + for (int i1 = 0; i1 < k * l; ++i1) { + abiomebase[i1] = BiomeBase.getBiome(aint[i1]); + } + + return abiomebase; + } + } + + public boolean a(int i, int j, int k, List list) { + IntCache.a(); + int l = i - k >> 2; + int i1 = j - k >> 2; + int j1 = i + k >> 2; + int k1 = j + k >> 2; + int l1 = j1 - l + 1; + int i2 = k1 - i1 + 1; + int[] aint = this.c.a(l, i1, l1, i2); + + try { + for (int j2 = 0; j2 < l1 * i2; ++j2) { + BiomeBase biomebase = BiomeBase.getBiome(aint[j2]); + + if (!list.contains(biomebase)) { + return false; + } + } + + return true; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Invalid Biome id"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Layer"); + + crashreportsystemdetails.a("Layer", this.c.toString()); + crashreportsystemdetails.a("x", Integer.valueOf(i)); + crashreportsystemdetails.a("z", Integer.valueOf(j)); + crashreportsystemdetails.a("radius", Integer.valueOf(k)); + crashreportsystemdetails.a("allowed", list); + throw new ReportedException(crashreport); + } + } + + public ChunkPosition a(int i, int j, int k, List list, Random random) { + IntCache.a(); + int l = i - k >> 2; + int i1 = j - k >> 2; + int j1 = i + k >> 2; + int k1 = j + k >> 2; + int l1 = j1 - l + 1; + int i2 = k1 - i1 + 1; + int[] aint = this.c.a(l, i1, l1, i2); + ChunkPosition chunkposition = null; + int j2 = 0; + + for (int k2 = 0; k2 < l1 * i2; ++k2) { + int l2 = l + k2 % l1 << 2; + int i3 = i1 + k2 / l1 << 2; + BiomeBase biomebase = BiomeBase.getBiome(aint[k2]); + + if (list.contains(biomebase) && (chunkposition == null || random.nextInt(j2 + 1) == 0)) { + chunkposition = new ChunkPosition(l2, 0, i3); + ++j2; + } + } + + return chunkposition; + } + + public void b() { + this.e.a(); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldData.java b/vspigot-server/src/main/java/net/minecraft/server/WorldData.java new file mode 100644 index 0000000..76a73f3 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldData.java @@ -0,0 +1,449 @@ +package net.minecraft.server; + +import java.util.concurrent.Callable; + +public class WorldData { + + private long seed; + private WorldType type; + private String generatorOptions; + private int spawnX; + private int spawnY; + private int spawnZ; + // Poweruser start + private float spawnYaw; + private float spawnPitch; + // Poweruser end + private long time; + private long dayTime; + private long lastPlayed; + private long sizeOnDisk; + private NBTTagCompound playerData; + private int dimension; + private String name; + private int version; + private boolean isRaining; + private int rainTicks; + private boolean isThundering; + private int thunderTicks; + private EnumGamemode gameType; + private boolean useMapFeatures; + private boolean hardcore; + private boolean allowCommands; + private boolean initialized; + private GameRules gameRules; + + protected WorldData() { + this.type = WorldType.NORMAL; + this.generatorOptions = ""; + this.gameRules = new GameRules(); + } + + public WorldData(NBTTagCompound nbttagcompound) { + this.type = WorldType.NORMAL; + this.generatorOptions = ""; + this.gameRules = new GameRules(); + this.seed = nbttagcompound.getLong("RandomSeed"); + if (nbttagcompound.hasKeyOfType("generatorName", 8)) { + String s = nbttagcompound.getString("generatorName"); + + this.type = WorldType.getType(s); + if (this.type == null) { + this.type = WorldType.NORMAL; + } else if (this.type.f()) { + int i = 0; + + if (nbttagcompound.hasKeyOfType("generatorVersion", 99)) { + i = nbttagcompound.getInt("generatorVersion"); + } + + this.type = this.type.a(i); + } + + if (nbttagcompound.hasKeyOfType("generatorOptions", 8)) { + this.generatorOptions = nbttagcompound.getString("generatorOptions"); + } + } + + this.gameType = EnumGamemode.getById(nbttagcompound.getInt("GameType")); + if (nbttagcompound.hasKeyOfType("MapFeatures", 99)) { + this.useMapFeatures = nbttagcompound.getBoolean("MapFeatures"); + } else { + this.useMapFeatures = true; + } + + this.spawnX = nbttagcompound.getInt("SpawnX"); + this.spawnY = nbttagcompound.getInt("SpawnY"); + this.spawnZ = nbttagcompound.getInt("SpawnZ"); + // Poweruser start + this.spawnYaw = nbttagcompound.getFloat("SpawnYaw"); + this.spawnPitch = nbttagcompound.getFloat("SpawnPitch"); + // Poweruser end + this.time = nbttagcompound.getLong("Time"); + if (nbttagcompound.hasKeyOfType("DayTime", 99)) { + this.dayTime = nbttagcompound.getLong("DayTime"); + } else { + this.dayTime = this.time; + } + + this.lastPlayed = nbttagcompound.getLong("LastPlayed"); + this.sizeOnDisk = nbttagcompound.getLong("SizeOnDisk"); + this.name = nbttagcompound.getString("LevelName"); + this.version = nbttagcompound.getInt("version"); + this.rainTicks = nbttagcompound.getInt("rainTime"); + this.isRaining = nbttagcompound.getBoolean("raining"); + this.thunderTicks = nbttagcompound.getInt("thunderTime"); + this.isThundering = nbttagcompound.getBoolean("thundering"); + this.hardcore = nbttagcompound.getBoolean("hardcore"); + if (nbttagcompound.hasKeyOfType("initialized", 99)) { + this.initialized = nbttagcompound.getBoolean("initialized"); + } else { + this.initialized = true; + } + + if (nbttagcompound.hasKeyOfType("allowCommands", 99)) { + this.allowCommands = nbttagcompound.getBoolean("allowCommands"); + } else { + this.allowCommands = this.gameType == EnumGamemode.CREATIVE; + } + + if (nbttagcompound.hasKeyOfType("Player", 10)) { + this.playerData = nbttagcompound.getCompound("Player"); + this.dimension = this.playerData.getInt("Dimension"); + } + + if (nbttagcompound.hasKeyOfType("GameRules", 10)) { + this.gameRules.a(nbttagcompound.getCompound("GameRules")); + } + } + + public WorldData(WorldSettings worldsettings, String s) { + this.type = WorldType.NORMAL; + this.generatorOptions = ""; + this.gameRules = new GameRules(); + this.seed = worldsettings.d(); + this.gameType = worldsettings.e(); + this.useMapFeatures = worldsettings.g(); + this.name = s; + this.hardcore = worldsettings.f(); + this.type = worldsettings.h(); + this.generatorOptions = worldsettings.j(); + this.allowCommands = worldsettings.i(); + this.initialized = false; + } + + public WorldData(WorldData worlddata) { + this.type = WorldType.NORMAL; + this.generatorOptions = ""; + this.gameRules = new GameRules(); + this.seed = worlddata.seed; + this.type = worlddata.type; + this.generatorOptions = worlddata.generatorOptions; + this.gameType = worlddata.gameType; + this.useMapFeatures = worlddata.useMapFeatures; + this.spawnX = worlddata.spawnX; + this.spawnY = worlddata.spawnY; + this.spawnZ = worlddata.spawnZ; + // Poweruser start + this.spawnYaw = worlddata.spawnYaw; + this.spawnPitch = worlddata.spawnPitch; + // Poweruser end + this.time = worlddata.time; + this.dayTime = worlddata.dayTime; + this.lastPlayed = worlddata.lastPlayed; + this.sizeOnDisk = worlddata.sizeOnDisk; + this.playerData = worlddata.playerData; + this.dimension = worlddata.dimension; + this.name = worlddata.name; + this.version = worlddata.version; + this.rainTicks = worlddata.rainTicks; + this.isRaining = worlddata.isRaining; + this.thunderTicks = worlddata.thunderTicks; + this.isThundering = worlddata.isThundering; + this.hardcore = worlddata.hardcore; + this.allowCommands = worlddata.allowCommands; + this.initialized = worlddata.initialized; + this.gameRules = worlddata.gameRules; + } + + public NBTTagCompound a() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + this.a(nbttagcompound, this.playerData); + return nbttagcompound; + } + + public NBTTagCompound a(NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + this.a(nbttagcompound1, nbttagcompound); + return nbttagcompound1; + } + + private void a(NBTTagCompound nbttagcompound, NBTTagCompound nbttagcompound1) { + nbttagcompound.setLong("RandomSeed", this.seed); + nbttagcompound.setString("generatorName", this.type.name()); + nbttagcompound.setInt("generatorVersion", this.type.getVersion()); + nbttagcompound.setString("generatorOptions", this.generatorOptions); + nbttagcompound.setInt("GameType", this.gameType.getId()); + nbttagcompound.setBoolean("MapFeatures", this.useMapFeatures); + nbttagcompound.setInt("SpawnX", this.spawnX); + nbttagcompound.setInt("SpawnY", this.spawnY); + nbttagcompound.setInt("SpawnZ", this.spawnZ); + // Poweruser start + nbttagcompound.setFloat("SpawnYaw", this.spawnYaw); + nbttagcompound.setFloat("SpawnPitch", this.spawnPitch); + // Poweruser end + nbttagcompound.setLong("Time", this.time); + nbttagcompound.setLong("DayTime", this.dayTime); + nbttagcompound.setLong("SizeOnDisk", this.sizeOnDisk); + nbttagcompound.setLong("LastPlayed", MinecraftServer.ar()); + nbttagcompound.setString("LevelName", this.name); + nbttagcompound.setInt("version", this.version); + nbttagcompound.setInt("rainTime", this.rainTicks); + nbttagcompound.setBoolean("raining", this.isRaining); + nbttagcompound.setInt("thunderTime", this.thunderTicks); + nbttagcompound.setBoolean("thundering", this.isThundering); + nbttagcompound.setBoolean("hardcore", this.hardcore); + nbttagcompound.setBoolean("allowCommands", this.allowCommands); + nbttagcompound.setBoolean("initialized", this.initialized); + nbttagcompound.set("GameRules", this.gameRules.a()); + if (nbttagcompound1 != null) { + nbttagcompound.set("Player", nbttagcompound1); + } + } + + public long getSeed() { + return this.seed; + } + + public int c() { + return this.spawnX; + } + + public int d() { + return this.spawnY; + } + + public int e() { + return this.spawnZ; + } + + public long getTime() { + return this.time; + } + + public long getDayTime() { + return this.dayTime; + } + + public NBTTagCompound i() { + return this.playerData; + } + + public int j() { + return this.dimension; + } + + public void setTime(long i) { + this.time = i; + } + + public void setDayTime(long i) { + this.dayTime = i; + } + + public void setSpawn(int i, int j, int k) { + this.spawnX = i; + this.spawnY = j; + this.spawnZ = k; + } + + // Poweruser start + public void setSpawn(int i, int j, int k, float yaw, float pitch) { + this.setSpawn(i, j, k); + this.spawnYaw = yaw; + this.spawnPitch = pitch; + } + + public float getSpawnYaw() { + return this.spawnYaw; + } + + public float getSpawnPitch() { + return this.spawnPitch; + } + // Poweruser end + + public String getName() { + return this.name; + } + + public void setName(String s) { + this.name = s; + } + + public int l() { + return this.version; + } + + public void e(int i) { + this.version = i; + } + + public boolean isThundering() { + return this.isThundering; + } + + public void setThundering(boolean flag) { + this.isThundering = flag; + } + + public int getThunderDuration() { + return this.thunderTicks; + } + + public void setThunderDuration(int i) { + this.thunderTicks = i; + } + + public boolean hasStorm() { + return this.isRaining; + } + + public void setStorm(boolean flag) { + this.isRaining = flag; + } + + public int getWeatherDuration() { + return this.rainTicks; + } + + public void setWeatherDuration(int i) { + this.rainTicks = i; + } + + public EnumGamemode getGameType() { + return this.gameType; + } + + public boolean shouldGenerateMapFeatures() { + return this.useMapFeatures; + } + + public void setGameType(EnumGamemode enumgamemode) { + this.gameType = enumgamemode; + } + + public boolean isHardcore() { + return this.hardcore; + } + + public WorldType getType() { + return this.type; + } + + public void setType(WorldType worldtype) { + this.type = worldtype; + } + + public String getGeneratorOptions() { + return this.generatorOptions; + } + + public boolean allowCommands() { + return this.allowCommands; + } + + public boolean isInitialized() { + return this.initialized; + } + + public void d(boolean flag) { + this.initialized = flag; + } + + public GameRules getGameRules() { + return this.gameRules; + } + + public void a(CrashReportSystemDetails crashreportsystemdetails) { + crashreportsystemdetails.a("Level seed", (Callable) (new CrashReportLevelSeed(this))); + crashreportsystemdetails.a("Level generator", (Callable) (new CrashReportLevelGenerator(this))); + crashreportsystemdetails.a("Level generator options", (Callable) (new CrashReportLevelGeneratorOptions(this))); + crashreportsystemdetails.a("Level spawn location", (Callable) (new CrashReportLevelSpawnLocation(this))); + crashreportsystemdetails.a("Level time", (Callable) (new CrashReportLevelTime(this))); + crashreportsystemdetails.a("Level dimension", (Callable) (new CrashReportLevelDimension(this))); + crashreportsystemdetails.a("Level storage version", (Callable) (new CrashReportLevelStorageVersion(this))); + crashreportsystemdetails.a("Level weather", (Callable) (new CrashReportLevelWeather(this))); + crashreportsystemdetails.a("Level game mode", (Callable) (new CrashReportLevelGameMode(this))); + } + + static WorldType a(WorldData worlddata) { + return worlddata.type; + } + + static boolean b(WorldData worlddata) { + return worlddata.useMapFeatures; + } + + static String c(WorldData worlddata) { + return worlddata.generatorOptions; + } + + static int d(WorldData worlddata) { + return worlddata.spawnX; + } + + static int e(WorldData worlddata) { + return worlddata.spawnY; + } + + static int f(WorldData worlddata) { + return worlddata.spawnZ; + } + + static long g(WorldData worlddata) { + return worlddata.time; + } + + static long h(WorldData worlddata) { + return worlddata.dayTime; + } + + static int i(WorldData worlddata) { + return worlddata.dimension; + } + + static int j(WorldData worlddata) { + return worlddata.version; + } + + static int k(WorldData worlddata) { + return worlddata.rainTicks; + } + + static boolean l(WorldData worlddata) { + return worlddata.isRaining; + } + + static int m(WorldData worlddata) { + return worlddata.thunderTicks; + } + + static boolean n(WorldData worlddata) { + return worlddata.isThundering; + } + + static EnumGamemode o(WorldData worlddata) { + return worlddata.gameType; + } + + static boolean p(WorldData worlddata) { + return worlddata.hardcore; + } + + static boolean q(WorldData worlddata) { + return worlddata.allowCommands; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenBigTree.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenBigTree.java new file mode 100644 index 0000000..1f8f1a9 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenBigTree.java @@ -0,0 +1,372 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenBigTree extends WorldGenTreeAbstract { + + static final byte[] a = new byte[] { (byte) 2, (byte) 0, (byte) 0, (byte) 1, (byte) 2, (byte) 1}; + Random b = new Random(); + World world; + int[] d = new int[] { 0, 0, 0}; + int e; + int f; + double g = 0.618D; + double h = 1.0D; + double i = 0.381D; + double j = 1.0D; + double k = 1.0D; + int l = 1; + int m = 12; + int n = 4; + int[][] o; + + public WorldGenBigTree(boolean flag) { + super(flag); + } + + void a() { + this.f = (int) ((double) this.e * this.g); + if (this.f >= this.e) { + this.f = this.e - 1; + } + + int i = (int) (1.382D + Math.pow(this.k * (double) this.e / 13.0D, 2.0D)); + + if (i < 1) { + i = 1; + } + + int[][] aint = new int[i * this.e][4]; + int j = this.d[1] + this.e - this.n; + int k = 1; + int l = this.d[1] + this.f; + int i1 = j - this.d[1]; + + aint[0][0] = this.d[0]; + aint[0][1] = j; + aint[0][2] = this.d[2]; + aint[0][3] = l; + --j; + + while (i1 >= 0) { + int j1 = 0; + float f = this.a(i1); + + if (f < 0.0F) { + --j; + --i1; + } else { + for (double d0 = 0.5D; j1 < i; ++j1) { + double d1 = this.j * (double) f * ((double) this.b.nextFloat() + 0.328D); + double d2 = (double) this.b.nextFloat() * 2.0D * 3.14159D; + int k1 = MathHelper.floor(d1 * Math.sin(d2) + (double) this.d[0] + d0); + int l1 = MathHelper.floor(d1 * Math.cos(d2) + (double) this.d[2] + d0); + int[] aint1 = new int[] { k1, j, l1}; + int[] aint2 = new int[] { k1, j + this.n, l1}; + + if (this.a(aint1, aint2) == -1) { + int[] aint3 = new int[] { this.d[0], this.d[1], this.d[2]}; + double d3 = Math.sqrt(Math.pow((double) Math.abs(this.d[0] - aint1[0]), 2.0D) + Math.pow((double) Math.abs(this.d[2] - aint1[2]), 2.0D)); + double d4 = d3 * this.i; + + if ((double) aint1[1] - d4 > (double) l) { + aint3[1] = l; + } else { + aint3[1] = (int) ((double) aint1[1] - d4); + } + + if (this.a(aint3, aint1) == -1) { + aint[k][0] = k1; + aint[k][1] = j; + aint[k][2] = l1; + aint[k][3] = aint3[1]; + ++k; + } + } + } + + --j; + --i1; + } + } + + this.o = new int[k][4]; + System.arraycopy(aint, 0, this.o, 0, k); + } + + void a(int i, int j, int k, float f, byte b0, Block block) { + int l = (int) ((double) f + 0.618D); + byte b1 = a[b0]; + byte b2 = a[b0 + 3]; + int[] aint = new int[] { i, j, k}; + int[] aint1 = new int[] { 0, 0, 0}; + int i1 = -l; + int j1 = -l; + + for (aint1[b0] = aint[b0]; i1 <= l; ++i1) { + aint1[b1] = aint[b1] + i1; + j1 = -l; + + while (j1 <= l) { + double d0 = Math.pow((double) Math.abs(i1) + 0.5D, 2.0D) + Math.pow((double) Math.abs(j1) + 0.5D, 2.0D); + + if (d0 > (double) (f * f)) { + ++j1; + } else { + aint1[b2] = aint[b2] + j1; + Block block1 = this.world.getType(aint1[0], aint1[1], aint1[2]); + + if (block1.getMaterial() != Material.AIR && block1.getMaterial() != Material.LEAVES) { + ++j1; + } else { + this.setTypeAndData(this.world, aint1[0], aint1[1], aint1[2], block, 0); + ++j1; + } + } + } + } + } + + float a(int i) { + if ((double) i < (double) ((float) this.e) * 0.3D) { + return -1.618F; + } else { + float f = (float) this.e / 2.0F; + float f1 = (float) this.e / 2.0F - (float) i; + float f2; + + if (f1 == 0.0F) { + f2 = f; + } else if (Math.abs(f1) >= f) { + f2 = 0.0F; + } else { + f2 = (float) Math.sqrt(Math.pow((double) Math.abs(f), 2.0D) - Math.pow((double) Math.abs(f1), 2.0D)); + } + + f2 *= 0.5F; + return f2; + } + } + + float b(int i) { + return i >= 0 && i < this.n ? (i != 0 && i != this.n - 1 ? 3.0F : 2.0F) : -1.0F; + } + + void a(int i, int j, int k) { + int l = j; + + for (int i1 = j + this.n; l < i1; ++l) { + float f = this.b(l - j); + + this.a(i, l, k, f, (byte) 1, Blocks.LEAVES); + } + } + + void a(int[] aint, int[] aint1, Block block) { + int[] aint2 = new int[] { 0, 0, 0}; + byte b0 = 0; + + byte b1; + + for (b1 = 0; b0 < 3; ++b0) { + aint2[b0] = aint1[b0] - aint[b0]; + if (Math.abs(aint2[b0]) > Math.abs(aint2[b1])) { + b1 = b0; + } + } + + if (aint2[b1] != 0) { + byte b2 = a[b1]; + byte b3 = a[b1 + 3]; + byte b4; + + if (aint2[b1] > 0) { + b4 = 1; + } else { + b4 = -1; + } + + double d0 = (double) aint2[b2] / (double) aint2[b1]; + double d1 = (double) aint2[b3] / (double) aint2[b1]; + int[] aint3 = new int[] { 0, 0, 0}; + int i = 0; + + for (int j = aint2[b1] + b4; i != j; i += b4) { + aint3[b1] = MathHelper.floor((double) (aint[b1] + i) + 0.5D); + aint3[b2] = MathHelper.floor((double) aint[b2] + (double) i * d0 + 0.5D); + aint3[b3] = MathHelper.floor((double) aint[b3] + (double) i * d1 + 0.5D); + byte b5 = 0; + int k = Math.abs(aint3[0] - aint[0]); + int l = Math.abs(aint3[2] - aint[2]); + int i1 = Math.max(k, l); + + if (i1 > 0) { + if (k == i1) { + b5 = 4; + } else if (l == i1) { + b5 = 8; + } + } + + this.setTypeAndData(this.world, aint3[0], aint3[1], aint3[2], block, b5); + } + } + } + + void b() { + int i = 0; + + for (int j = this.o.length; i < j; ++i) { + int k = this.o[i][0]; + int l = this.o[i][1]; + int i1 = this.o[i][2]; + + this.a(k, l, i1); + } + } + + boolean c(int i) { + return (double) i >= (double) this.e * 0.2D; + } + + void c() { + int i = this.d[0]; + int j = this.d[1]; + int k = this.d[1] + this.f; + int l = this.d[2]; + int[] aint = new int[] { i, j, l}; + int[] aint1 = new int[] { i, k, l}; + + this.a(aint, aint1, Blocks.LOG); + if (this.l == 2) { + ++aint[0]; + ++aint1[0]; + this.a(aint, aint1, Blocks.LOG); + ++aint[2]; + ++aint1[2]; + this.a(aint, aint1, Blocks.LOG); + aint[0] += -1; + aint1[0] += -1; + this.a(aint, aint1, Blocks.LOG); + } + } + + void d() { + int i = 0; + int j = this.o.length; + + for (int[] aint = new int[] { this.d[0], this.d[1], this.d[2]}; i < j; ++i) { + int[] aint1 = this.o[i]; + int[] aint2 = new int[] { aint1[0], aint1[1], aint1[2]}; + + aint[1] = aint1[3]; + int k = aint[1] - this.d[1]; + + if (this.c(k)) { + this.a(aint, aint2, Blocks.LOG); + } + } + } + + int a(int[] aint, int[] aint1) { + int[] aint2 = new int[] { 0, 0, 0}; + byte b0 = 0; + + byte b1; + + for (b1 = 0; b0 < 3; ++b0) { + aint2[b0] = aint1[b0] - aint[b0]; + if (Math.abs(aint2[b0]) > Math.abs(aint2[b1])) { + b1 = b0; + } + } + + if (aint2[b1] == 0) { + return -1; + } else { + byte b2 = a[b1]; + byte b3 = a[b1 + 3]; + byte b4; + + if (aint2[b1] > 0) { + b4 = 1; + } else { + b4 = -1; + } + + double d0 = (double) aint2[b2] / (double) aint2[b1]; + double d1 = (double) aint2[b3] / (double) aint2[b1]; + int[] aint3 = new int[] { 0, 0, 0}; + int i = 0; + + int j; + + for (j = aint2[b1] + b4; i != j; i += b4) { + aint3[b1] = aint[b1] + i; + aint3[b2] = MathHelper.floor((double) aint[b2] + (double) i * d0); + aint3[b3] = MathHelper.floor((double) aint[b3] + (double) i * d1); + Block block = this.world.getType(aint3[0], aint3[1], aint3[2]); + + if (!this.a(block) || aint[1] >= 256) { // CraftBukkit - fix trees wrapping around + break; + } + } + + return i == j ? -1 : Math.abs(i); + } + } + + boolean e() { + int[] aint = new int[] { this.d[0], this.d[1], this.d[2]}; + int[] aint1 = new int[] { this.d[0], this.d[1] + this.e - 1, this.d[2]}; + Block block = this.world.getType(this.d[0], this.d[1] - 1, this.d[2]); + + if (block != Blocks.DIRT && block != Blocks.GRASS && block != Blocks.SOIL) { + return false; + } else { + int i = this.a(aint, aint1); + + if (i == -1) { + return true; + } else if (i < 6) { + return false; + } else { + this.e = i; + return true; + } + } + } + + public void a(double d0, double d1, double d2) { + this.m = (int) (d0 * 12.0D); + if (d0 > 0.5D) { + this.n = 5; + } + + this.j = d1; + this.k = d2; + } + + public boolean generate(World world, Random random, int i, int j, int k) { + this.world = world; + long l = random.nextLong(); + + this.b.setSeed(l); + this.d[0] = i; + this.d[1] = j; + this.d[2] = k; + if (this.e == 0) { + this.e = 5 + this.b.nextInt(this.m); + } + + if (!this.e()) { + return false; + } else { + this.a(); + this.b(); + this.c(); + this.d(); + return true; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenCanyon.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenCanyon.java new file mode 100644 index 0000000..5c68df1 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenCanyon.java @@ -0,0 +1,189 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenCanyon extends WorldGenBase { + + private float[] d = new float[1024]; + + public WorldGenCanyon() {} + + protected void a(long i, int j, int k, Block[] ablock, double d0, double d1, double d2, float f, float f1, float f2, int l, int i1, double d3) { + Random random = new Random(i); + double d4 = (double) (j * 16 + 8); + double d5 = (double) (k * 16 + 8); + float f3 = 0.0F; + float f4 = 0.0F; + + if (i1 <= 0) { + int j1 = this.a * 16 - 16; + + i1 = j1 - random.nextInt(j1 / 4); + } + + boolean flag = false; + + if (l == -1) { + l = i1 / 2; + flag = true; + } + + float f5 = 1.0F; + + for (int k1 = 0; k1 < 256; ++k1) { + if (k1 == 0 || random.nextInt(3) == 0) { + f5 = 1.0F + random.nextFloat() * random.nextFloat() * 1.0F; + } + + this.d[k1] = f5 * f5; + } + + for (; l < i1; ++l) { + double d6 = 1.5D + (double) (MathHelper.sin((float) l * 3.1415927F / (float) i1) * f * 1.0F); + double d7 = d6 * d3; + + d6 *= (double) random.nextFloat() * 0.25D + 0.75D; + d7 *= (double) random.nextFloat() * 0.25D + 0.75D; + float f6 = MathHelper.cos(f2); + float f7 = MathHelper.sin(f2); + + d0 += (double) (MathHelper.cos(f1) * f6); + d1 += (double) f7; + d2 += (double) (MathHelper.sin(f1) * f6); + f2 *= 0.7F; + f2 += f4 * 0.05F; + f1 += f3 * 0.05F; + f4 *= 0.8F; + f3 *= 0.5F; + f4 += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 2.0F; + f3 += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 4.0F; + if (flag || random.nextInt(4) != 0) { + double d8 = d0 - d4; + double d9 = d2 - d5; + double d10 = (double) (i1 - l); + double d11 = (double) (f + 2.0F + 16.0F); + + if (d8 * d8 + d9 * d9 - d10 * d10 > d11 * d11) { + return; + } + + if (d0 >= d4 - 16.0D - d6 * 2.0D && d2 >= d5 - 16.0D - d6 * 2.0D && d0 <= d4 + 16.0D + d6 * 2.0D && d2 <= d5 + 16.0D + d6 * 2.0D) { + int l1 = MathHelper.floor(d0 - d6) - j * 16 - 1; + int i2 = MathHelper.floor(d0 + d6) - j * 16 + 1; + int j2 = MathHelper.floor(d1 - d7) - 1; + int k2 = MathHelper.floor(d1 + d7) + 1; + int l2 = MathHelper.floor(d2 - d6) - k * 16 - 1; + int i3 = MathHelper.floor(d2 + d6) - k * 16 + 1; + + if (l1 < 0) { + l1 = 0; + } + + if (i2 > 16) { + i2 = 16; + } + + if (j2 < 1) { + j2 = 1; + } + + if (k2 > 248) { + k2 = 248; + } + + if (l2 < 0) { + l2 = 0; + } + + if (i3 > 16) { + i3 = 16; + } + + boolean flag1 = false; + + int j3; + int k3; + + for (j3 = l1; !flag1 && j3 < i2; ++j3) { + for (int l3 = l2; !flag1 && l3 < i3; ++l3) { + for (int i4 = k2 + 1; !flag1 && i4 >= j2 - 1; --i4) { + k3 = (j3 * 16 + l3) * 256 + i4; + if (i4 >= 0 && i4 < 256) { + Block block = ablock[k3]; + + if (block == Blocks.WATER || block == Blocks.STATIONARY_WATER) { + flag1 = true; + } + + if (i4 != j2 - 1 && j3 != l1 && j3 != i2 - 1 && l3 != l2 && l3 != i3 - 1) { + i4 = j2; + } + } + } + } + } + + if (!flag1) { + for (j3 = l1; j3 < i2; ++j3) { + double d12 = ((double) (j3 + j * 16) + 0.5D - d0) / d6; + + for (k3 = l2; k3 < i3; ++k3) { + double d13 = ((double) (k3 + k * 16) + 0.5D - d2) / d6; + int j4 = (j3 * 16 + k3) * 256 + k2; + boolean flag2 = false; + + if (d12 * d12 + d13 * d13 < 1.0D) { + for (int k4 = k2 - 1; k4 >= j2; --k4) { + double d14 = ((double) k4 + 0.5D - d1) / d7; + + if ((d12 * d12 + d13 * d13) * (double) this.d[k4] + d14 * d14 / 6.0D < 1.0D) { + Block block1 = ablock[j4]; + + if (block1 == Blocks.GRASS) { + flag2 = true; + } + + if (block1 == Blocks.STONE || block1 == Blocks.DIRT || block1 == Blocks.GRASS) { + if (k4 < 10) { + ablock[j4] = Blocks.LAVA; + } else { + ablock[j4] = null; + if (flag2 && ablock[j4 - 1] == Blocks.DIRT) { + ablock[j4 - 1] = this.c.getBiome(j3 + j * 16, k3 + k * 16).ai; + } + } + } + } + + --j4; + } + } + } + } + + if (flag) { + break; + } + } + } + } + } + } + + protected void a(World world, int i, int j, int k, int l, Block[] ablock) { + if (this.b.nextInt(50) == 0) { + double d0 = (double) (i * 16 + this.b.nextInt(16)); + double d1 = (double) (this.b.nextInt(this.b.nextInt(40) + 8) + 20); + double d2 = (double) (j * 16 + this.b.nextInt(16)); + byte b0 = 1; + + for (int i1 = 0; i1 < b0; ++i1) { + float f = this.b.nextFloat() * 3.1415927F * 2.0F; + float f1 = (this.b.nextFloat() - 0.5F) * 2.0F / 8.0F; + float f2 = (this.b.nextFloat() * 2.0F + this.b.nextFloat()) * 2.0F; + + this.a(this.b.nextLong(), k, l, ablock, d0, d1, d2, f2, f, f1, 0, 0, 3.0D); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenCaves.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenCaves.java new file mode 100644 index 0000000..38077bf --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenCaves.java @@ -0,0 +1,206 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenCaves extends WorldGenBase { + + public WorldGenCaves() {} + + protected void a(long i, int j, int k, Block[] ablock, double d0, double d1, double d2) { + this.a(i, j, k, ablock, d0, d1, d2, 1.0F + this.b.nextFloat() * 6.0F, 0.0F, 0.0F, -1, -1, 0.5D); + } + + protected void a(long i, int j, int k, Block[] ablock, double d0, double d1, double d2, float f, float f1, float f2, int l, int i1, double d3) { + double d4 = (double) (j * 16 + 8); + double d5 = (double) (k * 16 + 8); + float f3 = 0.0F; + float f4 = 0.0F; + Random random = new Random(i); + + if (i1 <= 0) { + int j1 = this.a * 16 - 16; + + i1 = j1 - random.nextInt(j1 / 4); + } + + boolean flag = false; + + if (l == -1) { + l = i1 / 2; + flag = true; + } + + int k1 = random.nextInt(i1 / 2) + i1 / 4; + + for (boolean flag1 = random.nextInt(6) == 0; l < i1; ++l) { + double d6 = 1.5D + (double) (MathHelper.sin((float) l * 3.1415927F / (float) i1) * f * 1.0F); + double d7 = d6 * d3; + float f5 = MathHelper.cos(f2); + float f6 = MathHelper.sin(f2); + + d0 += (double) (MathHelper.cos(f1) * f5); + d1 += (double) f6; + d2 += (double) (MathHelper.sin(f1) * f5); + if (flag1) { + f2 *= 0.92F; + } else { + f2 *= 0.7F; + } + + f2 += f4 * 0.1F; + f1 += f3 * 0.1F; + f4 *= 0.9F; + f3 *= 0.75F; + f4 += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 2.0F; + f3 += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 4.0F; + if (!flag && l == k1 && f > 1.0F && i1 > 0) { + this.a(random.nextLong(), j, k, ablock, d0, d1, d2, random.nextFloat() * 0.5F + 0.5F, f1 - 1.5707964F, f2 / 3.0F, l, i1, 1.0D); + this.a(random.nextLong(), j, k, ablock, d0, d1, d2, random.nextFloat() * 0.5F + 0.5F, f1 + 1.5707964F, f2 / 3.0F, l, i1, 1.0D); + return; + } + + if (flag || random.nextInt(4) != 0) { + double d8 = d0 - d4; + double d9 = d2 - d5; + double d10 = (double) (i1 - l); + double d11 = (double) (f + 2.0F + 16.0F); + + if (d8 * d8 + d9 * d9 - d10 * d10 > d11 * d11) { + return; + } + + if (d0 >= d4 - 16.0D - d6 * 2.0D && d2 >= d5 - 16.0D - d6 * 2.0D && d0 <= d4 + 16.0D + d6 * 2.0D && d2 <= d5 + 16.0D + d6 * 2.0D) { + int l1 = MathHelper.floor(d0 - d6) - j * 16 - 1; + int i2 = MathHelper.floor(d0 + d6) - j * 16 + 1; + int j2 = MathHelper.floor(d1 - d7) - 1; + int k2 = MathHelper.floor(d1 + d7) + 1; + int l2 = MathHelper.floor(d2 - d6) - k * 16 - 1; + int i3 = MathHelper.floor(d2 + d6) - k * 16 + 1; + + if (l1 < 0) { + l1 = 0; + } + + if (i2 > 16) { + i2 = 16; + } + + if (j2 < 1) { + j2 = 1; + } + + if (k2 > 248) { + k2 = 248; + } + + if (l2 < 0) { + l2 = 0; + } + + if (i3 > 16) { + i3 = 16; + } + + boolean flag2 = false; + + int j3; + int k3; + + for (j3 = l1; !flag2 && j3 < i2; ++j3) { + for (int l3 = l2; !flag2 && l3 < i3; ++l3) { + for (int i4 = k2 + 1; !flag2 && i4 >= j2 - 1; --i4) { + k3 = (j3 * 16 + l3) * 256 + i4; + if (i4 >= 0 && i4 < 256) { + Block block = ablock[k3]; + + if (block == Blocks.WATER || block == Blocks.STATIONARY_WATER) { + flag2 = true; + } + + if (i4 != j2 - 1 && j3 != l1 && j3 != i2 - 1 && l3 != l2 && l3 != i3 - 1) { + i4 = j2; + } + } + } + } + } + + if (!flag2) { + for (j3 = l1; j3 < i2; ++j3) { + double d12 = ((double) (j3 + j * 16) + 0.5D - d0) / d6; + + for (k3 = l2; k3 < i3; ++k3) { + double d13 = ((double) (k3 + k * 16) + 0.5D - d2) / d6; + int j4 = (j3 * 16 + k3) * 256 + k2; + boolean flag3 = false; + + if (d12 * d12 + d13 * d13 < 1.0D) { + for (int k4 = k2 - 1; k4 >= j2; --k4) { + double d14 = ((double) k4 + 0.5D - d1) / d7; + + if (d14 > -0.7D && d12 * d12 + d14 * d14 + d13 * d13 < 1.0D) { + Block block1 = ablock[j4]; + + if (block1 == Blocks.GRASS) { + flag3 = true; + } + + if (block1 == Blocks.STONE || block1 == Blocks.DIRT || block1 == Blocks.GRASS) { + if (k4 < 10) { + ablock[j4] = Blocks.STATIONARY_LAVA; + } else { + ablock[j4] = null; + if (flag3 && ablock[j4 - 1] == Blocks.DIRT) { + ablock[j4 - 1] = this.c.getBiome(j3 + j * 16, k3 + k * 16).ai; + } + } + } + } + + --j4; + } + } + } + } + + if (flag) { + break; + } + } + } + } + } + } + + protected void a(World world, int i, int j, int k, int l, Block[] ablock) { + int i1 = this.b.nextInt(this.b.nextInt(this.b.nextInt((int) (15 * world.generatorConfig.cavesMultiplier)) + 1) + 1); + + if (this.b.nextFloat() > world.generatorConfig.cavesMultiplier / 7) { + i1 = 0; + } + + for (int j1 = 0; j1 < i1; ++j1) { + double d0 = (double) (i * 16 + this.b.nextInt(16)); + double d1 = (double) this.b.nextInt(this.b.nextInt(120) + 8); + double d2 = (double) (j * 16 + this.b.nextInt(16)); + int k1 = 1; + + if (this.b.nextInt(4) == 0) { + this.a(this.b.nextLong(), k, l, ablock, d0, d1, d2); + k1 += this.b.nextInt(4); + } + + for (int l1 = 0; l1 < k1; ++l1) { + float f = this.b.nextFloat() * 3.1415927F * 2.0F; + float f1 = (this.b.nextFloat() - 0.5F) * 2.0F / 8.0F; + float f2 = this.b.nextFloat() * 2.0F + this.b.nextFloat(); + + if (this.b.nextInt(10) == 0) { + f2 *= this.b.nextFloat() * this.b.nextFloat() * 3.0F + 1.0F; + } + + this.a(this.b.nextLong(), k, l, ablock, d0, d1, d2, f2, f, f1, 0, 0, 1.0D); + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenForestTree.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenForestTree.java new file mode 100644 index 0000000..6196163 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenForestTree.java @@ -0,0 +1,171 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenForestTree extends WorldGenTreeAbstract { + + public WorldGenForestTree(boolean flag) { + super(flag); + } + + public boolean generate(World world, Random random, int i, int j, int k) { + int l = random.nextInt(3) + random.nextInt(2) + 6; + boolean flag = true; + + if (j >= 1 && j + l + 1 <= 256) { + int i1; + int j1; + + for (int k1 = j; k1 <= j + 1 + l; ++k1) { + byte b0 = 1; + + if (k1 == j) { + b0 = 0; + } + + if (k1 >= j + 1 + l - 2) { + b0 = 2; + } + + for (i1 = i - b0; i1 <= i + b0 && flag; ++i1) { + for (j1 = k - b0; j1 <= k + b0 && flag; ++j1) { + if (k1 >= 0 && k1 < 256) { + Block block = world.getType(i1, k1, j1); + + if (!this.a(block)) { + flag = false; + } + } else { + flag = false; + } + } + } + } + + if (!flag) { + return false; + } else { + Block block1 = world.getType(i, j - 1, k); + + if ((block1 == Blocks.GRASS || block1 == Blocks.DIRT) && j < 256 - l - 1) { + this.setType(world, i, j - 1, k, Blocks.DIRT); + this.setType(world, i + 1, j - 1, k, Blocks.DIRT); + this.setType(world, i + 1, j - 1, k + 1, Blocks.DIRT); + this.setType(world, i, j - 1, k + 1, Blocks.DIRT); + int l1 = random.nextInt(4); + + i1 = l - random.nextInt(4); + j1 = 2 - random.nextInt(3); + int i2 = i; + int j2 = k; + int k2 = 0; + + int l2; + int i3; + + for (l2 = 0; l2 < l; ++l2) { + i3 = j + l2; + if (l2 >= i1 && j1 > 0) { + i2 += Direction.a[l1]; + j2 += Direction.b[l1]; + --j1; + } + + Block block2 = world.getType(i2, i3, j2); + + if (block2.getMaterial() == Material.AIR || block2.getMaterial() == Material.LEAVES) { + this.setTypeAndData(world, i2, i3, j2, Blocks.LOG2, 1); + this.setTypeAndData(world, i2 + 1, i3, j2, Blocks.LOG2, 1); + this.setTypeAndData(world, i2, i3, j2 + 1, Blocks.LOG2, 1); + this.setTypeAndData(world, i2 + 1, i3, j2 + 1, Blocks.LOG2, 1); + k2 = i3; + } + } + + for (l2 = -2; l2 <= 0; ++l2) { + for (i3 = -2; i3 <= 0; ++i3) { + byte b1 = -1; + + this.a(world, i2 + l2, k2 + b1, j2 + i3); + this.a(world, 1 + i2 - l2, k2 + b1, j2 + i3); + this.a(world, i2 + l2, k2 + b1, 1 + j2 - i3); + this.a(world, 1 + i2 - l2, k2 + b1, 1 + j2 - i3); + if ((l2 > -2 || i3 > -1) && (l2 != -1 || i3 != -2)) { + byte b2 = 1; + + this.a(world, i2 + l2, k2 + b2, j2 + i3); + this.a(world, 1 + i2 - l2, k2 + b2, j2 + i3); + this.a(world, i2 + l2, k2 + b2, 1 + j2 - i3); + this.a(world, 1 + i2 - l2, k2 + b2, 1 + j2 - i3); + } + } + } + + if (random.nextBoolean()) { + this.a(world, i2, k2 + 2, j2); + this.a(world, i2 + 1, k2 + 2, j2); + this.a(world, i2 + 1, k2 + 2, j2 + 1); + this.a(world, i2, k2 + 2, j2 + 1); + } + + for (l2 = -3; l2 <= 4; ++l2) { + for (i3 = -3; i3 <= 4; ++i3) { + if ((l2 != -3 || i3 != -3) && (l2 != -3 || i3 != 4) && (l2 != 4 || i3 != -3) && (l2 != 4 || i3 != 4) && (Math.abs(l2) < 3 || Math.abs(i3) < 3)) { + this.a(world, i2 + l2, k2, j2 + i3); + } + } + } + + for (l2 = -1; l2 <= 2; ++l2) { + for (i3 = -1; i3 <= 2; ++i3) { + if ((l2 < 0 || l2 > 1 || i3 < 0 || i3 > 1) && random.nextInt(3) <= 0) { + int j3 = random.nextInt(3) + 2; + + int k3; + + for (k3 = 0; k3 < j3; ++k3) { + Block block = world.getType(i + l2, k2 - k3 - 1, k + i3); + + if (block.getMaterial() == Material.AIR || block.getMaterial() == Material.LEAVES) + { + this.setTypeAndData(world, i + l2, k2 - k3 - 1, k + i3, Blocks.LOG2, 1); + } + } + + int l3; + + for (k3 = -1; k3 <= 1; ++k3) { + for (l3 = -1; l3 <= 1; ++l3) { + this.a(world, i2 + l2 + k3, k2 - 0, j2 + i3 + l3); + } + } + + for (k3 = -2; k3 <= 2; ++k3) { + for (l3 = -2; l3 <= 2; ++l3) { + if (Math.abs(k3) != 2 || Math.abs(l3) != 2) { + this.a(world, i2 + l2 + k3, k2 - 1, j2 + i3 + l3); + } + } + } + } + } + } + + return true; + } else { + return false; + } + } + } else { + return false; + } + } + + private void a(World world, int i, int j, int k) { + Block block = world.getType(i, j, k); + + if (block.getMaterial() == Material.AIR) { + this.setTypeAndData(world, i, j, k, Blocks.LEAVES2, 1); + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenGroundBush.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenGroundBush.java new file mode 100644 index 0000000..1a82656 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenGroundBush.java @@ -0,0 +1,53 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenGroundBush extends WorldGenTrees { + + private int a; + private int b; + + public WorldGenGroundBush(int i, int j) { + super(false); + this.b = i; + this.a = j; + } + + public boolean generate(World world, Random random, int i, int j, int k) { // Spigot + Block block; + + while (((block = world.getType(i, j, k)).getMaterial() == Material.AIR || block.getMaterial() == Material.LEAVES) && j > 0) { + --j; + } + + Block block1 = world.getType(i, j, k); + + if (block1 == Blocks.DIRT || block1 == Blocks.GRASS) { + ++j; + this.setTypeAndData(world, i, j, k, Blocks.LOG, this.b); + + for (int l = j; l <= j + 2; ++l) { + int i1 = l - j; + int j1 = 2 - i1; + + for (int k1 = i - j1; k1 <= i + j1; ++k1) { + int l1 = k1 - i; + + for (int i2 = k - j1; i2 <= k + j1; ++i2) { + int j2 = i2 - k; + + if ((Math.abs(l1) != j1 || Math.abs(j2) != j1 || random.nextInt(2) != 0) && !world.getType(k1, l, i2).j()) { + this.setTypeAndData(world, k1, l, i2, Blocks.LEAVES, this.a); + } + } + } + } + // CraftBukkit start - Return false if gen was unsuccessful + } else { + return false; + } + // CraftBukkit end + + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenLargeFeature.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenLargeFeature.java new file mode 100644 index 0000000..72f5d10 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenLargeFeature.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Map.Entry; + +public class WorldGenLargeFeature extends StructureGenerator { + + private static List e = Arrays.asList(new BiomeBase[] { BiomeBase.DESERT, BiomeBase.DESERT_HILLS, BiomeBase.JUNGLE, BiomeBase.JUNGLE_HILLS, BiomeBase.SWAMPLAND}); + private List f; + private int g; + private int h; + + public WorldGenLargeFeature() { + this.f = new ArrayList(); + this.g = 32; + this.h = 8; + this.f.add(new BiomeMeta(EntityWitch.class, 1, 1, 1)); + } + + public WorldGenLargeFeature(Map map) { + this(); + Iterator iterator = map.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + if (((String) entry.getKey()).equals("distance")) { + this.g = MathHelper.a((String) entry.getValue(), this.g, this.h + 1); + } + } + } + + public String a() { + return "Temple"; + } + + protected boolean a(int i, int j) { + int k = i; + int l = j; + + if (i < 0) { + i -= this.g - 1; + } + + if (j < 0) { + j -= this.g - 1; + } + + int i1 = i / this.g; + int j1 = j / this.g; + Random random = this.c.A(i1, j1, this.c.spigotConfig.largeFeatureSeed); // Spigot + + i1 *= this.g; + j1 *= this.g; + i1 += random.nextInt(this.g - this.h); + j1 += random.nextInt(this.g - this.h); + if (k == i1 && l == j1) { + BiomeBase biomebase = this.c.getWorldChunkManager().getBiome(k * 16 + 8, l * 16 + 8); + Iterator iterator = e.iterator(); + + while (iterator.hasNext()) { + BiomeBase biomebase1 = (BiomeBase) iterator.next(); + + if (biomebase == biomebase1) { + return true; + } + } + } + + return false; + } + + protected StructureStart b(int i, int j) { + return new WorldGenLargeFeatureStart(this.c, this.b, i, j); + } + + public boolean a(int i, int j, int k) { + StructureStart structurestart = this.c(i, j, k); + + if (structurestart != null && structurestart instanceof WorldGenLargeFeatureStart && !structurestart.a.isEmpty()) { + StructurePiece structurepiece = (StructurePiece) structurestart.a.getFirst(); + + return structurepiece instanceof WorldGenWitchHut; + } else { + return false; + } + } + + public List b() { + return this.f; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenMegaTreeAbstract.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenMegaTreeAbstract.java new file mode 100644 index 0000000..0fd2171 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenMegaTreeAbstract.java @@ -0,0 +1,126 @@ +package net.minecraft.server; + +import java.util.Random; + +public abstract class WorldGenMegaTreeAbstract extends WorldGenTreeAbstract { + + protected final int a; + protected final int b; + protected final int c; + protected int d; + + public WorldGenMegaTreeAbstract(boolean flag, int i, int j, int k, int l) { + super(flag); + this.a = i; + this.d = j; + this.b = k; + this.c = l; + } + + protected int a(Random random) { + int i = random.nextInt(3) + this.a; + + if (this.d > 1) { + i += random.nextInt(this.d); + } + + return i; + } + + private boolean b(World world, Random random, int i, int j, int k, int l) { + boolean flag = true; + + if (j >= 1 && j + l + 1 <= 256) { + for (int i1 = j; i1 <= j + 1 + l; ++i1) { + byte b0 = 2; + + if (i1 == j) { + b0 = 1; + } + + if (i1 >= j + 1 + l - 2) { + b0 = 2; + } + + for (int j1 = i - b0; j1 <= i + b0 && flag; ++j1) { + for (int k1 = k - b0; k1 <= k + b0 && flag; ++k1) { + if (i1 >= 0 && i1 < 256) { + Block block = world.getType(j1, i1, k1); + + // CraftBukkit - ignore our own saplings + if (block != Blocks.SAPLING && !this.a(block)) { + flag = false; + } + } else { + flag = false; + } + } + } + } + + return flag; + } else { + return false; + } + } + + private boolean c(World world, Random random, int i, int j, int k) { + Block block = world.getType(i, j - 1, k); + + if ((block == Blocks.GRASS || block == Blocks.DIRT) && j >= 2) { + world.setTypeAndData(i, j - 1, k, Blocks.DIRT, 0, 2); + world.setTypeAndData(i + 1, j - 1, k, Blocks.DIRT, 0, 2); + world.setTypeAndData(i, j - 1, k + 1, Blocks.DIRT, 0, 2); + world.setTypeAndData(i + 1, j - 1, k + 1, Blocks.DIRT, 0, 2); + return true; + } else { + return false; + } + } + + protected boolean a(World world, Random random, int i, int j, int k, int l) { + return this.b(world, random, i, j, k, l) && this.c(world, random, i, j, k); + } + + protected void a(World world, int i, int j, int k, int l, Random random) { + int i1 = l * l; + + for (int j1 = i - l; j1 <= i + l + 1; ++j1) { + int k1 = j1 - i; + + for (int l1 = k - l; l1 <= k + l + 1; ++l1) { + int i2 = l1 - k; + int j2 = k1 - 1; + int k2 = i2 - 1; + + if (k1 * k1 + i2 * i2 <= i1 || j2 * j2 + k2 * k2 <= i1 || k1 * k1 + k2 * k2 <= i1 || j2 * j2 + i2 * i2 <= i1) { + Block block = world.getType(j1, j, l1); + + if (block.getMaterial() == Material.AIR || block.getMaterial() == Material.LEAVES) { + this.setTypeAndData(world, j1, j, l1, Blocks.LEAVES, this.c); + } + } + } + } + } + + protected void b(World world, int i, int j, int k, int l, Random random) { + int i1 = l * l; + + for (int j1 = i - l; j1 <= i + l; ++j1) { + int k1 = j1 - i; + + for (int l1 = k - l; l1 <= k + l; ++l1) { + int i2 = l1 - k; + + if (k1 * k1 + i2 * i2 <= i1) { + Block block = world.getType(j1, j, l1); + + if (block.getMaterial() == Material.AIR || block.getMaterial() == Material.LEAVES) { + this.setTypeAndData(world, j1, j, l1, Blocks.LEAVES, this.c); + } + } + } + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenMinable.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenMinable.java new file mode 100644 index 0000000..4219410 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenMinable.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +public class WorldGenMinable extends WorldGenerator { + + private Block a; + private int b; + private Block c; + private boolean mustTouchAir; // MineHQ + + public WorldGenMinable(Block block, int i) { + this(block, i, Blocks.STONE); + } + + public WorldGenMinable(Block block, int i, Block block1) { + this.a = block; + this.b = i; + this.c = block1; + } + + // MineHQ start + public WorldGenMinable(Block block, int size, boolean mustTouchAir) { + this(block, size, Blocks.STONE); + this.mustTouchAir = mustTouchAir; + } + // MineHQ end + + public boolean generate(World world, Random random, int i, int j, int k) { + float f = random.nextFloat() * 3.1415927F; + double d0 = (double) ((float) (i + 8) + MathHelper.sin(f) * (float) this.b / 8.0F); + double d1 = (double) ((float) (i + 8) - MathHelper.sin(f) * (float) this.b / 8.0F); + double d2 = (double) ((float) (k + 8) + MathHelper.cos(f) * (float) this.b / 8.0F); + double d3 = (double) ((float) (k + 8) - MathHelper.cos(f) * (float) this.b / 8.0F); + double d4 = (double) (j + random.nextInt(3) - 2); + double d5 = (double) (j + random.nextInt(3) - 2); + + boolean touchedAir = false; + Set blocks = mustTouchAir ? new HashSet() : null; + + for (int l = 0; l <= this.b; ++l) { + double d6 = d0 + (d1 - d0) * (double) l / (double) this.b; + double d7 = d4 + (d5 - d4) * (double) l / (double) this.b; + double d8 = d2 + (d3 - d2) * (double) l / (double) this.b; + double d9 = random.nextDouble() * (double) this.b / 16.0D; + double d10 = (double) (MathHelper.sin((float) l * 3.1415927F / (float) this.b) + 1.0F) * d9 + 1.0D; + double d11 = (double) (MathHelper.sin((float) l * 3.1415927F / (float) this.b) + 1.0F) * d9 + 1.0D; + int i1 = MathHelper.floor(d6 - d10 / 2.0D); + int j1 = MathHelper.floor(d7 - d11 / 2.0D); + int k1 = MathHelper.floor(d8 - d10 / 2.0D); + int l1 = MathHelper.floor(d6 + d10 / 2.0D); + int i2 = MathHelper.floor(d7 + d11 / 2.0D); + int j2 = MathHelper.floor(d8 + d10 / 2.0D); + + for (int k2 = i1; k2 <= l1; ++k2) { + double d12 = ((double) k2 + 0.5D - d6) / (d10 / 2.0D); + + if (d12 * d12 < 1.0D) { + for (int l2 = j1; l2 <= i2; ++l2) { + double d13 = ((double) l2 + 0.5D - d7) / (d11 / 2.0D); + + if (d12 * d12 + d13 * d13 < 1.0D) { + for (int i3 = k1; i3 <= j2; ++i3) { + double d14 = ((double) i3 + 0.5D - d8) / (d10 / 2.0D); + + if (d12 * d12 + d13 * d13 + d14 * d14 < 1.0D && world.getType(k2, l2, i3) == this.c) { + if (mustTouchAir) { + blocks.add(new ChunkPosition(k2, l2, i3)); + touchedAir |= + world.getType(k2 + 1, l2, i3) == Blocks.AIR || + world.getType(k2 - 1, l2, i3) == Blocks.AIR || + world.getType(k2, l2 + 1, i3) == Blocks.AIR || + world.getType(k2, l2 - 1, i3) == Blocks.AIR || + world.getType(k2, l2, i3 + 1) == Blocks.AIR || + world.getType(k2, l2, i3 - 1) == Blocks.AIR; + } else { + world.setTypeAndData(k2, l2, i3, this.a, 0, 2); + } + } + } + } + } + } + } + } + + if (mustTouchAir && touchedAir) { + for (ChunkPosition block : blocks) { + world.setTypeAndData(block.x, block.y, block.z, this.a, 0, 2); + } + } + + return true; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenPackedIce2.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenPackedIce2.java new file mode 100644 index 0000000..ae1bf65 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenPackedIce2.java @@ -0,0 +1,101 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenPackedIce2 extends WorldGenerator { + + public WorldGenPackedIce2() {} + + public boolean generate(World world, Random random, int i, int j, int k) { + while (world.isEmpty(i, j, k) && j > 2) { + --j; + } + + if (world.getType(i, j, k) != Blocks.SNOW_BLOCK) { + return false; + } else { + j += random.nextInt(4); + int l = random.nextInt(4) + 7; + int i1 = l / 4 + random.nextInt(2); + + if (i1 > 1 && random.nextInt(60) == 0) { + j += 10 + random.nextInt(30); + } + + int j1; + int k1; + int l1; + + for (j1 = 0; j1 < l; ++j1) { + float f = (1.0F - (float) j1 / (float) l) * (float) i1; + + k1 = MathHelper.f(f); + + for (l1 = -k1; l1 <= k1; ++l1) { + float f1 = (float) MathHelper.a(l1) - 0.25F; + + for (int i2 = -k1; i2 <= k1; ++i2) { + float f2 = (float) MathHelper.a(i2) - 0.25F; + + if ((l1 == 0 && i2 == 0 || f1 * f1 + f2 * f2 <= f * f) && (l1 != -k1 && l1 != k1 && i2 != -k1 && i2 != k1 || random.nextFloat() <= 0.75F)) { + Block block = world.getType(i + l1, j + j1, k + i2); + + if (block.getMaterial() == Material.AIR || block == Blocks.DIRT || block == Blocks.SNOW_BLOCK || block == Blocks.ICE) { + world.setTypeUpdate(i + l1, j + j1, k + i2, Blocks.PACKED_ICE); // Spigot + } + + if (j1 != 0 && k1 > 1) { + block = world.getType(i + l1, j - j1, k + i2); + if (block.getMaterial() == Material.AIR || block == Blocks.DIRT || block == Blocks.SNOW_BLOCK || block == Blocks.ICE) { + world.setTypeUpdate(i + l1, j - j1, k + i2, Blocks.PACKED_ICE); // Spigot + } + } + } + } + } + } + + j1 = i1 - 1; + if (j1 < 0) { + j1 = 0; + } else if (j1 > 1) { + j1 = 1; + } + + for (int j2 = -j1; j2 <= j1; ++j2) { + k1 = -j1; + + while (k1 <= j1) { + l1 = j - 1; + int k2 = 50; + + if (Math.abs(j2) == 1 && Math.abs(k1) == 1) { + k2 = random.nextInt(5); + } + + while (true) { + if (l1 > 50) { + Block block1 = world.getType(i + j2, l1, k + k1); + + if (block1.getMaterial() == Material.AIR || block1 == Blocks.DIRT || block1 == Blocks.SNOW_BLOCK || block1 == Blocks.ICE || block1 == Blocks.PACKED_ICE) { + world.setTypeUpdate(i + j2, l1, k + k1, Blocks.PACKED_ICE); // Spigot + --l1; + --k2; + if (k2 <= 0) { + l1 -= random.nextInt(5) + 1; + k2 = random.nextInt(5); + } + continue; + } + } + + ++k1; + break; + } + } + } + + return true; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillage.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillage.java new file mode 100644 index 0000000..8d1721a --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillage.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Map.Entry; + +public class WorldGenVillage extends StructureGenerator { + + public static final List e = Arrays.asList(new BiomeBase[] { BiomeBase.PLAINS, BiomeBase.DESERT, BiomeBase.SAVANNA}); + private int f; + private int g; + private int h; + + public WorldGenVillage() { + this.g = 32; + this.h = 8; + } + + public WorldGenVillage(Map map) { + this(); + Iterator iterator = map.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + if (((String) entry.getKey()).equals("size")) { + this.f = MathHelper.a((String) entry.getValue(), this.f, 0); + } else if (((String) entry.getKey()).equals("distance")) { + this.g = MathHelper.a((String) entry.getValue(), this.g, this.h + 1); + } + } + } + + public String a() { + return "Village"; + } + + protected boolean a(int i, int j) { + int k = i; + int l = j; + + if (i < 0) { + i -= this.g - 1; + } + + if (j < 0) { + j -= this.g - 1; + } + + int i1 = i / this.g; + int j1 = j / this.g; + Random random = this.c.A(i1, j1, this.c.spigotConfig.villageSeed); // Spigot + + i1 *= this.g; + j1 *= this.g; + i1 += random.nextInt(this.g - this.h); + j1 += random.nextInt(this.g - this.h); + if (k == i1 && l == j1) { + boolean flag = this.c.getWorldChunkManager().a(k * 16 + 8, l * 16 + 8, 0, e); + + if (flag) { + return true; + } + } + + return false; + } + + protected StructureStart b(int i, int j) { + return new WorldGenVillageStart(this.c, this.b, i, j, this.f); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillagePiece.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillagePiece.java new file mode 100644 index 0000000..dd228e7 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillagePiece.java @@ -0,0 +1,189 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +abstract class WorldGenVillagePiece extends StructurePiece { + + protected int k = -1; + private int a; + private boolean b; + + public WorldGenVillagePiece() {} + + protected WorldGenVillagePiece(WorldGenVillageStartPiece worldgenvillagestartpiece, int i) { + super(i); + if (worldgenvillagestartpiece != null) { + this.b = worldgenvillagestartpiece.b; + } + } + + protected void a(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("HPos", this.k); + nbttagcompound.setInt("VCount", this.a); + nbttagcompound.setBoolean("Desert", this.b); + } + + protected void b(NBTTagCompound nbttagcompound) { + this.k = nbttagcompound.getInt("HPos"); + this.a = nbttagcompound.getInt("VCount"); + this.b = nbttagcompound.getBoolean("Desert"); + } + + protected StructurePiece a(WorldGenVillageStartPiece worldgenvillagestartpiece, List list, Random random, int i, int j) { + switch (this.g) { + case 0: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.a - 1, this.f.b + i, this.f.c + j, 1, this.d()); + + case 1: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.a + j, this.f.b + i, this.f.c - 1, 2, this.d()); + + case 2: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.a - 1, this.f.b + i, this.f.c + j, 1, this.d()); + + case 3: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.a + j, this.f.b + i, this.f.c - 1, 2, this.d()); + + default: + return null; + } + } + + protected StructurePiece b(WorldGenVillageStartPiece worldgenvillagestartpiece, List list, Random random, int i, int j) { + switch (this.g) { + case 0: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.d + 1, this.f.b + i, this.f.c + j, 3, this.d()); + + case 1: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.a + j, this.f.b + i, this.f.f + 1, 0, this.d()); + + case 2: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.d + 1, this.f.b + i, this.f.c + j, 3, this.d()); + + case 3: + return WorldGenVillagePieces.a(worldgenvillagestartpiece, list, random, this.f.a + j, this.f.b + i, this.f.f + 1, 0, this.d()); + + default: + return null; + } + } + + protected int b(World world, StructureBoundingBox structureboundingbox) { + int i = 0; + int j = 0; + + for (int k = this.f.c; k <= this.f.f; ++k) { + for (int l = this.f.a; l <= this.f.d; ++l) { + if (structureboundingbox.b(l, 64, k)) { + i += Math.max(world.i(l, k), world.worldProvider.getSeaLevel()); + ++j; + } + } + } + + if (j == 0) { + return -1; + } else { + return i / j; + } + } + + protected static boolean a(StructureBoundingBox structureboundingbox) { + return structureboundingbox != null && structureboundingbox.b > 10; + } + + protected void a(World world, StructureBoundingBox structureboundingbox, int i, int j, int k, int l) { + if (this.a < l) { + for (int i1 = this.a; i1 < l; ++i1) { + int j1 = this.a(i + i1, k); + int k1 = this.a(j); + int l1 = this.b(i + i1, k); + + if (!structureboundingbox.b(j1, k1, l1)) { + break; + } + + ++this.a; + EntityVillager entityvillager = new EntityVillager(world, this.b(i1)); + + entityvillager.setPositionRotation((double) j1 + 0.5D, (double) k1, (double) l1 + 0.5D, 0.0F, 0.0F); + world.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + } + } + + protected int b(int i) { + return 0; + } + + protected Block b(Block block, int i) { + if (this.b) { + if (block == Blocks.LOG || block == Blocks.LOG2) { + return Blocks.SANDSTONE; + } + + if (block == Blocks.COBBLESTONE) { + return Blocks.SANDSTONE; + } + + if (block == Blocks.WOOD) { + return Blocks.SANDSTONE; + } + + if (block == Blocks.WOOD_STAIRS) { + return Blocks.SANDSTONE_STAIRS; + } + + if (block == Blocks.COBBLESTONE_STAIRS) { + return Blocks.SANDSTONE_STAIRS; + } + + if (block == Blocks.GRAVEL) { + return Blocks.SANDSTONE; + } + } + + return block; + } + + protected int c(Block block, int i) { + if (this.b) { + if (block == Blocks.LOG || block == Blocks.LOG2) { + return 0; + } + + if (block == Blocks.COBBLESTONE) { + return 0; + } + + if (block == Blocks.WOOD) { + return 2; + } + } + + return i; + } + + protected void a(World world, Block block, int i, int j, int k, int l, StructureBoundingBox structureboundingbox) { + Block block1 = this.b(block, i); + int i1 = this.c(block, i); + + super.a(world, block1, i1, j, k, l, structureboundingbox); + } + + protected void a(World world, StructureBoundingBox structureboundingbox, int i, int j, int k, int l, int i1, int j1, Block block, Block block1, boolean flag) { + Block block2 = this.b(block, 0); + int k1 = this.c(block, 0); + Block block3 = this.b(block1, 0); + int l1 = this.c(block1, 0); + + super.a(world, structureboundingbox, i, j, k, l, i1, j1, block2, k1, block3, l1, flag); + } + + protected void b(World world, Block block, int i, int j, int k, int l, StructureBoundingBox structureboundingbox) { + Block block1 = this.b(block, i); + int i1 = this.c(block, i); + + super.b(world, block1, i1, j, k, l, structureboundingbox); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillagePieces.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillagePieces.java new file mode 100644 index 0000000..e08c6fe --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenVillagePieces.java @@ -0,0 +1,199 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +public class WorldGenVillagePieces { + + public static void a() { + WorldGenFactory.a(WorldGenVillageLibrary.class, "ViBH"); + WorldGenFactory.a(WorldGenVillageFarm2.class, "ViDF"); + WorldGenFactory.a(WorldGenVillageFarm.class, "ViF"); + WorldGenFactory.a(WorldGenVillageLight.class, "ViL"); + WorldGenFactory.a(WorldGenVillageButcher.class, "ViPH"); + WorldGenFactory.a(WorldGenVillageHouse.class, "ViSH"); + WorldGenFactory.a(WorldGenVillageHut.class, "ViSmH"); + WorldGenFactory.a(WorldGenVillageTemple.class, "ViST"); + WorldGenFactory.a(WorldGenVillageBlacksmith.class, "ViS"); + WorldGenFactory.a(WorldGenVillageStartPiece.class, "ViStart"); + WorldGenFactory.a(WorldGenVillageRoad.class, "ViSR"); + WorldGenFactory.a(WorldGenVillageHouse2.class, "ViTRH"); + WorldGenFactory.a(WorldGenVillageWell.class, "ViW"); + } + + public static List a(Random random, int i) { + ArrayList arraylist = new ArrayList(); + + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageHouse.class, 4, MathHelper.nextInt(random, 2 + i, 4 + i * 2))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageTemple.class, 20, MathHelper.nextInt(random, 0 + i, 1 + i))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageLibrary.class, 20, MathHelper.nextInt(random, 0 + i, 2 + i))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageHut.class, 3, MathHelper.nextInt(random, 2 + i, 5 + i * 3))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageButcher.class, 15, MathHelper.nextInt(random, 0 + i, 2 + i))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageFarm2.class, 3, MathHelper.nextInt(random, 1 + i, 4 + i))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageFarm.class, 3, MathHelper.nextInt(random, 2 + i, 4 + i * 2))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageBlacksmith.class, 15, MathHelper.nextInt(random, 0, 1 + i))); + arraylist.add(new WorldGenVillagePieceWeight(WorldGenVillageHouse2.class, 8, MathHelper.nextInt(random, 0 + i, 3 + i * 2))); + Iterator iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + if (((WorldGenVillagePieceWeight) iterator.next()).d == 0) { + iterator.remove(); + } + } + + return arraylist; + } + + private static int a(List list) { + boolean flag = false; + int i = 0; + + WorldGenVillagePieceWeight worldgenvillagepieceweight; + + for (Iterator iterator = list.iterator(); iterator.hasNext(); i += worldgenvillagepieceweight.b) { + worldgenvillagepieceweight = (WorldGenVillagePieceWeight) iterator.next(); + if (worldgenvillagepieceweight.d > 0 && worldgenvillagepieceweight.c < worldgenvillagepieceweight.d) { + flag = true; + } + } + + return flag ? i : -1; + } + + private static WorldGenVillagePiece a(WorldGenVillageStartPiece worldgenvillagestartpiece, WorldGenVillagePieceWeight worldgenvillagepieceweight, List list, Random random, int i, int j, int k, int l, int i1) { + Class oclass = worldgenvillagepieceweight.a; + Object object = null; + + if (oclass == WorldGenVillageHouse.class) { + object = WorldGenVillageHouse.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageTemple.class) { + object = WorldGenVillageTemple.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageLibrary.class) { + object = WorldGenVillageLibrary.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageHut.class) { + object = WorldGenVillageHut.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageButcher.class) { + object = WorldGenVillageButcher.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageFarm2.class) { + object = WorldGenVillageFarm2.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageFarm.class) { + object = WorldGenVillageFarm.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageBlacksmith.class) { + object = WorldGenVillageBlacksmith.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } else if (oclass == WorldGenVillageHouse2.class) { + object = WorldGenVillageHouse2.a(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } + + return (WorldGenVillagePiece) object; + } + + private static WorldGenVillagePiece c(WorldGenVillageStartPiece worldgenvillagestartpiece, List list, Random random, int i, int j, int k, int l, int i1) { + int j1 = a(worldgenvillagestartpiece.e); + + if (j1 <= 0) { + return null; + } else { + int k1 = 0; + + while (k1 < 5) { + ++k1; + int l1 = random.nextInt(j1); + Iterator iterator = worldgenvillagestartpiece.e.iterator(); + + while (iterator.hasNext()) { + WorldGenVillagePieceWeight worldgenvillagepieceweight = (WorldGenVillagePieceWeight) iterator.next(); + + l1 -= worldgenvillagepieceweight.b; + if (l1 < 0) { + if (!worldgenvillagepieceweight.a(i1) || worldgenvillagepieceweight == worldgenvillagestartpiece.d && worldgenvillagestartpiece.e.size() > 1) { + break; + } + + WorldGenVillagePiece worldgenvillagepiece = a(worldgenvillagestartpiece, worldgenvillagepieceweight, list, random, i, j, k, l, i1); + + if (worldgenvillagepiece != null) { + ++worldgenvillagepieceweight.c; + worldgenvillagestartpiece.d = worldgenvillagepieceweight; + if (!worldgenvillagepieceweight.a()) { + worldgenvillagestartpiece.e.remove(worldgenvillagepieceweight); + } + + return worldgenvillagepiece; + } + } + } + } + + StructureBoundingBox structureboundingbox = WorldGenVillageLight.a(worldgenvillagestartpiece, list, random, i, j, k, l); + + if (structureboundingbox != null) { + return new WorldGenVillageLight(worldgenvillagestartpiece, i1, random, structureboundingbox, l); + } else { + return null; + } + } + } + + private static StructurePiece d(WorldGenVillageStartPiece worldgenvillagestartpiece, List list, Random random, int i, int j, int k, int l, int i1) { + if (i1 > 50) { + return null; + } else if (Math.abs(i - worldgenvillagestartpiece.c().a) <= 112 && Math.abs(k - worldgenvillagestartpiece.c().c) <= 112) { + WorldGenVillagePiece worldgenvillagepiece = c(worldgenvillagestartpiece, list, random, i, j, k, l, i1 + 1); + + if (worldgenvillagepiece != null) { + int j1 = (worldgenvillagepiece.f.a + worldgenvillagepiece.f.d) / 2; + int k1 = (worldgenvillagepiece.f.c + worldgenvillagepiece.f.f) / 2; + int l1 = worldgenvillagepiece.f.d - worldgenvillagepiece.f.a; + int i2 = worldgenvillagepiece.f.f - worldgenvillagepiece.f.c; + int j2 = l1 > i2 ? l1 : i2; + + if (worldgenvillagestartpiece.e().a(j1, k1, j2 / 2 + 4, WorldGenVillage.e)) { + list.add(worldgenvillagepiece); + worldgenvillagestartpiece.i.add(worldgenvillagepiece); + return worldgenvillagepiece; + } + } + + return null; + } else { + return null; + } + } + + private static StructurePiece e(WorldGenVillageStartPiece worldgenvillagestartpiece, List list, Random random, int i, int j, int k, int l, int i1) { + if (i1 > 3 + worldgenvillagestartpiece.c) { + return null; + } else if (Math.abs(i - worldgenvillagestartpiece.c().a) <= 112 && Math.abs(k - worldgenvillagestartpiece.c().c) <= 112) { + StructureBoundingBox structureboundingbox = WorldGenVillageRoad.a(worldgenvillagestartpiece, list, random, i, j, k, l); + + if (structureboundingbox != null && structureboundingbox.b > 10) { + WorldGenVillageRoad worldgenvillageroad = new WorldGenVillageRoad(worldgenvillagestartpiece, i1, random, structureboundingbox, l); + int j1 = (worldgenvillageroad.f.a + worldgenvillageroad.f.d) / 2; + int k1 = (worldgenvillageroad.f.c + worldgenvillageroad.f.f) / 2; + int l1 = worldgenvillageroad.f.d - worldgenvillageroad.f.a; + int i2 = worldgenvillageroad.f.f - worldgenvillageroad.f.c; + int j2 = l1 > i2 ? l1 : i2; + + if (worldgenvillagestartpiece.e().a(j1, k1, j2 / 2 + 4, WorldGenVillage.e)) { + list.add(worldgenvillageroad); + worldgenvillagestartpiece.j.add(worldgenvillageroad); + return worldgenvillageroad; + } + } + + return null; + } else { + return null; + } + } + + static StructurePiece a(WorldGenVillageStartPiece worldgenvillagestartpiece, List list, Random random, int i, int j, int k, int l, int i1) { + return d(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } + + static StructurePiece b(WorldGenVillageStartPiece worldgenvillagestartpiece, List list, Random random, int i, int j, int k, int l, int i1) { + return e(worldgenvillagestartpiece, list, random, i, j, k, l, i1); + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldGenWitchHut.java b/vspigot-server/src/main/java/net/minecraft/server/WorldGenWitchHut.java new file mode 100644 index 0000000..ae60aeb --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldGenWitchHut.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenWitchHut extends WorldGenScatteredPiece { + + private boolean e; + + public WorldGenWitchHut() {} + + public WorldGenWitchHut(Random random, int i, int j) { + super(random, i, 64, j, 7, 5, 9); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setBoolean("Witch", this.e); + } + + protected void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + this.e = nbttagcompound.getBoolean("Witch"); + } + + public boolean a(World world, Random random, StructureBoundingBox structureboundingbox) { + if (!this.a(world, structureboundingbox, 0)) { + return false; + } else { + this.a(world, structureboundingbox, 1, 1, 1, 5, 1, 7, Blocks.WOOD, 1, Blocks.WOOD, 1, false); + this.a(world, structureboundingbox, 1, 4, 2, 5, 4, 7, Blocks.WOOD, 1, Blocks.WOOD, 1, false); + this.a(world, structureboundingbox, 2, 1, 0, 4, 1, 0, Blocks.WOOD, 1, Blocks.WOOD, 1, false); + this.a(world, structureboundingbox, 2, 2, 2, 3, 3, 2, Blocks.WOOD, 1, Blocks.WOOD, 1, false); + this.a(world, structureboundingbox, 1, 2, 3, 1, 3, 6, Blocks.WOOD, 1, Blocks.WOOD, 1, false); + this.a(world, structureboundingbox, 5, 2, 3, 5, 3, 6, Blocks.WOOD, 1, Blocks.WOOD, 1, false); + this.a(world, structureboundingbox, 2, 2, 7, 4, 3, 7, Blocks.WOOD, 1, Blocks.WOOD, 1, false); + this.a(world, structureboundingbox, 1, 0, 2, 1, 3, 2, Blocks.LOG, Blocks.LOG, false); + this.a(world, structureboundingbox, 5, 0, 2, 5, 3, 2, Blocks.LOG, Blocks.LOG, false); + this.a(world, structureboundingbox, 1, 0, 7, 1, 3, 7, Blocks.LOG, Blocks.LOG, false); + this.a(world, structureboundingbox, 5, 0, 7, 5, 3, 7, Blocks.LOG, Blocks.LOG, false); + this.a(world, Blocks.FENCE, 0, 2, 3, 2, structureboundingbox); + this.a(world, Blocks.FENCE, 0, 3, 3, 7, structureboundingbox); + this.a(world, Blocks.AIR, 0, 1, 3, 4, structureboundingbox); + this.a(world, Blocks.AIR, 0, 5, 3, 4, structureboundingbox); + this.a(world, Blocks.AIR, 0, 5, 3, 5, structureboundingbox); + this.a(world, Blocks.FLOWER_POT, 7, 1, 3, 5, structureboundingbox); + this.a(world, Blocks.WORKBENCH, 0, 3, 2, 6, structureboundingbox); + this.a(world, Blocks.CAULDRON, 0, 4, 2, 6, structureboundingbox); + this.a(world, Blocks.FENCE, 0, 1, 2, 1, structureboundingbox); + this.a(world, Blocks.FENCE, 0, 5, 2, 1, structureboundingbox); + int i = this.a(Blocks.WOOD_STAIRS, 3); + int j = this.a(Blocks.WOOD_STAIRS, 1); + int k = this.a(Blocks.WOOD_STAIRS, 0); + int l = this.a(Blocks.WOOD_STAIRS, 2); + + this.a(world, structureboundingbox, 0, 4, 1, 6, 4, 1, Blocks.SPRUCE_WOOD_STAIRS, i, Blocks.SPRUCE_WOOD_STAIRS, i, false); + this.a(world, structureboundingbox, 0, 4, 2, 0, 4, 7, Blocks.SPRUCE_WOOD_STAIRS, k, Blocks.SPRUCE_WOOD_STAIRS, k, false); + this.a(world, structureboundingbox, 6, 4, 2, 6, 4, 7, Blocks.SPRUCE_WOOD_STAIRS, j, Blocks.SPRUCE_WOOD_STAIRS, j, false); + this.a(world, structureboundingbox, 0, 4, 8, 6, 4, 8, Blocks.SPRUCE_WOOD_STAIRS, l, Blocks.SPRUCE_WOOD_STAIRS, l, false); + + int i1; + int j1; + + for (i1 = 2; i1 <= 7; i1 += 5) { + for (j1 = 1; j1 <= 5; j1 += 4) { + this.b(world, Blocks.LOG, 0, j1, -1, i1, structureboundingbox); + } + } + + if (!this.e) { + i1 = this.a(2, 5); + j1 = this.a(2); + int k1 = this.b(2, 5); + + if (structureboundingbox.b(i1, j1, k1)) { + this.e = true; + EntityWitch entitywitch = new EntityWitch(world); + + entitywitch.setPositionRotation((double) i1 + 0.5D, (double) j1, (double) k1 + 0.5D, 0.0F, 0.0F); + entitywitch.prepare((GroupDataEntity) null); + world.addEntity(entitywitch, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + } + + return true; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldManager.java b/vspigot-server/src/main/java/net/minecraft/server/WorldManager.java new file mode 100644 index 0000000..d72acf2 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldManager.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +import java.util.Iterator; + +public class WorldManager implements IWorldAccess { + + private MinecraftServer server; + public WorldServer world; // CraftBukkit - private -> public + + public WorldManager(MinecraftServer minecraftserver, WorldServer worldserver) { + this.server = minecraftserver; + this.world = worldserver; + } + + public void a(String s, double d0, double d1, double d2, double d3, double d4, double d5) {} + + public void a(Entity entity) { + this.world.getTracker().track(entity); + } + + public void b(Entity entity) { + this.world.getTracker().untrackEntity(entity); + } + + public void a(String s, double d0, double d1, double d2, float f, float f1) { + // CraftBukkit - this.world.dimension + this.world.playerMap.sendPacketNearby(null, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1)); + } + + public void a(EntityHuman entityhuman, String s, double d0, double d1, double d2, float f, float f1) { + // CraftBukkit - this.world.dimension + this.world.playerMap.sendPacketNearby((EntityPlayer) entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1)); // MineHQ + } + + public void a(int i, int j, int k, int l, int i1, int j1) {} + + public void a(int i, int j, int k) { + this.world.getPlayerChunkMap().flagDirty(i, j, k); + } + + public void b(int i, int j, int k) {} + + public void a(String s, int i, int j, int k) {} + + public void a(EntityHuman entityhuman, int i, int j, int k, int l, int i1) { + // CraftBukkit - this.world.dimension + this.world.playerMap.sendPacketNearby((EntityPlayer) entityhuman, (double) j, (double) k, (double) l, 64.0D, new PacketPlayOutWorldEvent(i, j, k, l, i1, false)); // MineHQ + } + + public void a(int i, int j, int k, int l, int i1) { + this.server.getPlayerList().sendAll(new PacketPlayOutWorldEvent(i, j, k, l, i1, true)); + } + + public void b(int i, int j, int k, int l, int i1) { + // MineHQ start - PlayerMap + Entity entity = this.world.getEntity(i); + EntityPlayer player = entity instanceof EntityPlayer ? (EntityPlayer) entity : null; + this.world.playerMap.sendPacketNearby(player, j, k, l, 32.0D, new PacketPlayOutBlockBreakAnimation(i, j, k, l, i1)); + // MineHQ end + } + + public void b() {} +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldMap.java b/vspigot-server/src/main/java/net/minecraft/server/WorldMap.java new file mode 100644 index 0000000..3254b87 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldMap.java @@ -0,0 +1,243 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +// CraftBukkit start +import java.util.UUID; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.map.CraftMapView; +// CraftBukkit end + +public class WorldMap extends PersistentBase { + + public int centerX; + public int centerZ; + public byte map; + public byte scale; + public byte[] colors = new byte[16384]; + public List f = new ArrayList(); + public Map i = new HashMap(); // Spigot + public Map decorations = new LinkedHashMap(); + + // CraftBukkit start + public final CraftMapView mapView; + private CraftServer server; + private UUID uniqueId = null; + // CraftBukkit end + + public WorldMap(String s) { + super(s); + // CraftBukkit start + mapView = new CraftMapView(this); + server = (CraftServer) org.bukkit.Bukkit.getServer(); + // CraftBukkit end + } + + public void a(NBTTagCompound nbttagcompound) { + // CraftBukkit start + byte dimension = nbttagcompound.getByte("dimension"); + + if (dimension >= 10) { + long least = nbttagcompound.getLong("UUIDLeast"); + long most = nbttagcompound.getLong("UUIDMost"); + + if (least != 0L && most != 0L) { + this.uniqueId = new UUID(most, least); + + CraftWorld world = (CraftWorld) server.getWorld(this.uniqueId); + // Check if the stored world details are correct. + if (world == null) { + /* All Maps which do not have their valid world loaded are set to a dimension which hopefully won't be reached. + This is to prevent them being corrupted with the wrong map data. */ + dimension = 127; + } else { + dimension = (byte) world.getHandle().dimension; + } + } + } + + this.map = dimension; + // CraftBukkit end + this.centerX = nbttagcompound.getInt("xCenter"); + this.centerZ = nbttagcompound.getInt("zCenter"); + this.scale = nbttagcompound.getByte("scale"); + if (this.scale < 0) { + this.scale = 0; + } + + if (this.scale > 4) { + this.scale = 4; + } + + short short1 = nbttagcompound.getShort("width"); + short short2 = nbttagcompound.getShort("height"); + + if (short1 == 128 && short2 == 128) { + this.colors = nbttagcompound.getByteArray("colors"); + } else { + byte[] abyte = nbttagcompound.getByteArray("colors"); + + this.colors = new byte[16384]; + int i = (128 - short1) / 2; + int j = (128 - short2) / 2; + + for (int k = 0; k < short2; ++k) { + int l = k + j; + + if (l >= 0 || l < 128) { + for (int i1 = 0; i1 < short1; ++i1) { + int j1 = i1 + i; + + if (j1 >= 0 || j1 < 128) { + this.colors[j1 + l * 128] = abyte[i1 + k * short1]; + } + } + } + } + } + } + + public void b(NBTTagCompound nbttagcompound) { + // CraftBukkit start + if (this.map >= 10) { + if (this.uniqueId == null) { + for (org.bukkit.World world : server.getWorlds()) { + CraftWorld cWorld = (CraftWorld) world; + if (cWorld.getHandle().dimension == this.map) { + this.uniqueId = cWorld.getUID(); + break; + } + } + } + /* Perform a second check to see if a matching world was found, this is a necessary + change incase Maps are forcefully unlinked from a World and lack a UID.*/ + if (this.uniqueId != null) { + nbttagcompound.setLong("UUIDLeast", this.uniqueId.getLeastSignificantBits()); + nbttagcompound.setLong("UUIDMost", this.uniqueId.getMostSignificantBits()); + } + } + // CraftBukkit end + nbttagcompound.setByte("dimension", this.map); + nbttagcompound.setInt("xCenter", this.centerX); + nbttagcompound.setInt("zCenter", this.centerZ); + nbttagcompound.setByte("scale", this.scale); + nbttagcompound.setShort("width", (short) 128); + nbttagcompound.setShort("height", (short) 128); + nbttagcompound.setByteArray("colors", this.colors); + } + + public void a(EntityHuman entityhuman, ItemStack itemstack) { + if (!this.i.containsKey(entityhuman)) { + WorldMapHumanTracker worldmaphumantracker = new WorldMapHumanTracker(this, entityhuman); + + this.i.put(entityhuman, worldmaphumantracker); + this.f.add(worldmaphumantracker); + } + + if (!entityhuman.inventory.c(itemstack)) { + this.decorations.remove(entityhuman.getName()); + } + + for (int i = 0; i < this.f.size(); ++i) { + WorldMapHumanTracker worldmaphumantracker1 = (WorldMapHumanTracker) this.f.get(i); + + if (!worldmaphumantracker1.trackee.dead && (worldmaphumantracker1.trackee.inventory.c(itemstack) || itemstack.A())) { + if (!itemstack.A() && worldmaphumantracker1.trackee.dimension == this.map) { + this.a(0, worldmaphumantracker1.trackee.world, worldmaphumantracker1.trackee.getName(), worldmaphumantracker1.trackee.locX, worldmaphumantracker1.trackee.locZ, (double) worldmaphumantracker1.trackee.yaw); + } + } else { + this.i.remove(worldmaphumantracker1.trackee); + this.f.remove(worldmaphumantracker1); + } + } + + if (itemstack.A()) { + this.a(1, entityhuman.world, "frame-" + itemstack.B().getId(), (double) itemstack.B().x, (double) itemstack.B().z, (double) (itemstack.B().direction * 90)); + } + } + + private void a(int i, World world, String s, double d0, double d1, double d2) { + int j = 1 << this.scale; + float f = (float) (d0 - (double) this.centerX) / (float) j; + float f1 = (float) (d1 - (double) this.centerZ) / (float) j; + byte b0 = (byte) ((int) ((double) (f * 2.0F) + 0.5D)); + byte b1 = (byte) ((int) ((double) (f1 * 2.0F) + 0.5D)); + byte b2 = 63; + byte b3; + + if (f >= (float) (-b2) && f1 >= (float) (-b2) && f <= (float) b2 && f1 <= (float) b2) { + d2 += d2 < 0.0D ? -8.0D : 8.0D; + b3 = (byte) ((int) (d2 * 16.0D / 360.0D)); + if (this.map < 0) { + int k = (int) (world.getWorldData().getDayTime() / 10L); + + b3 = (byte) (k * k * 34187121 + k * 121 >> 15 & 15); + } + } else { + if (Math.abs(f) >= 320.0F || Math.abs(f1) >= 320.0F) { + this.decorations.remove(s); + return; + } + + i = 6; + b3 = 0; + if (f <= (float) (-b2)) { + b0 = (byte) ((int) ((double) (b2 * 2) + 2.5D)); + } + + if (f1 <= (float) (-b2)) { + b1 = (byte) ((int) ((double) (b2 * 2) + 2.5D)); + } + + if (f >= (float) b2) { + b0 = (byte) (b2 * 2 + 1); + } + + if (f1 >= (float) b2) { + b1 = (byte) (b2 * 2 + 1); + } + } + + this.decorations.put(s, new WorldMapDecoration(this, (byte) i, b0, b1, b3)); + } + + public byte[] getUpdatePacket(ItemStack itemstack, World world, EntityHuman entityhuman) { + WorldMapHumanTracker worldmaphumantracker = (WorldMapHumanTracker) this.i.get(entityhuman); + + return worldmaphumantracker == null ? null : worldmaphumantracker.a(itemstack); + } + + public void flagDirty(int i, int j, int k) { + super.c(); + + for (int l = 0; l < this.f.size(); ++l) { + WorldMapHumanTracker worldmaphumantracker = (WorldMapHumanTracker) this.f.get(l); + + if (worldmaphumantracker.b[i] < 0 || worldmaphumantracker.b[i] > j) { + worldmaphumantracker.b[i] = j; + } + + if (worldmaphumantracker.c[i] < 0 || worldmaphumantracker.c[i] < k) { + worldmaphumantracker.c[i] = k; + } + } + } + + public WorldMapHumanTracker a(EntityHuman entityhuman) { + WorldMapHumanTracker worldmaphumantracker = (WorldMapHumanTracker) this.i.get(entityhuman); + + if (worldmaphumantracker == null) { + worldmaphumantracker = new WorldMapHumanTracker(this, entityhuman); + this.i.put(entityhuman, worldmaphumantracker); + this.f.add(worldmaphumantracker); + } + + return worldmaphumantracker; + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldMapHumanTracker.java b/vspigot-server/src/main/java/net/minecraft/server/WorldMapHumanTracker.java new file mode 100644 index 0000000..d22b6c9 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldMapHumanTracker.java @@ -0,0 +1,109 @@ +package net.minecraft.server; + +import java.util.Iterator; + +public class WorldMapHumanTracker { + + public final EntityHuman trackee; + public int[] b; + public int[] c; + private int f; + private int g; + private byte[] h; + public int d; + private boolean i; + final WorldMap worldMap; + + public WorldMapHumanTracker(WorldMap worldmap, EntityHuman entityhuman) { + this.worldMap = worldmap; + this.b = new int[128]; + this.c = new int[128]; + this.trackee = entityhuman; + + for (int i = 0; i < this.b.length; ++i) { + this.b[i] = 0; + this.c[i] = 127; + } + } + + public byte[] a(ItemStack itemstack) { + byte[] abyte; + + if (!this.i) { + abyte = new byte[] { (byte) 2, this.worldMap.scale}; + this.i = true; + return abyte; + } else { + int i; + int j; + + // Spigot start + boolean custom = this.worldMap.mapView.renderers.size() > 1 || !(this.worldMap.mapView.renderers.get(0) instanceof org.bukkit.craftbukkit.map.CraftMapRenderer); + org.bukkit.craftbukkit.map.RenderData render = (custom) ? this.worldMap.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) trackee.getBukkitEntity()) : null; // CraftBukkit + + if (--this.g < 0) { + this.g = 4; + abyte = new byte[((custom) ? render.cursors.size() : this.worldMap.decorations.size()) * 3 + 1]; // CraftBukkit + abyte[0] = 1; + i = 0; + + // CraftBukkit start + + // Spigot start + for (Iterator iterator = ((custom) ? render.cursors.iterator() : this.worldMap.decorations.values().iterator()); iterator.hasNext(); ++i) { + org.bukkit.map.MapCursor cursor = (custom) ? (org.bukkit.map.MapCursor) iterator.next() : null; + if (cursor != null && !cursor.isVisible()) continue; + WorldMapDecoration deco = (custom) ? null : (WorldMapDecoration) iterator.next(); + + abyte[i * 3 + 1] = (byte) (((custom) ? cursor.getRawType() : deco.type) << 4 | ((custom) ? cursor.getDirection() : deco.rotation) & 15); + abyte[i * 3 + 2] = (byte) ((custom) ? cursor.getX() : deco.locX); + abyte[i * 3 + 3] = (byte) ((custom) ? cursor.getY() : deco.locY); + } + // Spigot end + // CraftBukkit end + + boolean flag = !itemstack.A(); + + if (this.h != null && this.h.length == abyte.length) { + for (j = 0; j < abyte.length; ++j) { + if (abyte[j] != this.h[j]) { + flag = false; + break; + } + } + } else { + flag = false; + } + + if (!flag) { + this.h = abyte; + return abyte; + } + } + + for (int k = 0; k < 1; ++k) { + i = this.f++ * 11 % 128; + if (this.b[i] >= 0) { + int l = this.c[i] - this.b[i] + 1; + + j = this.b[i]; + byte[] abyte1 = new byte[l + 3]; + + abyte1[0] = 0; + abyte1[1] = (byte) i; + abyte1[2] = (byte) j; + + for (int i1 = 0; i1 < abyte1.length - 3; ++i1) { + abyte1[i1 + 3] = ((custom) ? render.buffer : this.worldMap.colors)[(i1 + j) * 128 + i]; + } + + this.c[i] = -1; + this.b[i] = -1; + return abyte1; + } + } + + return null; + } + } +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldNBTStorage.java b/vspigot-server/src/main/java/net/minecraft/server/WorldNBTStorage.java new file mode 100644 index 0000000..c00a855 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldNBTStorage.java @@ -0,0 +1,363 @@ +package net.minecraft.server; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.util.UUID; + +import org.bukkit.craftbukkit.entity.CraftPlayer; +// CraftBukkit end + +// Poweruser start +import net.valorhcf.PlayerDataCache; +import net.valorhcf.PlayerDataSaveJob; +import net.valorhcf.ThreadingManager; +// Poweruser end + +public class WorldNBTStorage implements IDataManager, IPlayerFileData { + + private static final Logger a = LogManager.getLogger(); + private final File baseDir; + private final File playerDir; + private final File dataDir; + private final long sessionId = MinecraftServer.ar(); + private final String f; + private UUID uuid = null; // CraftBukkit + private PlayerDataCache dataCache = new PlayerDataCache(); // Poweruser + + public WorldNBTStorage(File file1, String s, boolean flag) { + this.baseDir = new File(file1, s); + this.baseDir.mkdirs(); + this.playerDir = new File(this.baseDir, "playerdata"); + this.dataDir = new File(this.baseDir, "data"); + this.dataDir.mkdirs(); + this.f = s; + if (flag) { + this.playerDir.mkdirs(); + } + + this.h(); + + // MineHQ start - manually check lock on startup + try { + checkSession0(); + } catch (Throwable t) { + org.spigotmc.SneakyThrow.sneaky(t); + } + // MineHQ end + } + + private void h() { + try { + File file1 = new File(this.baseDir, "session.lock"); + DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(file1)); + + try { + dataoutputstream.writeLong(this.sessionId); + } finally { + dataoutputstream.close(); + } + } catch (IOException ioexception) { + ioexception.printStackTrace(); + throw new RuntimeException("Failed to check session lock for world located at " + this.baseDir + ", aborting. Stop the server and delete the session.lock in this world to prevent further issues."); // Spigot + } + } + + public File getDirectory() { + return this.baseDir; + } + + public void checkSession() throws ExceptionWorldConflict {} // CraftBukkit - throws ExceptionWorldConflict // MineHQ - we can safely do so as the server will stop upon detecting a session conflict on startup + + // MineHQ start - locally used checkSession + private void checkSession0() throws ExceptionWorldConflict { + try { + File file1 = new File(this.baseDir, "session.lock"); + DataInputStream datainputstream = new DataInputStream(new FileInputStream(file1)); + + try { + if (datainputstream.readLong() != this.sessionId) { + throw new ExceptionWorldConflict("The save for world located at " + this.baseDir + " is being accessed from another location, aborting"); // Spigot + } + } finally { + datainputstream.close(); + } + } catch (IOException ioexception) { + throw new ExceptionWorldConflict("Failed to check session lock for world located at " + this.baseDir + ", aborting. Stop the server and delete the session.lock in this world to prevent further issues."); // Spigot + } + } + // MineHQ end + + public IChunkLoader createChunkLoader(WorldProvider worldprovider) { + throw new RuntimeException("Old Chunk Storage is no longer supported."); + } + + public WorldData getWorldData() { + File file1 = new File(this.baseDir, "level.dat"); + NBTTagCompound nbttagcompound; + NBTTagCompound nbttagcompound1; + + if (file1.exists()) { + try { + nbttagcompound = NBTCompressedStreamTools.a((InputStream) (new FileInputStream(file1))); + nbttagcompound1 = nbttagcompound.getCompound("Data"); + return new WorldData(nbttagcompound1); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + file1 = new File(this.baseDir, "level.dat_old"); + if (file1.exists()) { + try { + nbttagcompound = NBTCompressedStreamTools.a((InputStream) (new FileInputStream(file1))); + nbttagcompound1 = nbttagcompound.getCompound("Data"); + return new WorldData(nbttagcompound1); + } catch (Exception exception1) { + exception1.printStackTrace(); + } + } + + return null; + } + + public void saveWorldData(WorldData worlddata, NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = worlddata.a(nbttagcompound); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + + nbttagcompound2.set("Data", nbttagcompound1); + + try { + File file1 = new File(this.baseDir, "level.dat_new"); + File file2 = new File(this.baseDir, "level.dat_old"); + File file3 = new File(this.baseDir, "level.dat"); + + NBTCompressedStreamTools.a(nbttagcompound2, (OutputStream) (new FileOutputStream(file1))); + if (file2.exists()) { + file2.delete(); + } + + file3.renameTo(file2); + if (file3.exists()) { + file3.delete(); + } + + file1.renameTo(file3); + if (file1.exists()) { + file1.delete(); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + public void saveWorldData(WorldData worlddata) { + NBTTagCompound nbttagcompound = worlddata.a(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.set("Data", nbttagcompound); + + try { + File file1 = new File(this.baseDir, "level.dat_new"); + File file2 = new File(this.baseDir, "level.dat_old"); + File file3 = new File(this.baseDir, "level.dat"); + + NBTCompressedStreamTools.a(nbttagcompound1, (OutputStream) (new FileOutputStream(file1))); + if (file2.exists()) { + file2.delete(); + } + + file3.renameTo(file2); + if (file3.exists()) { + file3.delete(); + } + + file1.renameTo(file3); + if (file1.exists()) { + file1.delete(); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + public void save(EntityHuman entityhuman) { + try { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + entityhuman.e(nbttagcompound); + + // Poweruser start + File file2 = new File(this.playerDir, entityhuman.getUniqueID().toString() + ".dat"); + synchronized(this.dataCache) { + this.dataCache.put(file2, nbttagcompound); + } + ThreadingManager.saveNBTPlayerDataStatic(new PlayerDataSaveJob(file2, nbttagcompound)); + // Poweruser end + } catch (Exception exception) { + a.warn("Failed to save player data for " + entityhuman.getName()); + } + } + + public NBTTagCompound load(EntityHuman entityhuman) { + NBTTagCompound nbttagcompound = null; + + try { + File file1 = new File(this.playerDir, entityhuman.getUniqueID().toString() + ".dat"); + // Spigot Start + boolean usingWrongFile = false; + + // Poweruser start + NBTTagCompound playerdata = null; + synchronized(this.dataCache) { + playerdata = this.dataCache.get(file1); + } + if ( !file1.exists() && playerdata == null) + // Poweruser end + { + file1 = new File( this.playerDir, UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + entityhuman.getName() ).getBytes( "UTF-8" ) ).toString() + ".dat"); + // Poweruser start + synchronized(this.dataCache) { + playerdata = this.dataCache.get(file1); + } + // Poweruser end + if ( file1.exists() ) + { + usingWrongFile = true; + org.bukkit.Bukkit.getServer().getLogger().warning( "Using offline mode UUID file for player " + entityhuman.getName() + " as it is the only copy we can find." ); + } + } + // Spigot End + + // Poweruser start + if (playerdata != null) { + nbttagcompound = playerdata; + } else + // Poweruser end + if (file1.exists() && file1.isFile()) { + nbttagcompound = NBTCompressedStreamTools.a((InputStream) (new FileInputStream(file1))); + } + // Spigot Start + if ( usingWrongFile ) + { + file1.renameTo( new File( file1.getPath() + ".offline-read" ) ); + } + // Spigot End + } catch (Exception exception) { + a.warn("Failed to load player data for " + entityhuman.getName()); + } + + if (nbttagcompound != null) { + // CraftBukkit start + if (entityhuman instanceof EntityPlayer) { + CraftPlayer player = (CraftPlayer) entityhuman.bukkitEntity; + // Only update first played if it is older than the one we have + long modified = new File(this.playerDir, entityhuman.getUniqueID().toString() + ".dat").lastModified(); + if (modified < player.getFirstPlayed()) { + player.setFirstPlayed(modified); + } + } + // CraftBukkit end + + entityhuman.f(nbttagcompound); + } + + return nbttagcompound; + } + + public NBTTagCompound getPlayerData(String s) { + try { + File file1 = new File(this.playerDir, s + ".dat"); + + if (file1.exists()) { + return NBTCompressedStreamTools.a((InputStream) (new FileInputStream(file1))); + } + } catch (Exception exception) { + a.warn("Failed to load player data for " + s); + } + + return null; + } + + public IPlayerFileData getPlayerFileData() { + return this; + } + + public String[] getSeenPlayers() { + String[] astring = this.playerDir.list(); + + for (int i = 0; i < astring.length; ++i) { + if (astring[i].endsWith(".dat")) { + astring[i] = astring[i].substring(0, astring[i].length() - 4); + } + } + + return astring; + } + + public void a() {} + + public File getDataFile(String s) { + return new File(this.dataDir, s + ".dat"); + } + + public String g() { + return this.f; + } + + // CraftBukkit start + public UUID getUUID() { + if (uuid != null) return uuid; + File file1 = new File(this.baseDir, "uid.dat"); + if (file1.exists()) { + DataInputStream dis = null; + try { + dis = new DataInputStream(new FileInputStream(file1)); + return uuid = new UUID(dis.readLong(), dis.readLong()); + } catch (IOException ex) { + a.warn("Failed to read " + file1 + ", generating new random UUID", ex); + } finally { + if (dis != null) { + try { + dis.close(); + } catch (IOException ex) { + // NOOP + } + } + } + } + uuid = UUID.randomUUID(); + DataOutputStream dos = null; + try { + dos = new DataOutputStream(new FileOutputStream(file1)); + dos.writeLong(uuid.getMostSignificantBits()); + dos.writeLong(uuid.getLeastSignificantBits()); + } catch (IOException ex) { + a.warn("Failed to write " + file1, ex); + } finally { + if (dos != null) { + try { + dos.close(); + } catch (IOException ex) { + // NOOP + } + } + } + return uuid; + } + + public File getPlayerDir() { + return playerDir; + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldProvider.java b/vspigot-server/src/main/java/net/minecraft/server/WorldProvider.java new file mode 100644 index 0000000..bd91565 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldProvider.java @@ -0,0 +1,99 @@ +package net.minecraft.server; + +public abstract class WorldProvider { + + public static final float[] a = new float[] { 1.0F, 0.75F, 0.5F, 0.25F, 0.0F, 0.25F, 0.5F, 0.75F}; + public World b; + public WorldType type; + public String d; + public WorldChunkManager e; + public boolean f; + public boolean g; + public float[] h = new float[16]; + public int dimension; + private float[] j = new float[4]; + + public WorldProvider() {} + + public final void a(World world) { + this.b = world; + this.type = world.getWorldData().getType(); + this.d = world.getWorldData().getGeneratorOptions(); + this.b(); + this.a(); + } + + protected void a() { + float f = 0.0F; + + for (int i = 0; i <= 15; ++i) { + float f1 = 1.0F - (float) i / 15.0F; + + this.h[i] = (1.0F - f1) / (f1 * 3.0F + 1.0F) * (1.0F - f) + f; + } + } + + protected void b() { + if (this.b.getWorldData().getType() == WorldType.FLAT) { + WorldGenFlatInfo worldgenflatinfo = WorldGenFlatInfo.a(this.b.getWorldData().getGeneratorOptions()); + + this.e = new WorldChunkManagerHell(BiomeBase.getBiome(worldgenflatinfo.a()), 0.5F); + } else { + this.e = new WorldChunkManager(this.b); + } + } + + public IChunkProvider getChunkProvider() { + return (IChunkProvider) (this.type == WorldType.FLAT ? new ChunkProviderFlat(this.b, this.b.getSeed(), this.b.getWorldData().shouldGenerateMapFeatures(), this.d) : new ChunkProviderGenerate(this.b, this.b.getSeed(), this.b.getWorldData().shouldGenerateMapFeatures())); + } + + public boolean canSpawn(int i, int j) { + return this.b.b(i, j) == Blocks.GRASS; + } + + public float a(long i, float f) { + int j = (int) (i % 24000L); + float f1 = ((float) j + f) / 24000.0F - 0.25F; + + if (f1 < 0.0F) { + ++f1; + } + + if (f1 > 1.0F) { + --f1; + } + + float f2 = f1; + + f1 = 1.0F - (float) ((Math.cos((double) f1 * 3.141592653589793D) + 1.0D) / 2.0D); + f1 = f2 + (f1 - f2) / 3.0F; + return f1; + } + + public int a(long i) { + return (int) (i / 24000L % 8L + 8L) % 8; + } + + public boolean d() { + return true; + } + + public boolean e() { + return true; + } + + public static WorldProvider byDimension(int i) { + return (WorldProvider) (i == -1 ? new WorldProviderHell() : (i == 0 ? new WorldProviderNormal() : (i == 1 ? new WorldProviderTheEnd() : null))); + } + + public ChunkCoordinates h() { + return null; + } + + public int getSeaLevel() { + return this.type == WorldType.FLAT ? 4 : 64; + } + + public abstract String getName(); + +} diff --git a/vspigot-server/src/main/java/net/minecraft/server/WorldServer.java b/vspigot-server/src/main/java/net/minecraft/server/WorldServer.java new file mode 100644 index 0000000..da78af5 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/server/WorldServer.java @@ -0,0 +1,1131 @@ +package net.minecraft.server; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.TreeSet; + +import net.minecraft.util.com.google.common.collect.Lists; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +// CraftBukkit start +import org.bukkit.WeatherType; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.craftbukkit.util.HashTreeSet; // PaperSpigot + +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.weather.LightningStrikeEvent; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; +// CraftBukkit end +import org.spigotmc.SpigotConfig; + +public class WorldServer extends World { + + private final MinecraftServer server; + public EntityTracker tracker; // CraftBukkit - private final -> public + private final PlayerChunkMap manager; + public HashTreeSet N; // PaperSpigot + public ChunkProviderServer chunkProviderServer; + public boolean savingDisabled; + private boolean O; + private int emptyTime; + private final PortalTravelAgent Q; + private final SpawnerCreature R = new SpawnerCreature(); + private BlockActionDataList[] S = new BlockActionDataList[] { new BlockActionDataList((BananaAPI) null), new BlockActionDataList((BananaAPI) null)}; + private int T; + private static final StructurePieceTreasure[] U = new StructurePieceTreasure[] { new StructurePieceTreasure(Items.STICK, 0, 1, 3, 10), new StructurePieceTreasure(Item.getItemOf(Blocks.WOOD), 0, 1, 3, 10), new StructurePieceTreasure(Item.getItemOf(Blocks.LOG), 0, 1, 3, 10), new StructurePieceTreasure(Items.STONE_AXE, 0, 1, 1, 3), new StructurePieceTreasure(Items.WOOD_AXE, 0, 1, 1, 5), new StructurePieceTreasure(Items.STONE_PICKAXE, 0, 1, 1, 3), new StructurePieceTreasure(Items.WOOD_PICKAXE, 0, 1, 1, 5), new StructurePieceTreasure(Items.APPLE, 0, 2, 3, 5), new StructurePieceTreasure(Items.BREAD, 0, 2, 3, 3), new StructurePieceTreasure(Item.getItemOf(Blocks.LOG2), 0, 1, 3, 10)}; + public List V = new ArrayList(); + private IntHashMap entitiesById; + + private boolean ticking = false; + + // CraftBukkit start + public final int dimension; + + // Add env and gen to constructor + public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, String s, int i, WorldSettings worldsettings, MethodProfiler methodprofiler, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { + super(idatamanager, s, worldsettings, WorldProvider.byDimension(env.getId()), methodprofiler, gen, env); + this.dimension = i; + this.pvpMode = minecraftserver.getPvP(); + // CraftBukkit end + this.server = minecraftserver; + this.tracker = new EntityTracker(this); + this.manager = new PlayerChunkMap(this, spigotConfig.viewDistance); // Spigot + if (this.entitiesById == null) { + this.entitiesById = new IntHashMap(); + } + + if (this.N == null) { + this.N = new HashTreeSet(); // PaperSpigot + } + + this.Q = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit + this.scoreboard = new ScoreboardServer(minecraftserver); + PersistentScoreboard persistentscoreboard = (PersistentScoreboard) this.worldMaps.get(PersistentScoreboard.class, "scoreboard"); + + if (persistentscoreboard == null) { + persistentscoreboard = new PersistentScoreboard(); + this.worldMaps.a("scoreboard", persistentscoreboard); + } + + persistentscoreboard.a(this.scoreboard); + ((ScoreboardServer) this.scoreboard).a(persistentscoreboard); + } + + public boolean checkTicking() { + if (this.ticking && this.players.isEmpty()) { + this.ticking = false; + Bukkit.getLogger().info("Not ticking world " + this.getWorld().getName() + ". Unloading spawn..."); + this.keepSpawnInMemory = false; + } else if (!this.players.isEmpty() && !this.ticking) { + this.ticking = true; + Bukkit.getLogger().info("Ticking world " + this.getWorld().getName() + ". Loading spawn..."); + this.keepSpawnInMemory = true; + } + + return this.ticking; + } + + // CraftBukkit start + @Override + public TileEntity getTileEntity(int i, int j, int k) { + TileEntity result = super.getTileEntity(i, j, k); + Block type = getType(i, j, k); + + if (type == Blocks.CHEST || type == Blocks.TRAPPED_CHEST) { // Spigot + if (!(result instanceof TileEntityChest)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.FURNACE) { + if (!(result instanceof TileEntityFurnace)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.DROPPER) { + if (!(result instanceof TileEntityDropper)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.DISPENSER) { + if (!(result instanceof TileEntityDispenser)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.JUKEBOX) { + if (!(result instanceof TileEntityRecordPlayer)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.NOTE_BLOCK) { + if (!(result instanceof TileEntityNote)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.MOB_SPAWNER) { + if (!(result instanceof TileEntityMobSpawner)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if ((type == Blocks.SIGN_POST) || (type == Blocks.WALL_SIGN)) { + if (!(result instanceof TileEntitySign)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.ENDER_CHEST) { + if (!(result instanceof TileEntityEnderChest)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.BREWING_STAND) { + if (!(result instanceof TileEntityBrewingStand)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.BEACON) { + if (!(result instanceof TileEntityBeacon)) { + result = fixTileEntity(i, j, k, type, result); + } + } else if (type == Blocks.HOPPER) { + if (!(result instanceof TileEntityHopper)) { + result = fixTileEntity(i, j, k, type, result); + } + } + + return result; + } + + private TileEntity fixTileEntity(int x, int y, int z, Block type, TileEntity found) { + this.getServer().getLogger().severe("Block at " + x + "," + y + "," + z + " is " + org.bukkit.Material.getMaterial(Block.getId(type)).toString() + " but has " + found + ". " + + "Bukkit will attempt to fix this, but there may be additional damage that we cannot recover."); + + if (type instanceof IContainer) { + TileEntity replacement = ((IContainer) type).a(this, this.getData(x, y, z)); + replacement.world = this; + this.setTileEntity(x, y, z, replacement); + return replacement; + } else { + this.getServer().getLogger().severe("Don't know how to fix for this type... Can't do anything! :("); + return found; + } + } + + private boolean canSpawn(int x, int z) { + if (this.generator != null) { + return this.generator.canSpawn(this.getWorld(), x, z); + } else { + return this.worldProvider.canSpawn(x, z); + } + } + // CraftBukkit end + + public void doTick() { + if (!SpigotConfig.disableWeatherTicking) super.doTick(); // MineHQ + if (this.getWorldData().isHardcore() && this.difficulty != EnumDifficulty.HARD) { + this.difficulty = EnumDifficulty.HARD; + } + + this.worldProvider.e.b(); + if (!SpigotConfig.disableSleepCheck && this.everyoneDeeplySleeping()) { // MineHQ + if (this.getGameRules().getBoolean("doDaylightCycle")) { + long i = this.worldData.getDayTime() + 24000L; + + this.worldData.setDayTime(i - i % 24000L); + } + + this.d(); + } + + this.methodProfiler.a("mobSpawner"); + // CraftBukkit start - Only call spawner if we have players online and the world allows for mobs or animals + long time = this.worldData.getTime(); + if (this.getGameRules().getBoolean("doMobSpawning") && (this.allowMonsters || this.allowAnimals) && (this instanceof WorldServer && this.players.size() > 0)) { + timings.mobSpawn.startTiming(); // Spigot + this.R.spawnEntities(this, this.allowMonsters && (this.ticksPerMonsterSpawns != 0 && time % this.ticksPerMonsterSpawns == 0L), this.allowAnimals && (this.ticksPerAnimalSpawns != 0 && time % this.ticksPerAnimalSpawns == 0L), this.worldData.getTime() % 400L == 0L); + timings.mobSpawn.stopTiming(); // Spigot + // CraftBukkit end + } + // CraftBukkit end + timings.doChunkUnload.startTiming(); // Spigot + this.methodProfiler.c("chunkSource"); + this.chunkProvider.unloadChunks(); + int j = this.a(1.0F); + + if (j != this.j) { + this.j = j; + } + + this.worldData.setTime(this.worldData.getTime() + 1L); + if (this.getGameRules().getBoolean("doDaylightCycle")) { + this.worldData.setDayTime(this.worldData.getDayTime() + 1L); + } + + timings.doChunkUnload.stopTiming(); // Spigot + this.methodProfiler.c("tickPending"); + timings.doTickPending.startTiming(); // Spigot + this.a(false); + timings.doTickPending.stopTiming(); // Spigot + this.methodProfiler.c("tickBlocks"); + timings.doTickTiles.startTiming(); // Spigot + if (!SpigotConfig.disableBlockTicking) this.g(); // MineHQ + timings.doTickTiles.stopTiming(); // Spigot + this.methodProfiler.c("chunkMap"); + timings.doChunkMap.startTiming(); // Spigot + this.manager.flush(); + timings.doChunkMap.stopTiming(); // Spigot + this.methodProfiler.c("village"); + timings.doVillages.startTiming(); // Spigot + // MineHQ start + if (!SpigotConfig.disableVillageTicking) { + this.villages.tick(); + this.siegeManager.a(); + } + // MineHQ end + timings.doVillages.stopTiming(); // Spigot + this.methodProfiler.c("portalForcer"); + timings.doPortalForcer.startTiming(); // Spigot + this.Q.a(this.getTime()); + timings.doPortalForcer.stopTiming(); // Spigot + this.methodProfiler.b(); + timings.doSounds.startTiming(); // Spigot + this.Z(); + timings.doSounds.stopTiming(); // Spigot + + timings.doChunkGC.startTiming(); // Spigot + this.getWorld().processChunkGC(); // CraftBukkit + timings.doChunkGC.stopTiming(); // Spigot + } + + public BiomeMeta a(EnumCreatureType enumcreaturetype, int i, int j, int k) { + List list = this.L().getMobsFor(enumcreaturetype, i, j, k); + + return list != null && !list.isEmpty() ? (BiomeMeta) WeightedRandom.a(this.random, (Collection) list) : null; + } + + public void everyoneSleeping() { + this.O = !this.players.isEmpty(); + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (!entityhuman.isSleeping() && !entityhuman.fauxSleeping) { // CraftBukkit + this.O = false; + break; + } + } + } + + protected void d() { + this.O = false; + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (entityhuman.isSleeping()) { + entityhuman.a(false, false, true); + } + } + + this.Y(); + } + + private void Y() { + // CraftBukkit start + WeatherChangeEvent weather = new WeatherChangeEvent(this.getWorld(), false); + this.getServer().getPluginManager().callEvent(weather); + + ThunderChangeEvent thunder = new ThunderChangeEvent(this.getWorld(), false); + this.getServer().getPluginManager().callEvent(thunder); + if (!weather.isCancelled()) { + this.worldData.setWeatherDuration(0); + this.worldData.setStorm(false); + } + if (!thunder.isCancelled()) { + this.worldData.setThunderDuration(0); + this.worldData.setThundering(false); + } + // CraftBukkit end + } + + public boolean everyoneDeeplySleeping() { + if (this.O && !this.isStatic) { + Iterator iterator = this.players.iterator(); + + // CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers + boolean foundActualSleepers = false; + + EntityHuman entityhuman; + + do { + if (!iterator.hasNext()) { + return foundActualSleepers; // CraftBukkit + } + + entityhuman = (EntityHuman) iterator.next(); + // CraftBukkit start + if (entityhuman.isDeeplySleeping()) { + foundActualSleepers = true; + } + } while (entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping); + // CraftBukkit end + + return false; + } else { + return false; + } + } + + protected void g() { + super.g(); + int i = 0; + int j = 0; + // CraftBukkit start + // Iterator iterator = this.chunkTickList.iterator(); + + this.timings.doTickTiles_tickingChunks.startTiming(); // Poweruser + // Spigot start + for (net.minecraft.util.gnu.trove.iterator.TLongShortIterator iter = chunkTickList.iterator(); iter.hasNext();) { + iter.advance(); + long chunkCoord = iter.key(); + int chunkX = World.keyToX(chunkCoord); + int chunkZ = World.keyToZ(chunkCoord); + // Spigot end + // ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next(); + int k = chunkX * 16; + int l = chunkZ * 16; + + this.methodProfiler.a("getChunk"); + this.timings.doTickTiles_tickingChunks_getChunk.startTiming(); // Poweruser + // Poweruser start + Chunk chunk = this.getChunkIfLoaded(chunkX, chunkZ); + if(chunk == null || !chunk.areNeighborsLoaded(1) || this.chunkProviderServer.unloadQueue.contains( chunkX, chunkZ )) { + iter.remove(); + continue; + } + // Poweruser end + // CraftBukkit end + + this.a(k, l, chunk); + this.timings.doTickTiles_tickingChunks_getChunk.stopTiming(); // Poweruser + this.methodProfiler.c("tickChunk"); + this.timings.doTickTiles_tickingChunks_tickChunk.startTiming(); // Poweruser + chunk.b(false); + this.timings.doTickTiles_tickingChunks_tickChunk.stopTiming(); // Poweruser + if (!chunk.areNeighborsLoaded(1)) continue; // MineHQ + this.methodProfiler.c("thunder"); + int i1; + int j1; + int k1; + int l1; + + if (this.random.nextInt(100000) == 0 && this.Q() && this.P()) { + this.k = this.k * 3 + 1013904223; + i1 = this.k >> 2; + j1 = k + (i1 & 15); + k1 = l + (i1 >> 8 & 15); + l1 = this.h(j1, k1); + if (this.isRainingAt(j1, l1, k1)) { + this.strikeLightning(new EntityLightning(this, (double) j1, (double) l1, (double) k1)); + } + } + + this.methodProfiler.c("iceandsnow"); + if (this.random.nextInt(16) == 0) { + this.timings.doTickTiles_tickingChunks_iceAndSnow.startTiming(); // Poweruser + this.k = this.k * 3 + 1013904223; + i1 = this.k >> 2; + j1 = i1 & 15; + k1 = i1 >> 8 & 15; + l1 = this.h(j1 + k, k1 + l); + if (this.s(j1 + k, l1 - 1, k1 + l)) { + // CraftBukkit start + BlockState blockState = this.getWorld().getBlockAt(j1 + k, l1 - 1, k1 + l).getState(); + blockState.setTypeId(Block.getId(Blocks.ICE)); + + BlockFormEvent iceBlockForm = new BlockFormEvent(blockState.getBlock(), blockState); + this.getServer().getPluginManager().callEvent(iceBlockForm); + if (!iceBlockForm.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } + + if (this.Q() && this.e(j1 + k, l1, k1 + l, true)) { + // CraftBukkit start + BlockState blockState = this.getWorld().getBlockAt(j1 + k, l1, k1 + l).getState(); + blockState.setTypeId(Block.getId(Blocks.SNOW)); + + BlockFormEvent snow = new BlockFormEvent(blockState.getBlock(), blockState); + this.getServer().getPluginManager().callEvent(snow); + if (!snow.isCancelled()) { + blockState.update(true); + } + // CraftBukkit end + } + + if (this.Q()) { + BiomeBase biomebase = this.getBiome(j1 + k, k1 + l); + + if (biomebase.e()) { + this.getType(j1 + k, l1 - 1, k1 + l).l(this, j1 + k, l1 - 1, k1 + l); + } + } + this.timings.doTickTiles_tickingChunks_iceAndSnow.stopTiming(); // Poweruser + } + + this.methodProfiler.c("tickBlocks"); + this.timings.doTickTiles_tickingChunks_tickBlocks.startTiming(); // Poweruser + ChunkSection[] achunksection = chunk.getSections(); + + j1 = achunksection.length; + + for (k1 = 0; k1 < j1; ++k1) { + ChunkSection chunksection = achunksection[k1]; + + if (chunksection != null && chunksection.shouldTick()) { + for (int i2 = 0; i2 < 3; ++i2) { + this.k = this.k * 3 + 1013904223; + int j2 = this.k >> 2; + int k2 = j2 & 15; + int l2 = j2 >> 8 & 15; + int i3 = j2 >> 16 & 15; + + ++j; + Block block = chunksection.getTypeId(k2, i3, l2); + + if (block.isTicking()) { + ++i; + this.growthOdds = (iter.value() < 1) ? this.modifiedOdds : 100; // Spigot - grow fast if no players are in this chunk (value = player count) + block.a(this, k2 + k, i3 + chunksection.getYPosition(), l2 + l, this.random); + } + } + } + } + this.timings.doTickTiles_tickingChunks_tickBlocks.stopTiming(); // Poweruser + + this.methodProfiler.b(); + } + // Spigot Start + if ( spigotConfig.clearChunksOnTick ) + { + chunkTickList.clear(); + } + // Spigot End + this.timings.doTickTiles_tickingChunks.stopTiming(); // Poweruser + } + + public boolean a(int i, int j, int k, Block block) { + NextTickListEntry nextticklistentry = new NextTickListEntry(i, j, k, block); + + return this.V.contains(nextticklistentry); + } + + public void a(int i, int j, int k, Block block, int l) { + this.a(i, j, k, block, l, 0); + } + + public void a(int i, int j, int k, Block block, int l, int i1) { + NextTickListEntry nextticklistentry = new NextTickListEntry(i, j, k, block); + byte b0 = 0; + + if (this.d && block.getMaterial() != Material.AIR) { + if (block.L()) { + b0 = 8; + if (this.b(nextticklistentry.a - b0, nextticklistentry.b - b0, nextticklistentry.c - b0, nextticklistentry.a + b0, nextticklistentry.b + b0, nextticklistentry.c + b0)) { + Block block1 = this.getType(nextticklistentry.a, nextticklistentry.b, nextticklistentry.c); + + if (block1.getMaterial() != Material.AIR && block1 == nextticklistentry.a()) { + block1.a(this, nextticklistentry.a, nextticklistentry.b, nextticklistentry.c, this.random); + } + } + + return; + } + + l = 1; + } + + if (this.b(i - b0, j - b0, k - b0, i + b0, j + b0, k + b0)) { + if (block.getMaterial() != Material.AIR) { + nextticklistentry.a((long) l + this.worldData.getTime()); + nextticklistentry.a(i1); + } + + if (!this.N.contains(nextticklistentry)) { // PaperSpigot + this.N.add(nextticklistentry); + } + } + } + + public void b(int i, int j, int k, Block block, int l, int i1) { + NextTickListEntry nextticklistentry = new NextTickListEntry(i, j, k, block); + + nextticklistentry.a(i1); + if (block.getMaterial() != Material.AIR) { + nextticklistentry.a((long) l + this.worldData.getTime()); + } + + if (!this.N.contains(nextticklistentry)) { // PaperSpigot + this.N.add(nextticklistentry); + } + } + + public void tickEntities() { + if (false && this.players.isEmpty()) { // CraftBukkit - this prevents entity cleanup, other issues on servers with no players + if (this.emptyTime++ >= 1200) { + return; + } + } else { + this.i(); + } + + super.tickEntities(); + spigotConfig.currentPrimedTnt = 0; // Spigot + } + + public void i() { + this.emptyTime = 0; + } + + public boolean a(boolean flag) { + int i = this.N.size(); + + if (false) { // PaperSpigot + throw new IllegalStateException("TickNextTick list out of synch"); + } else { + /* PaperSpigot start - Fix redstone lag issues + if (i > 1000) { + // CraftBukkit start - If the server has too much to process over time, try to alleviate that + if (i > 20 * 1000) { + i = i / 20; + } else { + i = 1000; + } + // CraftBukkit end + } */ + + if (i > paperSpigotConfig.tickNextTickListCap) { + i = paperSpigotConfig.tickNextTickListCap; + } + // PaperSpigot end + + this.methodProfiler.a("cleaning"); + + NextTickListEntry nextticklistentry; + + for (int j = 0; j < i; ++j) { + nextticklistentry = (NextTickListEntry) this.N.first(); + if (!flag && nextticklistentry.d > this.worldData.getTime()) { + break; + } + + this.N.remove(nextticklistentry); + this.V.add(nextticklistentry); + } + + // PaperSpigot start - Allow redstone ticks to bypass the tickNextTickListCap + if (paperSpigotConfig.tickNextTickListCapIgnoresRedstone) { + Iterator iterator = this.N.iterator(); + while (iterator.hasNext()) { + NextTickListEntry next = iterator.next(); + if (!flag && next.d > this.worldData.getTime()) { + break; + } + + if (next.a().isPowerSource() || next.a() instanceof IContainer) { + iterator.remove(); + this.V.add(next); + } + } + } + // PaperSpigot end + + this.methodProfiler.b(); + this.methodProfiler.a("ticking"); + Iterator iterator = this.V.iterator(); + + while (iterator.hasNext()) { + nextticklistentry = (NextTickListEntry) iterator.next(); + iterator.remove(); + byte b0 = 0; + + if (this.b(nextticklistentry.a - b0, nextticklistentry.b - b0, nextticklistentry.c - b0, nextticklistentry.a + b0, nextticklistentry.b + b0, nextticklistentry.c + b0)) { + Block block = this.getType(nextticklistentry.a, nextticklistentry.b, nextticklistentry.c); + + if (block.getMaterial() != Material.AIR && Block.a(block, nextticklistentry.a())) { + try { + block.a(this, nextticklistentry.a, nextticklistentry.b, nextticklistentry.c, this.random); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception while ticking a block"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being ticked"); + + int k; + + try { + k = this.getData(nextticklistentry.a, nextticklistentry.b, nextticklistentry.c); + } catch (Throwable throwable1) { + k = -1; + } + + CrashReportSystemDetails.a(crashreportsystemdetails, nextticklistentry.a, nextticklistentry.b, nextticklistentry.c, block, k); + throw new ReportedException(crashreport); + } + } + } else { + this.a(nextticklistentry.a, nextticklistentry.b, nextticklistentry.c, nextticklistentry.a(), 0); + } + } + + this.methodProfiler.b(); + this.V.clear(); + return !this.N.isEmpty(); + } + } + + private static int tookTooLongs = 0; + + public List a(Chunk chunk, boolean flag) { + + if (Bukkit.getPluginManager().getPlugin("UHC") != null) { + return null; + } + + ArrayList arraylist = null; + ChunkCoordIntPair chunkcoordintpair = chunk.l(); + int i = (chunkcoordintpair.x << 4) - 2; + int j = i + 16 + 2; + int k = (chunkcoordintpair.z << 4) - 2; + int l = k + 16 + 2; + + for (int i1 = 0; i1 < 2; ++i1) { + Iterator iterator; + + int size; + String ita; + if (i1 == 0) { + iterator = this.N.iterator(); + size = this.N.size(); + ita = "N"; + } else { + iterator = this.V.iterator(); + size = this.V.size(); + ita = "V"; + } + + long started = System.currentTimeMillis(); + + + while (iterator.hasNext() && (System.currentTimeMillis() - started < 500) && tookTooLongs < 30) { + NextTickListEntry nextticklistentry = (NextTickListEntry) iterator.next(); + + if (nextticklistentry.a >= i && nextticklistentry.a < j && nextticklistentry.c >= k && nextticklistentry.c < l) { + if (flag) { + iterator.remove(); + } + + if (arraylist == null) { + arraylist = new ArrayList(); + } + + arraylist.add(nextticklistentry); + } + } + + if (1000 <= System.currentTimeMillis() - started) { + Bukkit.getLogger().info("Saving took too long :("); + Bukkit.getLogger().info("Iterator size: " + size + ". Iterator: " + ita); + Bukkit.getLogger().info("Removing from iterator? " + flag); + tookTooLongs++; + } + } + + return arraylist; + } + + /* CraftBukkit start - We prevent spawning in general, so this butchering is not needed + public void entityJoinedWorld(Entity entity, boolean flag) { + if (!this.server.getSpawnAnimals() && (entity instanceof EntityAnimal || entity instanceof EntityWaterAnimal)) { + entity.die(); + } + + if (!this.server.getSpawnNPCs() && entity instanceof NPC) { + entity.die(); + } + + super.entityJoinedWorld(entity, flag); + } + // CraftBukkit end */ + + protected IChunkProvider j() { + IChunkLoader ichunkloader = this.dataManager.createChunkLoader(this.worldProvider); + + // CraftBukkit start + org.bukkit.craftbukkit.generator.InternalChunkGenerator gen; + + if (this.generator != null) { + gen = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, this.getSeed(), this.generator); + } else if (this.worldProvider instanceof WorldProviderHell) { + gen = new org.bukkit.craftbukkit.generator.NetherChunkGenerator(this, this.getSeed()); + } else if (this.worldProvider instanceof WorldProviderTheEnd) { + gen = new org.bukkit.craftbukkit.generator.SkyLandsChunkGenerator(this, this.getSeed()); + } else { + gen = new org.bukkit.craftbukkit.generator.NormalChunkGenerator(this, this.getSeed()); + } + + this.chunkProviderServer = new ChunkProviderServer(this, ichunkloader, gen); + // CraftBukkit end + + return this.chunkProviderServer; + } + + public List getTileEntities(int i, int j, int k, int l, int i1, int j1) { + ArrayList arraylist = new ArrayList(); + + // CraftBukkit start - Get tile entities from chunks instead of world + for (int chunkX = (i >> 4); chunkX <= ((l - 1) >> 4); chunkX++) { + for (int chunkZ = (k >> 4); chunkZ <= ((j1 - 1) >> 4); chunkZ++) { + Chunk chunk = getChunkAt(chunkX, chunkZ); + if (chunk == null) { + continue; + } + + for (Object te : chunk.tileEntities.values()) { + TileEntity tileentity = (TileEntity) te; + if ((tileentity.x >= i) && (tileentity.y >= j) && (tileentity.z >= k) && (tileentity.x < l) && (tileentity.y < i1) && (tileentity.z < j1)) { + arraylist.add(tileentity); + } + } + } + } + // CraftBukkit end + + return arraylist; + } + + public boolean a(EntityHuman entityhuman, int i, int j, int k) { + return !this.server.a(this, i, j, k, entityhuman); + } + + protected void a(WorldSettings worldsettings) { + if (this.entitiesById == null) { + this.entitiesById = new IntHashMap(); + } + + if (this.N == null) { + this.N = new HashTreeSet(); // PaperSpigot + } + + this.b(worldsettings); + super.a(worldsettings); + } + + protected void b(WorldSettings worldsettings) { + if (!this.worldProvider.e()) { + this.worldData.setSpawn(0, this.worldProvider.getSeaLevel(), 0); + } else { + this.isLoading = true; + WorldChunkManager worldchunkmanager = this.worldProvider.e; + List list = worldchunkmanager.a(); + Random random = new Random(this.getSeed()); + ChunkPosition chunkposition = worldchunkmanager.a(0, 0, 256, list, random); + int i = 0; + int j = this.worldProvider.getSeaLevel(); + int k = 0; + + // CraftBukkit start + if (this.generator != null) { + Random rand = new Random(this.getSeed()); + org.bukkit.Location spawn = this.generator.getFixedSpawnLocation(((WorldServer) this).getWorld(), rand); + + if (spawn != null) { + if (spawn.getWorld() != ((WorldServer) this).getWorld()) { + throw new IllegalStateException("Cannot set spawn point for " + this.worldData.getName() + " to be in another world (" + spawn.getWorld().getName() + ")"); + } else { + this.worldData.setSpawn(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ()); + this.isLoading = false; + return; + } + } + } + // CraftBukkit end + + if (chunkposition != null) { + i = chunkposition.x; + k = chunkposition.z; + } else { + LogManager.getLogger().warn("Unable to find spawn biome"); + } + + int l = 0; + + while (!this.canSpawn(i, k)) { // CraftBukkit - use our own canSpawn + i += random.nextInt(64) - random.nextInt(64); + k += random.nextInt(64) - random.nextInt(64); + ++l; + if (l == 1000) { + break; + } + } + + this.worldData.setSpawn(i, j, k); + this.isLoading = false; + if (worldsettings.c()) { + this.k(); + } + } + } + + protected void k() { + WorldGenBonusChest worldgenbonuschest = new WorldGenBonusChest(U, 10); + + for (int i = 0; i < 10; ++i) { + int j = this.worldData.c() + this.random.nextInt(6) - this.random.nextInt(6); + int k = this.worldData.e() + this.random.nextInt(6) - this.random.nextInt(6); + int l = this.i(j, k) + 1; + + if (worldgenbonuschest.generate(this, this.random, j, l, k)) { + break; + } + } + } + + public ChunkCoordinates getDimensionSpawn() { + return this.worldProvider.h(); + } + + // Poweruser start + public void saveOnlyLevel(boolean flag, IProgressUpdate iprogressupdate) throws ExceptionWorldConflict { + if (SpigotConfig.disableSaving) return; // MineHQ + if (this.chunkProvider.canSave()) { + if (iprogressupdate != null) { + iprogressupdate.a("Saving level"); + } + + this.a(); + } + } + + public boolean saveOnlyChunks(boolean flag, IProgressUpdate iprogressupdate) { + if (SpigotConfig.disableSaving) return true; // MineHQ + if (this.chunkProvider.canSave()) { + if (iprogressupdate != null) { + iprogressupdate.c("Saving chunks"); + } + + return this.chunkProvider.saveChunks(flag, iprogressupdate); + } + return true; + } + + public void unloadOnlyUnusedChunks(boolean flag, IProgressUpdate iprogressupdate) { + if (this.chunkProvider.canSave()) { + // CraftBukkit - ArrayList -> Collection + Collection arraylist = this.chunkProviderServer.a(); + Iterator iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + Chunk chunk = (Chunk) iterator.next(); + + if (chunk != null && !this.manager.a(chunk.locX, chunk.locZ)) { + this.chunkProviderServer.queueUnload(chunk.locX, chunk.locZ); + } + } + } + } + // Poweruser end + + public void save(boolean flag, IProgressUpdate iprogressupdate) throws ExceptionWorldConflict { // CraftBukkit - added throws + if (this.chunkProvider.canSave()) { + // Poweruser start + this.saveOnlyLevel(flag, iprogressupdate); + this.saveOnlyChunks(flag, iprogressupdate); + this.unloadOnlyUnusedChunks(flag, iprogressupdate); + // Poweruser end + } + } + + public void flushSave() { + if (SpigotConfig.disableSaving) return; // MineHQ + if (this.chunkProvider.canSave()) { + this.chunkProvider.c(); + } + } + + protected void a() throws ExceptionWorldConflict { // CraftBukkit - added throws + if (SpigotConfig.disableSaving) return; // MineHQ + this.G(); + this.dataManager.saveWorldData(this.worldData, this.server.getPlayerList().t()); + // CraftBukkit start - save worldMaps once, rather than once per shared world + if (!(this instanceof SecondaryWorldServer)) { + this.worldMaps.a(); + } + // CraftBukkit end + } + + protected void a(Entity entity) { + super.a(entity); + this.entitiesById.a(entity.getId(), entity); + Entity[] aentity = entity.at(); + + if (aentity != null) { + for (int i = 0; i < aentity.length; ++i) { + this.entitiesById.a(aentity[i].getId(), aentity[i]); + } + } + } + + protected void b(Entity entity) { + super.b(entity); + this.entitiesById.d(entity.getId()); + Entity[] aentity = entity.at(); + + if (aentity != null) { + for (int i = 0; i < aentity.length; ++i) { + this.entitiesById.d(aentity[i].getId()); + } + } + } + + public Entity getEntity(int i) { + return (Entity) this.entitiesById.get(i); + } + + public boolean strikeLightning(Entity entity) { + // CraftBukkit start + LightningStrikeEvent lightning = new LightningStrikeEvent(this.getWorld(), (org.bukkit.entity.LightningStrike) entity.getBukkitEntity()); + this.getServer().getPluginManager().callEvent(lightning); + + if (lightning.isCancelled()) { + return false; + } + + if (super.strikeLightning(entity)) { + this.server.getPlayerList().sendPacketNearby(entity.locX, entity.locY, entity.locZ, 512.0D, this.dimension, new PacketPlayOutSpawnEntityWeather(entity)); + // CraftBukkit end + return true; + } else { + return false; + } + } + + public void broadcastEntityEffect(Entity entity, byte b0) { + this.getTracker().sendPacketToEntity(entity, new PacketPlayOutEntityStatus(entity, b0)); + } + + public Explosion createExplosion(Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { + // CraftBukkit start + Explosion explosion = super.createExplosion(entity, d0, d1, d2, f, flag, flag1); + + if (explosion.wasCanceled) { + return explosion; + } + + /* Remove + explosion.a = flag; + explosion.b = flag1; + explosion.a(); + explosion.a(false); + */ + // CraftBukkit end - TODO: Check if explosions are still properly implemented + + if (!flag1) { + explosion.blocks.clear(); + } + + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (entityhuman.e(d0, d1, d2) < 4096.0D) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutExplosion(d0, d1, d2, f, explosion.blocks, (Vec3D) explosion.b().get(entityhuman))); + } + } + + return explosion; + } + + public void playBlockAction(int i, int j, int k, Block block, int l, int i1) { + BlockActionData blockactiondata = new BlockActionData(i, j, k, block, l, i1); + Iterator iterator = this.S[this.T].iterator(); + + BlockActionData blockactiondata1; + + do { + if (!iterator.hasNext()) { + this.S[this.T].add(blockactiondata); + return; + } + + blockactiondata1 = (BlockActionData) iterator.next(); + } while (!blockactiondata1.equals(blockactiondata)); + + } + + private void Z() { + while (!this.S[this.T].isEmpty()) { + int i = this.T; + + this.T ^= 1; + Iterator iterator = this.S[i].iterator(); + + while (iterator.hasNext()) { + BlockActionData blockactiondata = (BlockActionData) iterator.next(); + + if (this.a(blockactiondata)) { + // CraftBukkit - this.worldProvider.dimension -> this.dimension + this.server.getPlayerList().sendPacketNearby((double) blockactiondata.a(), (double) blockactiondata.b(), (double) blockactiondata.c(), 64.0D, this.dimension, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.b(), blockactiondata.c(), blockactiondata.f(), blockactiondata.d(), blockactiondata.e())); + } + } + + this.S[i].clear(); + } + } + + private boolean a(BlockActionData blockactiondata) { + Block block = this.getType(blockactiondata.a(), blockactiondata.b(), blockactiondata.c()); + + return block == blockactiondata.f() ? block.a(this, blockactiondata.a(), blockactiondata.b(), blockactiondata.c(), blockactiondata.d(), blockactiondata.e()) : false; + } + + public void saveLevel() { + this.dataManager.a(); + } + + protected void o() { + boolean flag = this.Q(); + + super.o(); + /* CraftBukkit start + if (this.m != this.n) { + this.server.getPlayerList().a(new PacketPlayOutGameStateChange(7, this.n), this.worldProvider.dimension); + } + + if (this.o != this.p) { + this.server.getPlayerList().a(new PacketPlayOutGameStateChange(8, this.p), this.worldProvider.dimension); + } + + if (flag != this.Q()) { + if (flag) { + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(2, 0.0F)); + } else { + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(1, 0.0F)); + } + + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(7, this.n)); + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(8, this.p)); + } + // */ + if (flag != this.Q()) { + // Only send weather packets to those affected + for (int i = 0; i < this.players.size(); ++i) { + if (((EntityPlayer) this.players.get(i)).world == this) { + ((EntityPlayer) this.players.get(i)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false); + } + } + // CraftBukkit end + } + } + + protected int p() { + return this.server.getPlayerList().s(); + } + + public MinecraftServer getMinecraftServer() { + return this.server; + } + + public EntityTracker getTracker() { + return this.tracker; + } + + public PlayerChunkMap getPlayerChunkMap() { + return this.manager; + } + + public PortalTravelAgent getTravelAgent() { + return this.Q; + } + + public void a(String s, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) { + PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(s, (float) d0, (float) d1, (float) d2, (float) d3, (float) d4, (float) d5, (float) d6, i); + + for (int j = 0; j < this.players.size(); ++j) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(j); + ChunkCoordinates chunkcoordinates = entityplayer.getChunkCoordinates(); + double d7 = d0 - (double) chunkcoordinates.x; + double d8 = d1 - (double) chunkcoordinates.y; + double d9 = d2 - (double) chunkcoordinates.z; + double d10 = d7 * d7 + d8 * d8 + d9 * d9; + + if (d10 <= 256.0D) { + entityplayer.playerConnection.sendPacket(packetplayoutworldparticles); + } + } + } + + // CraftBukkit start - Helper method + public int getTypeId(int x, int y, int z) { + return Block.getId(getType(x, y, z)); + } + // CraftBukkit end +} diff --git a/vspigot-server/src/main/java/net/minecraft/util/com/mojang/util/QueueLogAppender.java b/vspigot-server/src/main/java/net/minecraft/util/com/mojang/util/QueueLogAppender.java new file mode 100644 index 0000000..e6ed90e --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/util/com/mojang/util/QueueLogAppender.java @@ -0,0 +1,84 @@ +package net.minecraft.util.com.mojang.util; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.core.pattern.RegexReplacement; + +@Plugin( + name = "Queue", + category = "Core", + elementType = "appender", + printObject = true +) +public class QueueLogAppender extends AbstractAppender { + private static final int MAX_CAPACITY = 250; + private static final Map> QUEUES = new HashMap(); + private static final ReadWriteLock QUEUE_LOCK = new ReentrantReadWriteLock(); + private final BlockingQueue queue; + + public QueueLogAppender(String name, Filter filter, Layout layout, boolean ignoreExceptions, BlockingQueue queue) { + super(name, filter, layout, ignoreExceptions); + this.queue = queue; + } + + public void append(LogEvent event) { + if(this.queue.size() >= 250) { + this.queue.clear(); + } + + this.queue.add(this.getLayout().toSerializable(event).toString()); + } + + @PluginFactory + public static QueueLogAppender createAppender(@PluginAttribute("name") String name, @PluginAttribute("ignoreExceptions") String ignore, @PluginElement("Layout") Layout layout, @PluginElement("Filters") Filter filter) { + boolean ignoreExceptions = Boolean.parseBoolean(ignore); + if(name == null) { + LOGGER.error("No name provided for QueueLogAppender"); + return null; + } else { + QUEUE_LOCK.writeLock().lock(); + BlockingQueue queue = (BlockingQueue)QUEUES.get(name); + if(queue == null) { + queue = new LinkedBlockingQueue(); + QUEUES.put(name, queue); + } + + QUEUE_LOCK.writeLock().unlock(); + if(layout == null) { + layout = PatternLayout.createLayout((String)null, (Configuration)null, (RegexReplacement)null, (String)null, (String)null); + } + + return new QueueLogAppender(name, filter, (Layout)layout, ignoreExceptions, (BlockingQueue)queue); + } + } + + public static String getNextLogEvent(String queueName) { + QUEUE_LOCK.readLock().lock(); + BlockingQueue queue = (BlockingQueue)QUEUES.get(queueName); + QUEUE_LOCK.readLock().unlock(); + if(queue != null) { + try { + return (String)queue.take(); + } catch (InterruptedException var3) { + ; + } + } + + return null; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/minecraft/util/io/netty/channel/ChannelOutboundInvoker.java b/vspigot-server/src/main/java/net/minecraft/util/io/netty/channel/ChannelOutboundInvoker.java new file mode 100644 index 0000000..0079c56 --- /dev/null +++ b/vspigot-server/src/main/java/net/minecraft/util/io/netty/channel/ChannelOutboundInvoker.java @@ -0,0 +1,50 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package net.minecraft.util.io.netty.channel; + +import java.net.SocketAddress; + +interface ChannelOutboundInvoker { + ChannelFuture bind(SocketAddress var1); + + ChannelFuture connect(SocketAddress var1); + + ChannelFuture connect(SocketAddress var1, SocketAddress var2); + + ChannelFuture disconnect(); + + ChannelFuture close(); + + /** @deprecated */ + @Deprecated + ChannelFuture deregister(); + + ChannelFuture bind(SocketAddress var1, ChannelPromise var2); + + ChannelFuture connect(SocketAddress var1, ChannelPromise var2); + + ChannelFuture connect(SocketAddress var1, SocketAddress var2, ChannelPromise var3); + + ChannelFuture disconnect(ChannelPromise var1); + + ChannelFuture close(ChannelPromise var1); + + /** @deprecated */ + @Deprecated + ChannelFuture deregister(ChannelPromise var1); + + ChannelOutboundInvoker read(); + + ChannelFuture write(Object var1); + + ChannelFuture write(Object var1, ChannelPromise var2); + + ChannelOutboundInvoker flush(); + + ChannelFuture writeAndFlush(Object var1, ChannelPromise var2); + + ChannelFuture writeAndFlush(Object var1); +} diff --git a/vspigot-server/src/main/java/net/valorhcf/ChunkSectionSnapshot.java b/vspigot-server/src/main/java/net/valorhcf/ChunkSectionSnapshot.java new file mode 100644 index 0000000..a129581 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/ChunkSectionSnapshot.java @@ -0,0 +1,79 @@ +package net.valorhcf; + +import net.minecraft.server.NibbleArray; + +public class ChunkSectionSnapshot { + + private final int nonEmptyBlockCount; + private final int tickingBlockCount; + private final byte[] blockIds; + private final NibbleArray blockData; + private final NibbleArray emittedLight; + private final NibbleArray skyLight; + private final int compactId; + private final byte compactData; + private final byte compactEmitted; + private final byte compactSky; + + public ChunkSectionSnapshot(int nonEmptyBlockCount, + int tickingBlockCount, + byte[] blockIds, + NibbleArray blockData, + NibbleArray emittedLight, + NibbleArray skyLight, + int compactId, + byte compactData, + byte compactEmitted, + byte compactSky) { + this.nonEmptyBlockCount = nonEmptyBlockCount; + this.tickingBlockCount = tickingBlockCount; + this.blockIds = blockIds; + this.blockData = blockData; + this.emittedLight = emittedLight; + this.skyLight = skyLight; + this.compactId = compactId; + this.compactData = compactData; + this.compactEmitted = compactEmitted; + this.compactSky = compactSky; + } + + public final int getNonEmptyBlockCount() { + return nonEmptyBlockCount; + } + + public final int getTickingBlockCount() { + return tickingBlockCount; + } + + public final byte[] getBlockIds() { + return blockIds; + } + + public final NibbleArray getBlockData() { + return blockData; + } + + public final NibbleArray getEmittedLight() { + return emittedLight; + } + + public final NibbleArray getSkyLight() { + return skyLight; + } + + public final int getCompactId() { + return compactId; + } + + public final byte getCompactData() { + return compactData; + } + + public final byte getCompactEmitted() { + return compactEmitted; + } + + public final byte getCompactSky() { + return compactSky; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/CraftChunkSnapshot.java b/vspigot-server/src/main/java/net/valorhcf/CraftChunkSnapshot.java new file mode 100644 index 0000000..599f784 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/CraftChunkSnapshot.java @@ -0,0 +1,20 @@ +package net.valorhcf; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.NBTTagCompound; + +public class CraftChunkSnapshot implements ChunkSnapshot { + + private final ChunkSectionSnapshot[] sections = new ChunkSectionSnapshot[16]; + private final List tileEntities = new ArrayList<>(); + + public ChunkSectionSnapshot[] getSections() { + return this.sections; + } + + public List getTileEntities() { + return this.tileEntities; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/LightingUpdater.java b/vspigot-server/src/main/java/net/valorhcf/LightingUpdater.java new file mode 100644 index 0000000..a5a87e1 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/LightingUpdater.java @@ -0,0 +1,244 @@ +package net.valorhcf; + +import java.util.HashMap; +import java.util.List; + +import org.bukkit.craftbukkit.util.LongHash; + +import net.minecraft.server.Block; +import net.minecraft.server.Blocks; +import net.minecraft.server.Chunk; +import net.minecraft.server.EnumSkyBlock; +import net.minecraft.server.Facing; +import net.minecraft.server.IWorldAccess; +import net.minecraft.server.MathHelper; + +public class LightingUpdater { + + private int[] arrayI; + private HashMap chunks; + + public LightingUpdater() { + this.arrayI = new int['\u8000']; + this.chunks = new HashMap(); + } + + public boolean c(EnumSkyBlock enumskyblock, int i, int j, int k, Chunk chunk, List neighbors) { // PaperSpigot + if (chunk != null && neighbors != null) { + this.chunks.clear(); + this.chunks.put(LongHash.toLong(chunk.locX, chunk.locZ), chunk); + for(Chunk neighborchunk: neighbors) { + this.chunks.put(LongHash.toLong(neighborchunk.locX, neighborchunk.locZ), neighborchunk); + } + + int l = 0; + int i1 = 0; + + //this.methodProfiler.a("getBrightness"); + int j1 = this.b(enumskyblock, i, j, k); + int k1 = this.a(i, j, k, enumskyblock); + int l1; + int i2; + int j2; + int k2; + int l2; + int i3; + int j3; + int k3; + int l3; + + if (k1 > j1) { + arrayI[i1++] = 133152; + } else if (k1 < j1) { + arrayI[i1++] = 133152 | j1 << 18; + + while (l < i1) { + l1 = arrayI[l++]; + i2 = (l1 & 63) - 32 + i; + j2 = (l1 >> 6 & 63) - 32 + j; + k2 = (l1 >> 12 & 63) - 32 + k; + l2 = l1 >> 18 & 15; + i3 = this.b(enumskyblock, i2, j2, k2); + if (i3 == l2) { + this.b(enumskyblock, i2, j2, k2, 0); + if (l2 > 0) { + j3 = MathHelper.a(i2 - i); + l3 = MathHelper.a(j2 - j); + k3 = MathHelper.a(k2 - k); + if (j3 + l3 + k3 < 17) { + for (int i4 = 0; i4 < 6; ++i4) { + int j4 = i2 + Facing.b[i4]; + int k4 = j2 + Facing.c[i4]; + int l4 = k2 + Facing.d[i4]; + int i5 = Math.max(1, this.getType(j4, k4, l4).k()); + + i3 = this.b(enumskyblock, j4, k4, l4); + if (i3 == l2 - i5 && i1 < arrayI.length) { + arrayI[i1++] = j4 - i + 32 | k4 - j + 32 << 6 | l4 - k + 32 << 12 | l2 - i5 << 18; + } + } + } + } + } + } + + l = 0; + } + + //this.methodProfiler.b(); + //this.methodProfiler.a("checkedPosition < toCheckCount"); + + while (l < i1) { + l1 = arrayI[l++]; + i2 = (l1 & 63) - 32 + i; + j2 = (l1 >> 6 & 63) - 32 + j; + k2 = (l1 >> 12 & 63) - 32 + k; + l2 = this.b(enumskyblock, i2, j2, k2); + i3 = this.a(i2, j2, k2, enumskyblock); + if (i3 != l2) { + this.b(enumskyblock, i2, j2, k2, i3); + if (i3 > l2) { + j3 = Math.abs(i2 - i); + l3 = Math.abs(j2 - j); + k3 = Math.abs(k2 - k); + boolean flag = i1 < arrayI.length - 6; + + if (j3 + l3 + k3 < 17 && flag) { + if (this.b(enumskyblock, i2 - 1, j2, k2) < i3) { + arrayI[i1++] = i2 - 1 - i + 32 + (j2 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2 + 1, j2, k2) < i3) { + arrayI[i1++] = i2 + 1 - i + 32 + (j2 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2 - 1, k2) < i3) { + arrayI[i1++] = i2 - i + 32 + (j2 - 1 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2 + 1, k2) < i3) { + arrayI[i1++] = i2 - i + 32 + (j2 + 1 - j + 32 << 6) + (k2 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2, k2 - 1) < i3) { + arrayI[i1++] = i2 - i + 32 + (j2 - j + 32 << 6) + (k2 - 1 - k + 32 << 12); + } + + if (this.b(enumskyblock, i2, j2, k2 + 1) < i3) { + arrayI[i1++] = i2 - i + 32 + (j2 - j + 32 << 6) + (k2 + 1 - k + 32 << 12); + } + } + } + } + } + + // PaperSpigot start - Asynchronous light updates + if (chunk.world.paperSpigotConfig.useAsyncLighting) { + chunk.pendingLightUpdates.decrementAndGet(); + if (neighbors != null) { + for (Chunk neighbor : neighbors) { + neighbor.pendingLightUpdates.decrementAndGet(); + } + } + } + // PaperSpigot end + //this.methodProfiler.b(); + this.chunks.clear(); + return true; + } + return false; + } + + private void b(EnumSkyBlock enumskyblock, int i, int j, int k, int l) { + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) { + if (j >= 0) { + if (j < 256) { + Chunk chunk = this.getChunk(i >> 4, k >> 4); + if(chunk != null) { + chunk.a(enumskyblock, i & 15, j, k & 15, l); + chunk.world.m(i, j, k); + } + } + } + } + } + + private int b(EnumSkyBlock enumskyblock, int x, int y, int z) { + Chunk chunk = this.getChunk(x >> 4, z >> 4); + if (chunk != null && x >= -30000000 && z >= -30000000 && x < 30000000 && z < 30000000) { + if (y < 0) { + y = 0; + } + + if (y >= 256) { + y = 255; + } + return chunk.getBrightness(enumskyblock, x & 15, y, z & 15); + } else { + return enumskyblock.c; + } + } + + private Chunk getChunk(int x, int z) { + return this.chunks.get(LongHash.toLong(x, z)); + } + + private int a(int i, int j, int k, EnumSkyBlock enumskyblock) { + if (enumskyblock == EnumSkyBlock.SKY && this.i(i, j, k)) { + return 15; + } else { + Block block = this.getType(i, j, k); + int l = enumskyblock == EnumSkyBlock.SKY ? 0 : block.m(); + int i1 = block.k(); + + if (i1 >= 15 && block.m() > 0) { + i1 = 1; + } + + if (i1 < 1) { + i1 = 1; + } + + if (i1 >= 15) { + return 0; + } else if (l >= 14) { + return l; + } else { + for (int j1 = 0; j1 < 6; ++j1) { + int k1 = i + Facing.b[j1]; + int l1 = j + Facing.c[j1]; + int i2 = k + Facing.d[j1]; + int j2 = this.b(enumskyblock, k1, l1, i2) - i1; + + if (j2 > l) { + l = j2; + } + + if (l >= 14) { + return l; + } + } + + return l; + } + } + } + + private Block getType(int i, int j, int k) { + if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000 && j >= 0 && j < 256) { + Chunk chunk = this.getChunk(i >> 4, k >> 4); + if(chunk != null) { + return chunk.getType(i & 15, j, k & 15); + } + } + return Blocks.AIR; + } + + private boolean i(int i, int j, int k) { + Chunk chunk = this.getChunk(i >> 4, k >> 4); + if(chunk != null) { + return chunk.d(i & 15, j, k & 15); + } + return true; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/NamePriorityThreadFactory.java b/vspigot-server/src/main/java/net/valorhcf/NamePriorityThreadFactory.java new file mode 100644 index 0000000..bbff426 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/NamePriorityThreadFactory.java @@ -0,0 +1,81 @@ +package net.valorhcf; + +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +public class NamePriorityThreadFactory implements ThreadFactory { + private int priority; + private int idCounter = 0; + private String name = "mSpigotThread"; + private boolean isDaemon = false; + private Queue> createdThreadList; + + public NamePriorityThreadFactory(int priority) { + this.priority = Math.min(Math.max(priority, Thread.MIN_PRIORITY), Thread.MAX_PRIORITY); + } + + public NamePriorityThreadFactory(int priority, String name) { + this(priority); + this.name = name; + } + + public NamePriorityThreadFactory(String name) { + this(Thread.NORM_PRIORITY); + this.name = name; + } + + public NamePriorityThreadFactory setDaemon(boolean daemon) { + this.isDaemon = daemon; + return this; + } + + public NamePriorityThreadFactory setLogThreads(boolean log) { + if(log) { + if(this.createdThreadList == null) { + this.createdThreadList = new ConcurrentLinkedQueue>(); + } + } else { + this.createdThreadList = null; + } + return this; + } + + @Override + public Thread newThread(Runnable runnable) { + Thread thread = Executors.defaultThreadFactory().newThread(runnable); + thread.setPriority(this.priority); + thread.setName(this.name + "-" + String.valueOf(idCounter)); + thread.setDaemon(this.isDaemon); + if(this.createdThreadList != null) { + this.createdThreadList.add(new WeakReference(thread)); + } + idCounter++; + return thread; + } + + public int getActiveCount() { + if(this.createdThreadList != null) { + Iterator> iter = this.createdThreadList.iterator(); + int count = 0; + while(iter.hasNext()) { + WeakReference ref = iter.next(); + Thread t = ref.get(); + if(t == null) { + iter.remove(); + } else if(t.isAlive()) { + count++; + } + } + return count; + } + return -1; + } + + public Queue> getThreadList() { + return this.createdThreadList; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/PlayerDataCache.java b/vspigot-server/src/main/java/net/valorhcf/PlayerDataCache.java new file mode 100644 index 0000000..25eca74 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/PlayerDataCache.java @@ -0,0 +1,20 @@ +package net.valorhcf; + +import java.util.LinkedHashMap; +import java.util.Map; + +import net.minecraft.server.MinecraftServer; + +public class PlayerDataCache extends LinkedHashMap{ + + private static final long serialVersionUID = 5272337123874421616L; + + public PlayerDataCache() { + super(100, 0.75F, true); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return this.size() > MinecraftServer.getServer().getPlayerList().getPlayerCount(); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/PlayerDataSaveJob.java b/vspigot-server/src/main/java/net/valorhcf/PlayerDataSaveJob.java new file mode 100644 index 0000000..68ed250 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/PlayerDataSaveJob.java @@ -0,0 +1,45 @@ +package net.valorhcf; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import net.minecraft.server.NBTCompressedStreamTools; +import net.minecraft.server.NBTTagCompound; + +public class PlayerDataSaveJob implements Runnable { + + private File target; + private NBTTagCompound data; + + public PlayerDataSaveJob (File target, NBTTagCompound data) { + this.target = target; + this.data = data; + } + + @Override + public void run() { + File temp = new File(this.target.getPath() + ".tmp"); + FileOutputStream fileoutputstream = null; + try { + fileoutputstream = new FileOutputStream(temp); + NBTCompressedStreamTools.a(this.data, (OutputStream) fileoutputstream); + if (this.target.exists()) { + this.target.delete(); + } + temp.renameTo(this.target); + } catch (Exception e) { + System.out.println(e.toString()); + e.printStackTrace(); + } finally { + if(fileoutputstream != null) { + try { + fileoutputstream.close(); + } catch (IOException e) { } + } + } + this.target = null; + this.data = null; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/PlayerMap.java b/vspigot-server/src/main/java/net/valorhcf/PlayerMap.java new file mode 100644 index 0000000..dfc9322 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/PlayerMap.java @@ -0,0 +1,259 @@ +package net.valorhcf; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.function.Consumer; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MathHelper; +import net.minecraft.server.Packet; + +public class PlayerMap { + + private static final int CHUNK_BITS = 5; + private final Long2ObjectOpenHashMap> map = new Long2ObjectOpenHashMap>(); + + private static long xzToKey(long x, long z) { + return ((long) x << 32) + z - Integer.MIN_VALUE; + } + + public void add(EntityPlayer player) { + int x = MathHelper.floor(player.locX) >> CHUNK_BITS; + int z = MathHelper.floor(player.locZ) >> CHUNK_BITS; + long key = xzToKey(x, z); + List list = map.get(key); + if (list == null) { + list = new ArrayList(); + map.put(key, list); + } + list.add(player); + player.playerMapX = x; + player.playerMapZ = z; + } + + public void move(EntityPlayer player) { + int x = MathHelper.floor(player.locX) >> CHUNK_BITS; + int z = MathHelper.floor(player.locZ) >> CHUNK_BITS; + + // did we move? + if (x == player.playerMapX && z == player.playerMapZ) { + return; + } + + // do remove + long key = xzToKey(player.playerMapX, player.playerMapZ); + List list = map.get(key); + list.remove(player); + if (list.isEmpty()) { + map.remove(key); + } + + // do add + key = xzToKey(x, z); + list = map.get(key); + if (list == null) { + list = new ArrayList(); + map.put(key, list); + } + list.add(player); + player.playerMapX = x; + player.playerMapZ = z; + } + + public void remove(EntityPlayer player) { + long key = xzToKey(player.playerMapX, player.playerMapZ); + List list = map.get(key); + if (list == null) { + // player has not yet been added to this playermap, this happens when teleporting to another world during PlayerJoinEvent + return; + } + list.remove(player); + if (list.isEmpty()) { + map.remove(key); + } + } + + public void forEachNearby(double x, double y, double z, double distance, boolean useRadius, Consumer function) { + for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) { + for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) { + List players = map.get(xzToKey(chunkX, chunkZ)); + if (players != null) { + for (EntityPlayer player : players) { + if (!useRadius || player.e(x, y, z) < distance * distance) { + try { + function.accept(player); + } catch (ConcurrentModificationException ignored) {} + } + } + } + } + } + } + + public EntityPlayer getNearestPlayer(double x, double y, double z, double distance) { + double bestDistanceSqrd = -1.0; + EntityPlayer bestPlayer = null; + + for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) { + for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) { + List players = map.get(xzToKey(chunkX, chunkZ)); + if (players != null) { + for (EntityPlayer player : players) { + double playerDistSqrd = player.e(x, y, z); + if (playerDistSqrd < distance * distance && (bestDistanceSqrd == -1.0 || playerDistSqrd < bestDistanceSqrd)) { + bestDistanceSqrd = playerDistSqrd; + bestPlayer = player; + } + } + } + } + } + return bestPlayer; + } + + public boolean isPlayerNearby(double x, double y, double z, double distance, boolean respectSpawningApi) { + for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) { + for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) { + List players = map.get(xzToKey(chunkX, chunkZ)); + if (players != null) { + for (EntityPlayer player : players) { + if (player != null && !player.dead && (!respectSpawningApi || player.affectsSpawning)) { + double playerDistSqrd = player.e(x, y, z); + if (playerDistSqrd < distance * distance) { + return true; + } + } + } + } + } + } + return false; + } + + + public EntityPlayer getNearbyPlayer(double x, double y, double z, double distance, boolean respectSpawningApi) { + double bestDistanceSqrd = -1.0; + EntityPlayer bestPlayer = null; + + for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) { + for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) { + List players = map.get(xzToKey(chunkX, chunkZ)); + if (players != null) { + for (EntityPlayer player : players) { + if (player != null && !player.dead && (!respectSpawningApi || player.affectsSpawning)) { + double playerDistSqrd = player.e(x, y, z); + if (playerDistSqrd < distance * distance && (bestDistanceSqrd == -1.0 || playerDistSqrd < bestDistanceSqrd)) { + bestDistanceSqrd = playerDistSqrd; + bestPlayer = player; + } + } + } + } + } + } + + return bestPlayer; + } + + public List getNearbyPlayersIgnoreHeight(double x, double y, double z, double distance) { + List toReturn = null; + + for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) { + for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) { + List players = map.get(xzToKey(chunkX, chunkZ)); + if (players != null) { + for (EntityPlayer player : players) { + if (player != null && !player.dead && Math.abs(player.locX - x) <= distance && Math.abs(player.locZ - z) <= distance) { + if (toReturn == null) { + toReturn = Lists.newArrayList(); + } + + toReturn.add(player); + } + } + } + } + } + + return toReturn == null ? Collections.emptyList() : toReturn; + } + + public EntityPlayer getNearestAttackablePlayer(double x, double y, double z, double maxXZ, double maxY, Function visibility) { + return getNearestAttackablePlayer(x, y, z, maxXZ, maxY, visibility, null); + } + + public EntityPlayer getNearestAttackablePlayer(double x, double y, double z, double maxXZ, double maxY, Function visibility, Predicate condition) { + double bestDistanceSqrd = -1.0; + EntityPlayer bestPlayer = null; + + for (int chunkX = MathHelper.floor(x - maxXZ) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + maxXZ) >> CHUNK_BITS; chunkX++) { + for (int chunkZ = MathHelper.floor(z - maxXZ) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + maxXZ) >> CHUNK_BITS; chunkZ++) { + List players = map.get(xzToKey(chunkX, chunkZ)); + if (players != null) { + for (EntityPlayer player : players) { + if (!player.abilities.isInvulnerable && player.isAlive() && (condition == null || condition.apply(player))) { + double dx = player.locX - x; + double dz = player.locZ - z; + double playerDistSqrd = dx * dx + dz * dz; + double dy = Math.abs(player.locY - y); + double distForPlayer = maxXZ; + + if (player.isSneaking()) { + distForPlayer *= 0.8; + } + + if (visibility != null) { + Double v = visibility.apply(player); + if (v != null) { + distForPlayer *= v; + } + } + + // mojang mistake squaring maxY? + if ((maxY < 0.0 || dy < maxY * maxY) && playerDistSqrd < distForPlayer * distForPlayer && (bestDistanceSqrd == -1.0 || playerDistSqrd < bestDistanceSqrd)) { + bestDistanceSqrd = playerDistSqrd; + bestPlayer = player; + } + } + } + } + } + } + + return bestPlayer; + } + + public void sendPacketNearby(EntityPlayer source, double x, double y, double z, double distance, Packet packet) { + for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) { + for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) { + List players = map.get(xzToKey(chunkX, chunkZ)); + if (players != null) { + for (EntityPlayer player : players) { + // don't send self + if (player == source) { + continue; + } + + // bukkit visibility api + if (source != null && !player.getBukkitEntity().canSee(source.getBukkitEntity())) { + continue; + } + + double playerDistSqrd = player.e(x, y, z); + if (playerDistSqrd < distance * distance) { + player.playerConnection.sendPacket(packet); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/valorhcf/ReusableByteArray.java b/vspigot-server/src/main/java/net/valorhcf/ReusableByteArray.java new file mode 100644 index 0000000..4965e69 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/ReusableByteArray.java @@ -0,0 +1,29 @@ +package net.valorhcf; + +public class ReusableByteArray { + + private final ThreadLocal arrays; + + public ReusableByteArray(final int initialSize) { + arrays = new ThreadLocal() { + @Override + protected byte[] initialValue() { + return new byte[initialSize]; + } + }; + } + + /** + * Returns a (thread local) byte array of at least minSize + * @param minSize Minimum size of returned array + * @return byte array + */ + public byte[] get(int minSize) { + byte[] array = arrays.get(); + if (array.length < minSize) { + array = new byte[minSize]; + arrays.set(array); + } + return array; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/ThreadingManager.java b/vspigot-server/src/main/java/net/valorhcf/ThreadingManager.java new file mode 100644 index 0000000..0937c03 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/ThreadingManager.java @@ -0,0 +1,299 @@ +package net.valorhcf; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.ref.WeakReference; +import java.util.ArrayDeque; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import net.valorhcf.command.WorldStatsCommand.WorldStatsTask; +import net.valorhcf.pathsearch.PathSearchThrottlerThread; +import net.valorhcf.pathsearch.jobs.PathSearchJob; +import net.minecraft.server.NBTCompressedStreamTools; +import net.minecraft.server.NBTTagCompound; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.spigotmc.SpigotConfig; + +public class ThreadingManager { + + private final Logger log = LogManager.getLogger(); + private static ThreadingManager instance; + private PathSearchThrottlerThread pathSearchThrottler; + private ScheduledExecutorService timerService = Executors.newScheduledThreadPool(1, new NamePriorityThreadFactory(Thread.NORM_PRIORITY + 2, "mSpigot_TimerService")); + private TickCounter tickCounter = new TickCounter(); + private NamePriorityThreadFactory cachedThreadPoolFactory; + private ExecutorService cachedThreadPool; + + private ScheduledFuture tickTimerTask; + private TickTimer tickTimerObject; + private static int timerDelay = 45; + + private TaskQueueWorker nbtFiles; + private TaskQueueWorker headConversions; + + public ThreadingManager() { + instance = this; + this.pathSearchThrottler = new PathSearchThrottlerThread(2); + this.timerService.scheduleAtFixedRate(this.tickCounter, 1, 1000, TimeUnit.MILLISECONDS); + this.tickTimerObject = new TickTimer(); + this.cachedThreadPoolFactory = new NamePriorityThreadFactory(Thread.currentThread().getPriority() - 1, "mSpigot_Async-Executor").setLogThreads(true).setDaemon(true); + this.cachedThreadPool = Executors.newCachedThreadPool(this.cachedThreadPoolFactory); + this.nbtFiles = this.createTaskQueueWorker(); + this.headConversions = this.createTaskQueueWorker(); + } + + public void shutdown() { + this.pathSearchThrottler.shutdown(); + this.timerService.shutdown(); + this.cachedThreadPool.shutdown(); + while((this.nbtFiles.isActive()) && !this.cachedThreadPool.isTerminated()) { + try { + this.cachedThreadPool.awaitTermination(10, TimeUnit.SECONDS); + log.warn("mSpigot is still waiting for NBT files to be written to disk. " + this.nbtFiles.getTaskCount() + " to go..."); + } catch(InterruptedException e) {} + } + if(!this.cachedThreadPool.isTerminated()) { + this.cachedThreadPool.shutdownNow(); + try { + this.cachedThreadPool.awaitTermination(10L, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if(SpigotConfig.logRemainingAsyncThreadsDuringShutdown && this.cachedThreadPoolFactory.getActiveCount() > 0) { + log.warn("mSpigot is still waiting for " + this.cachedThreadPoolFactory.getActiveCount() + " async threads to finish."); + Queue> queue = this.cachedThreadPoolFactory.getThreadList(); + Iterator> iter = null; + if(queue != null) { + System.out.println("== List of async threads that did not terminate on their own == "); + iter = queue.iterator(); + } + while(iter != null && iter.hasNext()) { + WeakReference ref = iter.next(); + Thread t = ref.get(); + if(t == null) { + iter.remove(); + } else if (t.isAlive()) { + StackTraceElement[] e = t.getStackTrace(); + System.out.println(t.getName() + " - " + t.getState().toString()); + for(StackTraceElement et: e) { + System.out.println(et.toString()); + } + System.out.println("========================== "); + } + } + } + } + } + + public static void saveNBTPlayerDataStatic(PlayerDataSaveJob savejob) { + instance.nbtFiles.queueTask(savejob); + } + + public static void saveNBTFileStatic(NBTTagCompound compound, File file) { + instance.saveNBTFile(compound, file); + } + + public void saveNBTFile(NBTTagCompound compound, File file) { + this.nbtFiles.queueTask(new NBTFileSaver(compound, file)); + } + + private class NBTFileSaver implements Runnable { + + private NBTTagCompound compound; + private File file; + + public NBTFileSaver(NBTTagCompound compound, File file) { + this.compound = compound; + this.file = file; + } + + public void run() { + FileOutputStream fileoutputstream = null; + try { + fileoutputstream = new FileOutputStream(this.file); + NBTCompressedStreamTools.a(this.compound, (OutputStream) fileoutputstream); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if(fileoutputstream != null) { + try { + fileoutputstream.close(); + } catch (IOException e) {} + } + } + this.compound = null; + this.file = null; + } + } + + public static boolean queuePathSearch(PathSearchJob pathSearchJob) { + return instance.pathSearchThrottler.queuePathSearch(pathSearchJob); + } + + public class TickCounter implements Runnable { + + private ArrayDeque ticksPerSecond; + private AtomicInteger ticksCounter; + + public TickCounter() { + this.ticksPerSecond = new ArrayDeque(); + this.ticksCounter = new AtomicInteger(0); + } + + @Override + public void run() { + int lastCount = this.ticksCounter.getAndSet(0); + synchronized(this.ticksPerSecond) { + this.ticksPerSecond.addLast(lastCount); + if(this.ticksPerSecond.size() > 30) { + this.ticksPerSecond.removeFirst(); + } + } + } + + public void increaseTickCounter() { + this.ticksCounter.incrementAndGet(); + } + + public Integer[] getTicksPerSecond() { + synchronized(this.ticksPerSecond) { + return this.ticksPerSecond.toArray(new Integer[0]); + } + } + } + + public static TickCounter getTickCounter() { + return instance.tickCounter; + } + + public static void startTickTimerTask() { + instance.tickTimerTask = instance.timerService.schedule(instance.tickTimerObject, timerDelay, TimeUnit.MILLISECONDS); + } + + public static void cancelTimerTask(float tickTime) { + if(checkTickTime(tickTime) && instance.tickTimerTask.cancel(false)) { + instance.tickTimerObject.tickFinishedEarly(); + } + } + + private static boolean checkTickTime(float tickTime) { + if(tickTime > 45.0F) { + if(timerDelay > 40) { + timerDelay--; + } + } else { + if(timerDelay < 45) { + timerDelay++; + } + return tickTime < 40.0F; + } + return false; + } + + private class TickTimer implements Callable { + public Object call() { + this.tickIsGoingToFinishLate(); + return null; + } + + public void tickIsGoingToFinishLate() { + } + + public void tickFinishedEarly() { + } + } + + public static void addWorldStatsTask(WorldStatsTask task) { + instance.timerService.schedule(task, 2, TimeUnit.SECONDS); + } + + public static void execute(Runnable runnable) { + instance.cachedThreadPool.execute(runnable); + } + + public static Future submit(Runnable runnable) { + return instance.cachedThreadPool.submit(runnable); + } + + public static Future submit(Callable callable) { + return instance.cachedThreadPool.submit(callable); + } + + public static void queueHeadConversion(Runnable runnable) { + instance.headConversions.queueTask(runnable); + } + + public static TaskQueueWorker createTaskQueue() { + return instance.createTaskQueueWorker(); + } + + public TaskQueueWorker createTaskQueueWorker() { + return new TaskQueueWorker(this.cachedThreadPool); + } + + public class TaskQueueWorker implements Runnable { + + private ConcurrentLinkedDeque taskQueue = new ConcurrentLinkedDeque(); + private ExecutorService service; + private volatile boolean isActive = false; + + public TaskQueueWorker(ExecutorService service) { + this.service = service; + } + + @Override + public void run() { + Runnable task = null; + while(this.isActive = ((task = this.taskQueue.pollFirst()) != null)) { + try { + task.run(); + } catch (Exception e) { + log.error("Thread " + Thread.currentThread().getName() + " encountered an exception: " + e.getMessage(), e); + } + } + } + + public void queueTask(Runnable runnable) { + this.taskQueue.addLast(runnable); + if(!this.isActive) { + this.isActive = true; + this.service.execute(this); + } + } + + public boolean isActive() { + if(!this.isActive && !this.taskQueue.isEmpty()) { + this.isActive = true; + this.service.execute(this); + } + return this.isActive; + } + + public int getTaskCount() { + int count = this.taskQueue.size(); + if(this.isActive) { + count++; + } + return count; + } + } + + public static Executor getCommonThreadPool() { + return instance.cachedThreadPool; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/ValorSpigot.java b/vspigot-server/src/main/java/net/valorhcf/ValorSpigot.java new file mode 100644 index 0000000..3ca7bae --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/ValorSpigot.java @@ -0,0 +1,35 @@ +package net.valorhcf; + +import net.valorhcf.handler.MovementHandler; +import net.valorhcf.handler.PacketHandler; +import org.bukkit.Bukkit; + +import java.util.HashSet; +import java.util.Set; + +public enum ValorSpigot { + + INSTANCE; + + private Set packetHandlers = new HashSet<>(); + private Set movementHandlers = new HashSet<>(); + + public Set getPacketHandlers() { + return this.packetHandlers; + } + + public Set getMovementHandlers() { + return this.movementHandlers; + } + + public void addPacketHandler(PacketHandler handler) { + Bukkit.getLogger().info("Adding packet handler: " + handler.getClass().getPackage().getName() + "." + handler.getClass().getName()); + this.packetHandlers.add(handler); + } + + public void addMovementHandler(MovementHandler handler) { + Bukkit.getLogger().info("Adding movement handler: " + handler.getClass().getPackage().getName() + "." + handler.getClass().getName()); + this.movementHandlers.add(handler); + } + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/WeakChunkCache.java b/vspigot-server/src/main/java/net/valorhcf/WeakChunkCache.java new file mode 100644 index 0000000..26f9137 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/WeakChunkCache.java @@ -0,0 +1,113 @@ +package net.valorhcf; + +import net.minecraft.server.Block; +import net.minecraft.server.Blocks; +import net.minecraft.server.Chunk; +import net.minecraft.server.IBlockAccess; +import net.minecraft.server.TileEntity; +import net.minecraft.server.World; + +public class WeakChunkCache implements IBlockAccess { + private int lowerChunkX; + private int lowerChunkZ; + private Chunk[][] chunks; + private boolean ownArray = false; + + public WeakChunkCache(Chunk[][] chunks, int lowerChunkX, int lowerChunkZ) { + this.chunks = chunks; + this.lowerChunkX = lowerChunkX; + this.lowerChunkZ = lowerChunkZ; + } + + public WeakChunkCache(World world, int chunkX, int chunkZ, int chunkRadius) { + this(world, chunkX - chunkRadius, chunkZ - chunkRadius, chunkX + chunkRadius, chunkZ + chunkRadius); + } + + public WeakChunkCache(World world, int lowerChunkX, int lowerChunkZ, int upperChunkX, int upperChunkZ) { + this.lowerChunkX = lowerChunkX; + this.lowerChunkZ = lowerChunkZ; + + this.chunks = new Chunk[upperChunkX - this.lowerChunkX + 1][upperChunkZ - this.lowerChunkZ + 1]; + this.ownArray = true; + + for (int chunkX = this.lowerChunkX; chunkX <= upperChunkX; ++chunkX) { + for (int chunkZ = this.lowerChunkZ; chunkZ <= upperChunkZ; ++chunkZ) { + this.chunks[chunkX - this.lowerChunkX][chunkZ - this.lowerChunkZ] = world.getChunkIfLoaded(chunkX, chunkZ); + } + } + } + + public WeakChunkCache(World world, int lowerBlockX, int lowerBlockY, int lowerBlockZ, int upperBlockX, int upperBlockY, int upperBlockZ, int blockRadius) { + this(world, (lowerBlockX - blockRadius) >> 4, + (lowerBlockZ - blockRadius) >> 4, + (upperBlockX + blockRadius) >> 4, + (upperBlockZ + blockRadius) >> 4); + } + + public Block getType(int x, int y, int z) { + Block block = Blocks.AIR; + + if (y >= 0 && y < 256) { + int indexX = (x >> 4) - this.lowerChunkX; + int indexZ = (z >> 4) - this.lowerChunkZ; + + if (indexX >= 0 && indexX < this.chunks.length && indexZ >= 0 && indexZ < this.chunks[indexX].length) { + Chunk chunk = this.chunks[indexX][indexZ]; + + if (chunk != null) { + block = chunk.getType(x & 15, y, z & 15); + } + } + } + + return block; + } + + public TileEntity getTileEntity(int x, int y, int z) { + int indexX = (x >> 4) - this.lowerChunkX; + int indexZ = (z >> 4) - this.lowerChunkZ; + + Chunk chunk = this.chunks[indexX][indexZ]; + if(chunk != null) { + return chunk.e(x & 15, y, z & 15); + } + return null; + } + + public int getData(int x, int y, int z) { + if (y >= 0 && y < 256) { + int indexX = (x >> 4) - this.lowerChunkX; + int indexZ = (z >> 4) - this.lowerChunkZ; + if (indexX >= 0 && indexX < this.chunks.length && indexZ >= 0 && indexZ < this.chunks[indexX].length) { + Chunk chunk = this.chunks[indexX][indexZ]; + if(chunk != null) { + return chunk.getData(x & 15, y, z & 15); + } + } + } + return 0; + } + + public int getBlockPower(int i, int j, int k, int l) { + return this.getType(i, j, k).c(this, i, j, k, l); + } + + public boolean isLoaded(int x, int y, int z) { + int indexX = (x >> 4) - this.lowerChunkX; + int indexZ = (z >> 4) - this.lowerChunkZ; + if (indexX >= 0 && indexX < this.chunks.length && indexZ >= 0 && indexZ < this.chunks[indexX].length) { + return this.chunks[indexX][indexZ] != null; + } + return false; + } + + public void clear() { + if(this.ownArray) { + for(int i = 0; i < this.chunks.length; i++) { + for(int j = 0; j < this.chunks[i].length; j++) { + this.chunks[i][j] = null; + } + } + } + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/autosave/AutoSave.java b/vspigot-server/src/main/java/net/valorhcf/autosave/AutoSave.java new file mode 100644 index 0000000..a236f4b --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/autosave/AutoSave.java @@ -0,0 +1,195 @@ +package net.valorhcf.autosave; + +import java.util.ArrayDeque; +import java.util.Queue; + +import org.bukkit.event.world.WorldSaveEvent; +import org.spigotmc.SpigotConfig; + +import net.minecraft.server.ExceptionWorldConflict; +import net.minecraft.server.FileIOThread; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.RegionFileCache; +import net.minecraft.server.WorldServer; + +public class AutoSave { + + private AutoSaveStep step; + private Queue levelAndMapsQueue; + private Queue saveChunksQueue; + private Queue unloadChunksQueue; + private Queue eventQueue; + private long fileioStart; + private long fileioEnd; + private int regionFileCount; + private long regionFileCacheStart; + private long regionFileCacheEnd; + private int chunkQueuedCount; + + public AutoSave() { + this.levelAndMapsQueue = new ArrayDeque(); + this.saveChunksQueue = new ArrayDeque(); + this.unloadChunksQueue = new ArrayDeque(); + this.eventQueue = new ArrayDeque(); + this.reset(); + } + + public void queueWorld(WorldServer worldserver) { + this.levelAndMapsQueue.add(worldserver); + this.saveChunksQueue.add(worldserver); + this.unloadChunksQueue.add(worldserver); + this.eventQueue.add(worldserver); + } + + public void reset() { + this.levelAndMapsQueue.clear(); + this.saveChunksQueue.clear(); + this.unloadChunksQueue.clear(); + this.eventQueue.clear(); + this.step = AutoSaveStep.IDLE; + this.chunkQueuedCount = 0; + } + + private void moveToNextStep() { + this.step = AutoSaveStep.nextStep(this.step); + } + + public boolean execute() throws ExceptionWorldConflict { + WorldServer worldServer; + switch(this.step) { + default: + case IDLE: + break; + case START: + MinecraftServer.getLogger().info("[Autosave] Started .."); + for(WorldServer world: this.saveChunksQueue) { + world.getAutoSaveWorldData().setLastAutosaveTimeStamp(); + } + this.moveToNextStep(); + return this.execute(); + case SAVE_PLAYERS: + MinecraftServer.getServer().getPlayerList().savePlayers(); + this.moveToNextStep(); + break; + case SAVE_LEVEL_AND_MAPS: + worldServer = this.levelAndMapsQueue.poll(); + if(worldServer != null) { + worldServer.saveOnlyLevel(true, null); + } + + if(this.levelAndMapsQueue.isEmpty()) { + this.moveToNextStep(); + } + + break; + case SAVE_CHUNKS: + worldServer = this.saveChunksQueue.peek(); + if(worldServer != null) { + if(worldServer.saveOnlyChunks(false, null)) { + this.chunkQueuedCount += worldServer.getAutoSaveWorldData().getAutoSaveChunkCount(); + this.saveChunksQueue.poll(); + } + } + + if(this.saveChunksQueue.isEmpty()) { + this.moveToNextStep(); + } + + break; + case UNLOAD_CHUNKS: + worldServer = this.unloadChunksQueue.poll(); + if(worldServer != null) { + worldServer.unloadOnlyUnusedChunks(true, null); + } + + if(this.unloadChunksQueue.isEmpty()) { + this.moveToNextStep(); + } + + break; + case WRITE_FILES_START: + FileIOThread.a.setNoDelay(true); + this.fileioStart = System.nanoTime(); + this.moveToNextStep(); + break; + case WRITE_FILES_WAIT: + if(FileIOThread.a.isDone()) { + this.fileioEnd = System.nanoTime(); + FileIOThread.a.setNoDelay(false); + this.moveToNextStep(); + } + break; + case FIRE_WORLDSAVEEVENT: + if(SpigotConfig.autoSaveClearRegionFileCache) { + this.regionFileCount = RegionFileCache.a.size(); + this.regionFileCacheStart = System.nanoTime(); + RegionFileCache.a(); + this.regionFileCacheEnd = System.nanoTime(); + } + + if(SpigotConfig.autoSaveFireWorldSaveEvent) { + for(WorldServer worldserver: this.eventQueue) { + WorldSaveEvent event = new WorldSaveEvent(worldserver.getWorld()); + MinecraftServer.getServer().server.getPluginManager().callEvent(event); + } + } + this.moveToNextStep(); + break; + case FINISHED: + MinecraftServer.getLogger().info("[Autosave] Done. Queued " + this.chunkQueuedCount + " chunks for saving. Took " + formatLongTime(this.fileioEnd - this.fileioStart) + " seconds to write them."); + if(SpigotConfig.autoSaveClearRegionFileCache) { + MinecraftServer.getLogger().info("[Autosave] Cleared " + this.regionFileCount + " cached region files in " + formatLongTime(this.regionFileCacheEnd - this.regionFileCacheStart) + " seconds."); + } + this.reset(); + return true; + } + return false; + } + + private static String formatLongTime(long duration) { + return String.format("%.2f", ((double) (duration / 1000000L)) / 1000.0D); + } + + public boolean isActive() { + return this.step.isActiveState(); + } + + public void start() { + this.step = AutoSaveStep.START; + } + + public enum AutoSaveStep { + IDLE(false), + START(), + SAVE_PLAYERS(), + SAVE_LEVEL_AND_MAPS(), + SAVE_CHUNKS(), + UNLOAD_CHUNKS(), + WRITE_FILES_START(), + WRITE_FILES_WAIT(), + FIRE_WORLDSAVEEVENT(), + FINISHED(); + + private final boolean activeState; + + private AutoSaveStep() { + this(true); + } + + private AutoSaveStep(boolean activeState) { + this.activeState = activeState; + } + + public static AutoSaveStep nextStep(AutoSaveStep current) { + AutoSaveStep[] values = AutoSaveStep.values(); + if(current.ordinal() + 1 < values.length) { + return values[current.ordinal() + 1]; + } + return current; + } + + public boolean isActiveState() { + return this.activeState; + } + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/autosave/AutoSaveWorldData.java b/vspigot-server/src/main/java/net/valorhcf/autosave/AutoSaveWorldData.java new file mode 100644 index 0000000..9901976 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/autosave/AutoSaveWorldData.java @@ -0,0 +1,32 @@ +package net.valorhcf.autosave; + +import net.minecraft.server.World; + +public class AutoSaveWorldData { + + private long lastAutoSaveTimeStamp; + private int autoSaveChunkCount; + private final World world; + + public AutoSaveWorldData(World world) { + this.world = world; + this.setLastAutosaveTimeStamp(); + } + + public void setLastAutosaveTimeStamp() { + this.lastAutoSaveTimeStamp = this.world.worldData.getTime(); + this.autoSaveChunkCount = 0; + } + + public long getLastAutosaveTimeStamp() { + return this.lastAutoSaveTimeStamp; + } + + public void addAutoSaveChunkCount(int count) { + this.autoSaveChunkCount += count; + } + + public int getAutoSaveChunkCount() { + return this.autoSaveChunkCount; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/block/BlockPosition.java b/vspigot-server/src/main/java/net/valorhcf/block/BlockPosition.java new file mode 100644 index 0000000..6b8eb61 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/block/BlockPosition.java @@ -0,0 +1,56 @@ +package net.valorhcf.block; + +import org.bukkit.block.Block; + +public class BlockPosition { + public final int x; + public final int y; + public final int z; + + public BlockPosition fromBlock(Block block) { + return new BlockPosition(block.getX(), block.getY(), block.getZ()); + } + + public BlockPosition(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + + public int getZ() { + return this.z; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + BlockPosition that = (BlockPosition)o; + if (this.x != that.x) { + return false; + } + if (this.y != that.y) { + return false; + } + return this.z == that.z; + } + + public int hashCode() { + int result = this.x; + result = 31 * result + this.y; + result = 31 * result + this.z; + return result; + } +} + \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/valorhcf/block/BlockPositionData.java b/vspigot-server/src/main/java/net/valorhcf/block/BlockPositionData.java new file mode 100644 index 0000000..1db81c8 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/block/BlockPositionData.java @@ -0,0 +1,22 @@ +package net.valorhcf.block; + +import org.bukkit.material.MaterialData; + +public class BlockPositionData { + private final BlockPosition blockPosition; + private final MaterialData materialData; + + public BlockPositionData(BlockPosition blockPosition, MaterialData materialData) { + this.blockPosition = blockPosition; + this.materialData = materialData; + } + + public BlockPosition getBlockPosition() { + return this.blockPosition; + } + + public MaterialData getMaterialData() { + return this.materialData; + } +} + \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/valorhcf/block/ChunkPosition.java b/vspigot-server/src/main/java/net/valorhcf/block/ChunkPosition.java new file mode 100644 index 0000000..3eb3a59 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/block/ChunkPosition.java @@ -0,0 +1,39 @@ +package net.valorhcf.block; + +public class ChunkPosition +{ + private final int x; + private final int z; + + public ChunkPosition(final int x, final int z) { + this.x = x; + this.z = z; + } + + public int getX() { + return this.x; + } + + public int getZ() { + return this.z; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + final ChunkPosition that = (ChunkPosition)o; + return this.x == that.x && this.z == that.z; + } + + @Override + public int hashCode() { + int result = this.x; + result = 31 * result + this.z; + return result; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/blocks/BlockAccessCache.java b/vspigot-server/src/main/java/net/valorhcf/blocks/BlockAccessCache.java new file mode 100644 index 0000000..b899eef --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/blocks/BlockAccessCache.java @@ -0,0 +1,74 @@ +package net.valorhcf.blocks; + +import net.minecraft.server.Block; + +public class BlockAccessCache { + + private BlockCacheEntry[] array; + + public BlockAccessCache() { + this.array = new BlockCacheEntry[4096]; + for(int i = 0; i < this.array.length; i++) { + this.array[i] = new PendingBlockEntry(this); + } + } + + public Block getById(int id) { + if(id >= 0 && id < this.array.length) { + return this.array[id].getById(id); + } else if(id >= this.array.length) { + BlockCacheEntry[] arr = new BlockCacheEntry[this.array.length * 2]; + System.arraycopy(this.array, 0, arr, 0, this.array.length); + for(int i = this.array.length; i < arr.length; i++) { + arr[i] = new PendingBlockEntry(this); + } + this.array = arr; + return this.getById(id); + } + return (Block) Block.REGISTRY_BLOCKS.a(id); + } + + private interface BlockCacheEntry { + public Block getById(int id); + } + + private class FinalBlockEntry implements BlockCacheEntry { + + private Block block; + + public FinalBlockEntry(Block block) { + this.block = block; + } + + @Override + public Block getById(int id) { + return block; + } + } + + private class PendingBlockEntry implements BlockCacheEntry { + + private BlockAccessCache cache; + + public PendingBlockEntry(BlockAccessCache cache) { + this.cache = cache; + } + + @Override + public Block getById(int id) { + Object obj = Block.REGISTRY_BLOCKS.getByIdWithoutDefaulting(id); + if(obj != null) { + Block block = (Block) obj; + this.cache.updateCacheEntry(id, block); + return block; + } + return (Block) Block.REGISTRY_BLOCKS.getDefaultBlock(); + } + } + + private void updateCacheEntry(int id, Block block) { + if(id >= 0 && id < this.array.length) { + this.array[id] = new FinalBlockEntry(block); + } + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/command/KnockbackCommand.java b/vspigot-server/src/main/java/net/valorhcf/command/KnockbackCommand.java new file mode 100644 index 0000000..6e079bb --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/command/KnockbackCommand.java @@ -0,0 +1,178 @@ +package net.valorhcf.command; + +import net.valorhcf.knockback.CraftKnockback; +import net.valorhcf.knockback.Knockback; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.spigotmc.SpigotConfig; + +import java.util.Arrays; + +public class KnockbackCommand extends Command { + + public KnockbackCommand() { + super("beefwellington"); + + this.setAliases(Arrays.asList("knockback", "kb")); + this.setUsage(StringUtils.join(new String[]{ + ChatColor.BLUE + ChatColor.STRIKETHROUGH.toString() + StringUtils.repeat("-", 35), + ChatColor.RED + "/kb list", + ChatColor.RED + "/kb create ", + ChatColor.RED + "/kb delete ", + ChatColor.RED + "/kb update ", + ChatColor.RED + "/kb setglobal ", + ChatColor.RED + "/kb toggle ", + ChatColor.BLUE + ChatColor.STRIKETHROUGH.toString() + StringUtils.repeat("-", 35) + }, "\n")); + } + + @Override + public boolean execute(CommandSender sender, String alias, String[] args) { + if (!sender.isOp()) { + sender.sendMessage(ChatColor.RED + "Unknown command."); + return true; + } + + if (args.length == 0) { + sender.sendMessage(usageMessage); + return true; + } + + switch (args[0].toLowerCase()) { + case "list": { + SpigotConfig.sendKnockbackInfo(sender); + } + break; + case "toggle": { + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Invalid usage!"); + } else { + switch (args[1].toLowerCase()) { + case "hcf": { + SpigotConfig.hcf = !SpigotConfig.hcf; + + if (SpigotConfig.hcf) { + sender.sendMessage(ChatColor.GREEN + "HCF mode enabled!"); + } else { + sender.sendMessage(ChatColor.RED + "HCF mode disabled!"); + } + + SpigotConfig.saveKnockbackProfiles(); + } + break; + case "wtap": { + SpigotConfig.wtap = !SpigotConfig.wtap; + + if (SpigotConfig.wtap) { + sender.sendMessage(ChatColor.GREEN + "Auto-WTap enabled!"); + } else { + sender.sendMessage(ChatColor.RED + "Auto-WTap disabled!"); + } + + SpigotConfig.saveKnockbackProfiles(); + } + break; + default: { + sender.sendMessage(ChatColor.RED + "Invalid usage!"); + } + break; + } + } + } + break; + case "create": { + if (args.length > 1) { + String name = args[1]; + + for (Knockback profile : SpigotConfig.knockbacks) { + if (profile.getName().equalsIgnoreCase(name)) { + sender.sendMessage(ChatColor.RED + "A profile with that name already exists."); + return true; + } + } + + CraftKnockback profile = new CraftKnockback(name); + + SpigotConfig.knockbacks.add(profile); + SpigotConfig.saveKnockbackProfiles(); + + sender.sendMessage(ChatColor.GOLD + "New profile created."); + } else { + sender.sendMessage(ChatColor.RED + "Usage: /kb create "); + } + } + break; + case "delete": { + if (args.length > 1) { + final String name = args[1]; + + if (SpigotConfig.defaultKnockback.getName().equalsIgnoreCase(name)) { + sender.sendMessage(ChatColor.RED + "You can't delete the active global knockback profile."); + return true; + } else { + if (SpigotConfig.knockbacks.removeIf(profile -> profile.getName().equalsIgnoreCase(name))) { + SpigotConfig.saveKnockbackProfiles(); + sender.sendMessage(ChatColor.RED + "Deleted profile."); + } else { + sender.sendMessage(ChatColor.RED + "A profile with that name couldn't be found."); + } + + return true; + } + } else { + sender.sendMessage(ChatColor.RED + "Usage: /kb delete "); + } + } + break; + case "update": { + if (args.length == 8) { + Knockback profile = SpigotConfig.getKnockbackByName(args[1]); + + if (profile == null) { + sender.sendMessage(ChatColor.RED + "A profile with that name couldn't be found."); + return true; + } + + profile.setFriction(Double.parseDouble(args[2])); + profile.setHorizontal(Double.parseDouble(args[3])); + profile.setVertical(Double.parseDouble(args[4])); + profile.setVerticalLimit(Double.parseDouble(args[5])); + profile.setExtraHorizontal(Double.parseDouble(args[6])); + profile.setExtraVertical(Double.parseDouble(args[7])); + + SpigotConfig.saveKnockbackProfiles(); + + sender.sendMessage(ChatColor.GREEN + "Updated values."); + } else { + sender.sendMessage(ChatColor.RED + "Wrong syntax."); + } + } + break; + case "setglobal": { + if (args.length > 1) { + Knockback profile = SpigotConfig.getKnockbackByName(args[1]); + + if (profile == null) { + sender.sendMessage(ChatColor.RED + "A profile with that name couldn't be found."); + return true; + } + + SpigotConfig.defaultKnockback = profile; + SpigotConfig.saveKnockbackProfiles(); + + sender.sendMessage(ChatColor.GREEN + "Global profile set to " + profile.getName() + "."); + return true; + } + } + break; + default: { + sender.sendMessage(usageMessage); + } + } + + return true; + } + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/command/NoTrackCommand.java b/vspigot-server/src/main/java/net/valorhcf/command/NoTrackCommand.java new file mode 100644 index 0000000..12e76b2 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/command/NoTrackCommand.java @@ -0,0 +1,53 @@ +package net.valorhcf.command; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftWorld; + +import net.minecraft.server.EntityTracker; + +public class NoTrackCommand extends Command { + + public NoTrackCommand(String name) { + super(name); + this.usageMessage = "/" + name + " "; + this.description = "Adjusts a world's no track distance"; + this.setPermission("valor.command.hideplayers"); + } + + @Override + public boolean execute(CommandSender sender, String arg1, String[] args) { + if (!testPermission(sender)) { + return true; + } + + if (args != null && args.length == 2) { + String worldName = args[0]; + String newNTR = args[1]; + int trackRange = -1; + try { + trackRange = Integer.parseInt(newNTR); + } catch (NumberFormatException e) { + sender.sendMessage("'" + newNTR + "' is not a valid integer."); + return false; + } + trackRange = Math.max(trackRange, 0); + World world = Bukkit.getWorld(worldName); + if (world != null) { + CraftWorld craftworld = (CraftWorld) world; + EntityTracker entityTracker = craftworld.getHandle().getTracker(); + entityTracker.setNoTrackDistance(trackRange); + sender.sendMessage("Track distance of world '" + worldName + "' was set to " + trackRange); + } else { + sender.sendMessage("World '" + worldName + "' was not found!"); + } + return true; + } else { + sender.sendMessage("[mSpigot] Command - notrack: " + this.description + "\nUsage: " + this.usageMessage); + } + return false; + } + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/command/SetViewDistanceCommand.java b/vspigot-server/src/main/java/net/valorhcf/command/SetViewDistanceCommand.java new file mode 100644 index 0000000..5147cbe --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/command/SetViewDistanceCommand.java @@ -0,0 +1,53 @@ +package net.valorhcf.command; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftWorld; + +public class SetViewDistanceCommand extends Command { + + public SetViewDistanceCommand(String name) { + super(name); + this.usageMessage = "/" + name + " "; + this.description = "Adjusts a world's viewdistance"; + this.setPermission( "valor.command.setviewdistance" ); + } + + @Override + public boolean execute(CommandSender sender, String arg1, String[] args) { + if(!testPermission(sender)) { return true; } + + if(args != null && args.length == 2) { + String worldName = args[0]; + String newVD = args[1]; + int vd = -1; + try { + vd = Integer.parseInt(newVD); + } catch (NumberFormatException e) { + sender.sendMessage("'" + newVD + "' is not a valid integer."); + return false; + } + World world = Bukkit.getWorld(worldName); + if(world != null) { + CraftWorld craftworld = (CraftWorld) world; + int before = craftworld.getHandle().getPlayerChunkMap().getWorldViewDistance(); + craftworld.getHandle().getPlayerChunkMap().a(vd); + int after = craftworld.getHandle().getPlayerChunkMap().getWorldViewDistance(); + if(before != after) { + sender.sendMessage("The view distance of world '" + world.getName() + "' was set from " + before + " to " + after); + } else { + sender.sendMessage("The view distance of world '" + world.getName() + "' remained the same ( " + before + " )."); + } + } else { + sender.sendMessage("World '" + worldName + "' was not found!"); + } + return true; + } else { + sender.sendMessage("[mSpigot] Command - setviewdistance: " + this.description + "\nUsage: " + this.usageMessage); + } + return false; + } + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/command/TPSCommand.java b/vspigot-server/src/main/java/net/valorhcf/command/TPSCommand.java new file mode 100644 index 0000000..274b497 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/command/TPSCommand.java @@ -0,0 +1,83 @@ +package net.valorhcf.command; + +import net.valorhcf.ThreadingManager; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class TPSCommand extends Command { + + private int[] steps = new int[] { 20, 19, 18, 14, 9, 0}; + private String[] notes = new String[] { " > 20", " = 20", " = 19", ">= 15", ">= 10", ">= 1" }; + private ChatColor[] colors = new ChatColor[] {ChatColor.DARK_GREEN, ChatColor.GREEN, ChatColor.GREEN, ChatColor.YELLOW, ChatColor.GOLD, ChatColor.RED }; + private StringBuilder[] builders; + + public TPSCommand(String name) { + super(name); + this.usageMessage = "/" + name; + this.description = "Displays the servers tick rate of the last 30 seconds"; + this.setPermission( "valor.command.tpsgraph" ); + this.builders = new StringBuilder[this.steps.length]; + for(int i = 0; i < this.builders.length; i++) { + this.builders[i] = new StringBuilder(); + } + } + + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if(!testPermission(sender)) { return true; } + Integer[] array = ThreadingManager.getTickCounter().getTicksPerSecond(); + boolean runByPlayer = (sender instanceof Player); + if(array.length == 0) { + sender.sendMessage(ChatColor.GOLD + "TPS statistic: " + ChatColor.RESET + "No data available yet. Try again later"); + return true; + } + for(int i = 0; i < this.builders.length; i++) { + this.builders[i].delete(0, this.builders[i].length()); + } + + int start = array.length - 30; + for(int i = start; i < array.length; i++) { + Integer k; + if(i < 0) { + k = 0; + } else { + k = array[i]; + } + for(int j = 0; j < this.steps.length; j++) { + if(k > this.steps[j]) { + if(runByPlayer) { + this.builders[j].append(this.colors[j]); + this.builders[j].append('\u2B1B'); + this.builders[j].append(ChatColor.RESET); + } else { + this.builders[j].append("#"); + } + } else { + if(runByPlayer) { + this.builders[j].append(ChatColor.BLACK); + this.builders[j].append('\u2B1C'); + this.builders[j].append(ChatColor.RESET); + } else { + this.builders[j].append("_"); + } + } + } + } + ChatColor current = ChatColor.RED; + Integer last = array[array.length - 1]; + for(int i = 0; i < this.steps.length; i++) { + if(this.steps[i] < last) { + current = this.colors[i]; + break; + } + } + sender.sendMessage(ChatColor.GOLD + " TPS statistic (last 30 seconds) - Current TPS: " + current + last); + for(int i = 0; i < this.builders.length; i++) { + this.builders[i].append(" " + this.notes[i]); + sender.sendMessage(this.builders[i].toString()); + } + return true; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/command/WorldStatsCommand.java b/vspigot-server/src/main/java/net/valorhcf/command/WorldStatsCommand.java new file mode 100644 index 0000000..9162c82 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/command/WorldStatsCommand.java @@ -0,0 +1,220 @@ +package net.valorhcf.command; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +import net.valorhcf.ThreadingManager; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldServer; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.craftbukkit.SpigotTimings; +import org.bukkit.plugin.SimplePluginManager; +import org.spigotmc.CustomTimingsHandler; + +public class WorldStatsCommand extends Command { + + public static WorldStatsTask task = null; + + public WorldStatsCommand(String name) { + super(name); + this.usageMessage = "/worldstats"; + this.description = "Displays technically important details about the active worlds"; + this.setPermission("valor.command.worldstats"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if(!this.testPermission(sender)) { return true; } + + SimplePluginManager pm = (SimplePluginManager)Bukkit.getPluginManager(); + boolean isTimingsAlreadyOn = pm.useTimings(); + + if(task == null) { + task = new WorldStatsTask(isTimingsAlreadyOn); + ThreadingManager.addWorldStatsTask(task); + if(!isTimingsAlreadyOn) { + pm.useTimings(true); + CustomTimingsHandler.reload(); + } + } + task.addSender(sender); + return true; + } + + public class WorldStatsTask implements Runnable { + + private List senderList = new ArrayList(); + private WorldNameComparator comparator = new WorldNameComparator(); + private DecimalFormat formater = new DecimalFormat("###0.0"); + + private boolean wasTimingAlreadyOn; + + public WorldStatsTask(boolean wasTimingAlreadyOn) { + this.wasTimingAlreadyOn = wasTimingAlreadyOn; + } + + private void broadcastMessage(String msg) { + for(CommandSender sender: this.senderList) { + if(sender instanceof ConsoleCommandSender) { + sender.sendMessage(ChatColor.stripColor(msg)); + } else { + sender.sendMessage(msg); + } + } + } + + @Override + public void run() { + WorldStatsCommand.task = null; + SimplePluginManager pm = (SimplePluginManager) Bukkit.getPluginManager(); + pm.useTimings(this.wasTimingAlreadyOn); + + StringBuilder sb = new StringBuilder(); + sb.append("[mSpigot]"); + sb.append(ChatColor.GOLD); + sb.append(" WorldStats:"); + sb.append("\n"); + sb.append(ChatColor.GRAY); + sb.append("Name "); + sb.append(ChatColor.YELLOW); + sb.append("Chunks "); + sb.append(ChatColor.GREEN); + sb.append("Entites "); + sb.append(ChatColor.BLUE); + sb.append("TileEntities "); + sb.append(ChatColor.DARK_PURPLE); + sb.append("Players "); + sb.append(ChatColor.RED); + sb.append("TickTime"); + sb.append("\n"); + + List worlds = new LinkedList(); + worlds.addAll(MinecraftServer.getServer().worlds); + Collections.sort(worlds, this.comparator); + InfoHolder overall = new InfoHolder("TOTAL"); + + for(WorldServer ws: worlds) { + InfoHolder worldDetails = this.readWorldDetails(ws); + overall.add(worldDetails); + sb.append(ChatColor.GRAY); + sb.append(worldDetails.name); + sb.append(" "); + sb.append(ChatColor.YELLOW); + sb.append(worldDetails.chunks); + sb.append(ChatColor.RED); + sb.append("("); + sb.append(formatNanoToMilliseconds(ws.timings.doTick.getRecentAverage())); + sb.append(") "); + sb.append(ChatColor.GREEN); + sb.append(worldDetails.entities); + sb.append(ChatColor.RED); + sb.append("("); + sb.append(formatNanoToMilliseconds(ws.timings.entityTick.getRecentAverage())); + sb.append(" | "); + sb.append(ChatColor.DARK_PURPLE); + sb.append(formatNanoToMilliseconds(ws.timings.entityPlayerTickNormal.getRecentAverage())); + sb.append(ChatColor.RED); + sb.append(" | "); + sb.append(ChatColor.DARK_PURPLE); + sb.append(formatNanoToMilliseconds(ws.timings.entityPlayerTickOnMove.getRecentAverage())); + sb.append(ChatColor.RED); + sb.append(") "); + sb.append(ChatColor.BLUE); + //sb.append(worldDetails.tileEntities); + sb.append("--"); + sb.append(ChatColor.RED); + sb.append("("); + sb.append(formatNanoToMilliseconds(ws.timings.tileEntityTick.getRecentAverage())); + sb.append(") "); + sb.append(ChatColor.DARK_PURPLE); + sb.append(worldDetails.players); + sb.append(ChatColor.RED); + sb.append("("); + sb.append(formatNanoToMilliseconds(ws.timings.tracker.getRecentAverage())); + sb.append(") "); + sb.append("\n"); + } + + sb.append(ChatColor.DARK_AQUA); + sb.append(ChatColor.BOLD); + sb.append(overall.name); + sb.append(" "); + sb.append(ChatColor.RESET); + sb.append(ChatColor.YELLOW); + sb.append(overall.chunks); + sb.append(" "); + sb.append(ChatColor.GREEN); + sb.append(overall.entities); + sb.append(" "); + sb.append(ChatColor.BLUE); + //sb.append(overall.tileEntities); + sb.append("--"); + sb.append(" "); + sb.append(ChatColor.DARK_PURPLE); + sb.append(overall.players); + sb.append(ChatColor.RED); + sb.append(" ("); + sb.append(formatNanoToMilliseconds(SpigotTimings.serverTickTimer.getRecentAverage())); + sb.append(")"); + this.broadcastMessage(sb.toString()); + } + + public void addSender(CommandSender sender) { + if(!this.senderList.contains(sender)) { + this.senderList.add(sender); + } + } + + private String formatNanoToMilliseconds(long nano) { + long milliPlusOne = nano / 100000L; + double milli = (double) milliPlusOne / 10.0F; + return this.formater.format(milli); + } + + private class InfoHolder { + public String name; + public int chunks = 0; + public int entities = 0; + public int tileEntities = 0; + public int players = 0; + + public InfoHolder(String name) { + this.name = name; + } + + public void add(InfoHolder ih) { + this.chunks += ih.chunks; + this.entities += ih.entities; + this.tileEntities += ih.tileEntities; + this.players += ih.players; + } + } + + private class WorldNameComparator implements Comparator { + @Override + public int compare(WorldServer arg0, WorldServer arg1) { + String n1 = arg0.getWorld().getName(); + String n2 = arg1.getWorld().getName(); + return n1.compareToIgnoreCase(n2); + } + } + + private InfoHolder readWorldDetails(WorldServer ws) { + InfoHolder ih = new InfoHolder(ws.getWorld().getName()); + ih.chunks = ws.chunkProviderServer.chunks.size(); + ih.entities = ws.entityList.size(); + //ih.tileEntities = ws.tileEntityList.size(); + ih.players = ws.players.size(); + return ih; + } + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/generator/GenLayerRemoveSpawnRivers.java b/vspigot-server/src/main/java/net/valorhcf/generator/GenLayerRemoveSpawnRivers.java new file mode 100644 index 0000000..8fb24e3 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/generator/GenLayerRemoveSpawnRivers.java @@ -0,0 +1,39 @@ +package net.valorhcf.generator; + +import net.minecraft.server.GenLayer; +import net.minecraft.server.IntCache; +import net.minecraft.server.World; + +public class GenLayerRemoveSpawnRivers extends GenLayer { + + private final GenLayer parent; + private final World world; + + public GenLayerRemoveSpawnRivers(GenLayer parent, World world) { + super(1); + + this.parent = parent; + this.world = world; + } + + @Override + public int[] a(int x, int z, int w, int h) { + int radiusSqrd = world.generatorConfig.spawnBiomeRadius * world.generatorConfig.spawnBiomeRadius; + int[] in = parent.a(x, z, w, h); + int[] out = IntCache.a(w * h); + + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + int index = j * w + i; + int worldX = (x + i) << 2; + int worldZ = (z + j) << 2; + if (worldX * worldX + worldZ * worldZ < radiusSqrd) { + out[index] = -1; + } else { + out[index] = in[index]; + } + } + } + return out; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/generator/GenLayerSpawnBiome.java b/vspigot-server/src/main/java/net/valorhcf/generator/GenLayerSpawnBiome.java new file mode 100644 index 0000000..d94d42b --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/generator/GenLayerSpawnBiome.java @@ -0,0 +1,40 @@ +package net.valorhcf.generator; + +import net.minecraft.server.GenLayer; +import net.minecraft.server.IntCache; +import net.minecraft.server.World; + +public class GenLayerSpawnBiome extends GenLayer { + + private final GenLayer parent; + private final int biomeSize; + private final World world; + + public GenLayerSpawnBiome(GenLayer parent, int biomeSize, World world) { + super(1); + this.parent = parent; + this.biomeSize = biomeSize; + this.world = world; + } + + @Override + public int[] a(int x, int z, int w, int h) { + int radiusSqrd = world.generatorConfig.spawnBiomeRadius * world.generatorConfig.spawnBiomeRadius; + int[] in = parent.a(x, z, w, h); + int[] out = IntCache.a(w * h); + + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + int index = j * w + i; + int worldX = (x + i) << 2 << biomeSize; + int worldZ = (z + j) << 2 << biomeSize; + if (worldX * worldX + worldZ * worldZ < radiusSqrd) { + out[index] = world.generatorConfig.spawnBiome.id; + } else { + out[index] = in[index]; + } + } + } + return out; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/generator/GeneratorConfig.java b/vspigot-server/src/main/java/net/valorhcf/generator/GeneratorConfig.java new file mode 100644 index 0000000..474381b --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/generator/GeneratorConfig.java @@ -0,0 +1,168 @@ +package net.valorhcf.generator; + +import net.minecraft.server.BiomeBase; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class GeneratorConfig { + + private final String worldName; + private final File file; + private final YamlConfiguration conf; + + public boolean oceans; + + public boolean biomePlains; + public boolean biomeDesert; + public boolean biomeDesertHills; + public boolean biomeExtremeHills; + public boolean biomeExtremeHillsPlus; + public boolean biomeForest; + public boolean biomeForestHills; + public boolean biomeTaiga; + public boolean biomeTaigaHills; + public boolean biomeSwampland; + public boolean biomeIcePlains; + public boolean biomeIceMountains; + public boolean biomeMushroomIsland; + public boolean biomeJungle; + public boolean biomeJungleHills; + public boolean biomeBirchForest; + public boolean biomeBirchForestHills; + public boolean biomeRoofedForest; + public boolean biomeColdTaiga; + public boolean biomeColdTaigaHills; + public boolean biomeMegaTaiga; + public boolean biomeMegaTaigaHills; + public boolean biomeSavanna; + public boolean biomeSavannaPlateau; + public boolean biomeMesa; + public boolean biomeMesaPlateauF; + public boolean biomeMesaPlateau; + + public BiomeBase spawnBiome; + public int spawnBiomeRadius; + public boolean spawnBiomeRivers; + + public float cavesMultiplier; + + public float coalMultiplier; + public int coalSize; + public boolean coalMustTouchAir; + public float ironMultiplier; + public int ironSize; + public boolean ironMustTouchAir; + public float goldMultiplier; + public int goldSize; + public boolean goldMustTouchAir; + public float redstoneMultiplier; + public int redstoneSize; + public boolean redstoneMustTouchAir; + public float diamondMultiplier; + public int diamondSize; + public boolean diamondMustTouchAir; + public float lapisMultiplier; + public int lapisSize; + public boolean lapisMustTouchAir; + public float sugarCaneMultiplier; + + public GeneratorConfig(String worldName) { + this.worldName = worldName; + this.file = new File("config/generator", worldName + ".yml"); + conf = YamlConfiguration.loadConfiguration(file); + conf.options().copyDefaults(true); + + oceans = getBoolean("oceans", true); + biomePlains = getBoolean("biome.plains", true); + biomeDesert = getBoolean("biome.desert", true); + biomeDesertHills = getBoolean("biome.desert-hills", true); + biomeExtremeHills = getBoolean("biome.extreme-hills", true); + biomeExtremeHillsPlus = getBoolean("biome.extreme-hills-plus", true); + biomeForest = getBoolean("biome.forest", true); + biomeForestHills = getBoolean("biome.forest-hills", true); + biomeTaiga = getBoolean("biome.taiga", true); + biomeTaigaHills = getBoolean("biome.taiga-hills", true); + biomeSwampland = getBoolean("biome.swampland", true); + biomeIcePlains = getBoolean("biome.ice-plains", true); + biomeIceMountains = getBoolean("biome.ice-mountains", true); + biomeMushroomIsland = getBoolean("biome.mushroom-island", true); + biomeJungle = getBoolean("biome.jungle", true); + biomeJungleHills = getBoolean("biome.jungle-hills", true); + biomeBirchForest = getBoolean("biome.birch-forest", true); + biomeBirchForestHills = getBoolean("biome.birch-forest-hills", true); + biomeRoofedForest = getBoolean("biome.roofed-forest", true); + biomeColdTaiga = getBoolean("biome.cold-taiga", true); + biomeColdTaigaHills = getBoolean("biome.cold-taiga-hills", true); + biomeMegaTaiga = getBoolean("biome.mega-taiga", true); + biomeMegaTaigaHills = getBoolean("biome.mega-taiga-hills", true); + biomeSavanna = getBoolean("biome.savanna", true); + biomeSavannaPlateau = getBoolean("biome.savanna-plateau", true); + biomeMesa = getBoolean("biome.mesa", true); + biomeMesaPlateauF = getBoolean("biome.mesa-plateau-f", true); + biomeMesaPlateau = getBoolean("biome.mesa-plateau", true); + + spawnBiome = getBiome(getString("spawn.biome", "plains")); + spawnBiomeRadius = getInt("spawn.radius", 0); + spawnBiomeRivers = getBoolean("spawn.rivers", false); + + cavesMultiplier = (float) getDouble("caves.multiplier", 1.0); + + coalMultiplier = (float) getDouble("ores.coal.multiplier", 1.0); + coalSize = getInt("ores.coal.size", 16); + coalMustTouchAir = getBoolean("ores.coal.must-touch-air", false); + ironMultiplier = (float) getDouble("ores.iron.multiplier", 1.0); + ironSize = getInt("ores.iron.size", 8); + ironMustTouchAir = getBoolean("ores.iron.must-touch-air", false); + goldMultiplier = (float) getDouble("ores.gold.multiplier", 1.0); + goldSize = getInt("ores.gold.size", 8); + goldMustTouchAir = getBoolean("ores.gold.must-touch-air", false); + redstoneMultiplier = (float) getDouble("ores.redstone.multiplier", 1.0); + redstoneSize = getInt("ores.redstone.size", 7); + redstoneMustTouchAir = getBoolean("ores.redstone.must-touch-air", false); + diamondMultiplier = (float) getDouble("ores.diamond.multiplier", 1.0); + diamondSize = getInt("ores.diamond.size", 7); + diamondMustTouchAir = getBoolean("ores.diamond.must-touch-air", false); + lapisMultiplier = (float) getDouble("ores.lapis.multiplier", 1.0); + lapisSize = getInt("ores.lapis.size", 6); + lapisMustTouchAir = getBoolean("ores.lapis.must-touch-air", false); + + sugarCaneMultiplier = (float) getDouble("sugar-cane.multiplier", 1.0); + + try { + conf.save(file); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + private boolean getBoolean(String path, boolean def) { + conf.addDefault(path, def); + return conf.getBoolean(path, def); + } + + private String getString(String path, String def) { + conf.addDefault(path, def); + return conf.getString(path, def); + } + + private int getInt(String path, int def) { + conf.addDefault(path, def); + return conf.getInt(path, def); + } + + private double getDouble(String path, double def) { + conf.addDefault(path, def); + return conf.getDouble(path, def); + } + + private BiomeBase getBiome(String name) { + for (BiomeBase biome : BiomeBase.getBiomes()) { + if (biome.af.equalsIgnoreCase(name)) { + return biome; + } + } + return BiomeBase.PLAINS; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/handler/MovementHandler.java b/vspigot-server/src/main/java/net/valorhcf/handler/MovementHandler.java new file mode 100644 index 0000000..988371e --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/handler/MovementHandler.java @@ -0,0 +1,13 @@ +package net.valorhcf.handler; + +import net.minecraft.server.PacketPlayInFlying; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public interface MovementHandler { + + void handleUpdateLocation(Player player, Location to, Location from, PacketPlayInFlying packet); + + void handleUpdateRotation(Player player, Location to, Location from, PacketPlayInFlying packet); + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/handler/PacketHandler.java b/vspigot-server/src/main/java/net/valorhcf/handler/PacketHandler.java new file mode 100644 index 0000000..adeca42 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/handler/PacketHandler.java @@ -0,0 +1,16 @@ +package net.valorhcf.handler; + +import net.minecraft.server.Packet; +import net.minecraft.server.PlayerConnection; + +public interface PacketHandler { + + void handleReceivedPacket(PlayerConnection connection, Packet packet); + + void handleSentPacket(PlayerConnection connection, Packet packet); + + default boolean handleSentPacketCancellable(PlayerConnection connection, Packet packet) { + return true; + } + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/json/JsonConfig.java b/vspigot-server/src/main/java/net/valorhcf/json/JsonConfig.java new file mode 100644 index 0000000..6822aee --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/json/JsonConfig.java @@ -0,0 +1,304 @@ +package net.valorhcf.json; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class JsonConfig { + + private Gson gson; + private File file; + private HashMap map; + + public JsonConfig(File file) { + this(file, new GsonBuilder().create()); + } + + public JsonConfig(File file, Gson gson) { + this.file = file; + this.gson = gson; + this.map = new HashMap<>(); + } + + public JsonConfig load() { + ensureFileExists(); + loadToMemory(); + return this; + } + + public HashMap getMap() { + return map; + } + + public JsonConfig clear() { + map = new HashMap<>(); + return this; + } + + public void ensureFileExists() { + if (!file.exists()) { + try { + file.getParentFile().mkdirs(); + + InputStream source = getClass().getResourceAsStream( "/" + file.getName()); + byte[] buffer = new byte[source.available()]; + + source.read(buffer); + source.close(); + + OutputStream outStream = new FileOutputStream(file); + outStream.write(buffer); + outStream.flush(); + outStream.close(); + } catch (Exception e) { + throw new RuntimeException("Could not create new file", e); + } + } + + if (file.isDirectory()) { + throw new RuntimeException("Given file is a directory"); + } + } + + private void loadToMemory() { + if (!file.exists()) { + throw new RuntimeException("The config file does not exist"); + } + + try { + map = gson.fromJson(new FileReader(file), new HashMap().getClass()); + } catch (Exception e) { + throw new RuntimeException("Failed to load config to memory", e); + } + } + + public boolean contains(String path) { + String[] split = path.split("\\."); + + if (split.length == 1) { + return map.containsKey(split[0]); + } + + Object lastElement = null; + + for (int i = 0; i < split.length; i++) { + String currentPart = split[i]; + + if (lastElement == null) { + lastElement = map.get(currentPart); + + if (!(lastElement instanceof Map)) { + return false; + } + } else { + if (lastElement instanceof Map) { + Map nextEntryPoint = (Map) lastElement; + + if (i + 1 == split.length) { + return nextEntryPoint.containsKey(currentPart); + } else { + lastElement = nextEntryPoint.get(currentPart); + } + } else { + return false; + } + } + } + + return false; + } + + public void set(String path, Object value) { + String[] split = path.split("\\."); + + if (split.length == 1) { + map.put(split[0], serializeType(value)); + return; + } + + Object lastElement = null; + + for (int i = 0; i < split.length; i++) { + String currentPart = split[i]; + + if (lastElement == null) { + map.computeIfAbsent(currentPart, (k) -> new HashMap<>()); + + lastElement = map.get(currentPart); + + if (!(lastElement instanceof Map)) { + lastElement = new HashMap<>(); + map.put(currentPart, lastElement); + } + } else { + Map map = (Map) lastElement; + + if (i + 1 == split.length) { + map.put(currentPart, serializeType(value)); + } else { + map.computeIfAbsent(currentPart, (k) -> new HashMap<>()); + lastElement = map.get(currentPart); + } + } + } + } + + public Object get(String path) { + String[] split = path.split("\\."); + + if (split.length == 1) { + return map.get(split[0]); + } + + Object lastElement = null; + + for (int i = 0; i < split.length; i++) { + String currentPart = split[i]; + + if (lastElement == null) { + lastElement = map.get(currentPart); + } else { + if (lastElement instanceof Map) { + lastElement = ((Map) lastElement).get(currentPart); + } else if (lastElement instanceof List) { + lastElement = ((List) lastElement).get(Integer.parseInt(currentPart)); + } else if (lastElement.getClass().isPrimitive()) { + throw new RuntimeException("Cannot continue processing path if last element is primitive or null!"); + } + } + } + + return lastElement; + } + + public T get(String path, Class clazz) { + return clazz.cast(get(path)); + } + + public T get(String path, T def, Class clazz) { + T value = clazz.cast(get(path)); + return value == null ? def : value; + } + + public byte getByte(String path) { + return get(path, byte.class); + } + + public byte getByte(String path, byte def) { + return get(path, def, byte.class); + } + + public byte[] getByteArray(String path) { + return get(path, byte[].class); + } + + public byte[] getByteArray(String path, byte[] def) { + return get(path, def, byte[].class); + } + + public boolean getBoolean(String path) { + return get(path, boolean.class); + } + + public boolean getBoolean(String path, boolean def) { + return get(path, def, boolean.class); + } + + public short getShort(String path) { + return get(path, short.class); + } + + public short getShort(String path, short def) { + return get(path, def, short.class); + } + + public int getInt(String path) { + return get(path, Integer.class); + } + + public int getInt(String path, int def) { + return get(path, def, Integer.class); + } + + public double getDouble(String path) { + return get(path, double.class); + } + + public double getDouble(String path, double def) { + return get(path, def, double.class); + } + + public float getFloat(String path) { + return get(path, float.class); + } + + public float getFloat(String path, float def) { + return get(path, def, float.class); + } + + public String getString(String path) { + return get(path, String.class); + } + + public String getString(String path, String def) { + return get(path, def, String.class); + } + + public List getList(String path) { + Object listObject = get(path); + + if (listObject == null) { + throw new RuntimeException("No list found at path"); + } + + if (!(listObject instanceof List)) { + throw new RuntimeException("Object found at path is not a list: " + listObject.getClass().getName()); + } + + return (List) listObject; + } + + public List getStringList(String path) { + List list = getList(path); + List stringList = new ArrayList<>(); + + for (Object o : list) { + stringList.add(o.toString()); + } + + return stringList; + } + + public void save() { + file.delete(); + + try { + Files.write(file.toPath(), gson.toJson(map).getBytes(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); + } catch (IOException e) { + throw new RuntimeException("Failed to save config", e); + } + } + + private Object serializeType(Object value) { + if (value instanceof JsonElement) { + return gson.fromJson((JsonElement) value, new HashMap().getClass()); + } else if (value.getClass().isPrimitive() || value instanceof Map || value instanceof ArrayList) { + return value; + } else { + return value.toString(); + } + } + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/knockback/CraftKnockback.java b/vspigot-server/src/main/java/net/valorhcf/knockback/CraftKnockback.java new file mode 100644 index 0000000..a967e6f --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/knockback/CraftKnockback.java @@ -0,0 +1,69 @@ +package net.valorhcf.knockback; + +public class CraftKnockback implements Knockback { + + private String name; + private double friction = 2.0D; + private double horizontal = 0.35D; + private double vertical = 0.35D; + private double verticalLimit = 0.4000000059604645D; + private double extraHorizontal = 0.425D; + private double extraVertical = 0.085D; + + public CraftKnockback(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public double getFriction() { + return friction; + } + + public void setFriction(double friction) { + this.friction = friction; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public double getVerticalLimit() { + return verticalLimit; + } + + public void setVerticalLimit(double verticalLimit) { + this.verticalLimit = verticalLimit; + } + + public double getExtraHorizontal() { + return extraHorizontal; + } + + public void setExtraHorizontal(double extraHorizontal) { + this.extraHorizontal = extraHorizontal; + } + + public double getExtraVertical() { + return extraVertical; + } + + public void setExtraVertical(double extraVertical) { + this.extraVertical = extraVertical; + } + +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/AsyncNavigation.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/AsyncNavigation.java new file mode 100644 index 0000000..bd3e487 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/AsyncNavigation.java @@ -0,0 +1,200 @@ +package net.valorhcf.pathsearch; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.UUID; + +import org.bukkit.util.BlockVector; + +import net.valorhcf.pathsearch.cache.SearchCacheEntry; +import net.valorhcf.pathsearch.cache.SearchCacheEntryEntity; +import net.valorhcf.pathsearch.cache.SearchCacheEntryPosition; +import net.valorhcf.pathsearch.jobs.PathSearchJob; +import net.valorhcf.pathsearch.jobs.PathSearchJobNavigationEntity; +import net.valorhcf.pathsearch.jobs.PathSearchJobNavigationPosition; +import net.valorhcf.pathsearch.jobs.PathSearchQueuingManager; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.MathHelper; +import net.minecraft.server.Navigation; +import net.minecraft.server.PathEntity; +import net.minecraft.server.World; + +public class AsyncNavigation extends Navigation { + + private final HashMap searchCache; + private final HashMap positionSearchCache; + private static final double minimumDistanceForOffloadingSquared = 0.0D; + private int cleanUpDelay = 0; + private final PathSearchQueuingManager queuingManager; + + public AsyncNavigation(EntityInsentient entityinsentient, World world) { + super(entityinsentient, world); + this.searchCache = new HashMap(); + this.positionSearchCache = new HashMap(); + this.queuingManager = new PathSearchQueuingManager(); + } + + private BlockVector createBlockVectorForPosition(int x, int y, int z) { + return new BlockVector(x, y, z); + } + + private void issueSearch(Entity target, float range, boolean j, boolean k, boolean l, boolean m) { + this.queuingManager.queueSearch(new PathSearchJobNavigationEntity(this.a, target, range, j, k, l, m)); + } + + private void issueSearch(PositionPathSearchType type, int x, int y, int z, float range, boolean j, boolean k, boolean l, boolean m) { + this.queuingManager.queueSearch(new PathSearchJobNavigationPosition(type, this.a, x, y, z, range, j, k, l, m)); + } + + @Override + public void cancelSearch(PathSearchJob pathSearch) { + this.queuingManager.checkLastSearchResult(pathSearch); + pathSearch.cleanup(); + } + + @Override + public void setSearchResult(PathSearchJobNavigationEntity pathSearch) { + this.queuingManager.checkLastSearchResult(pathSearch); + SearchCacheEntry entry = pathSearch.getCacheEntryValue(); + if(entry != null && entry.didSearchSucceed()) { + synchronized(this.searchCache) { + UUID key = pathSearch.getCacheEntryKey(); + this.searchCache.put(key, entry); + } + } + } + + @Override + public void setSearchResult(PathSearchJobNavigationPosition pathSearch) { + this.queuingManager.checkLastSearchResult(pathSearch); + SearchCacheEntryPosition entry = pathSearch.getCacheEntryValue(); + if(entry != null && entry.didSearchSucceed()) { + synchronized(this.positionSearchCache) { + PositionPathSearchType key = pathSearch.getCacheEntryKey(); + this.positionSearchCache.put(key, entry); + } + } + } + + @Override + public PathEntity a(double d0, double d1, double d2) { + return this.a(PositionPathSearchType.ANYOTHER, d0, d1, d2); + } + + @Override + public boolean a(PositionPathSearchType type, double d0, double d1, double d2, double d3) { + PathEntity pathentity = this.a(type, MathHelper.floor(d0), (int) d1, MathHelper.floor(d2)); + + return this.a(pathentity, d3); + } + + public void cleanUpExpiredSearches() { + if(++this.cleanUpDelay > 100) { + this.cleanUpDelay = 0; + synchronized(this.searchCache) { + Iterator> iter = this.searchCache.entrySet().iterator(); + while(iter.hasNext()) { + Entry entry = iter.next(); + if(entry.getValue().hasExpired()) { + iter.remove(); + } + } + } + synchronized(this.positionSearchCache) { + Iterator> iter2 = this.positionSearchCache.entrySet().iterator(); + while(iter2.hasNext()) { + Entry entry = iter2.next(); + if(entry.getValue().hasExpired()) { + iter2.remove(); + } + } + } + } + } + + @Override + public PathEntity a(Entity entity) { + if(!this.offloadSearches() || this.a.f(entity) < minimumDistanceForOffloadingSquared) { + return super.a(entity); + } + if(!this.l()) { + return null; + } + SearchCacheEntry entry = null; + UUID id = entity.getUniqueID(); + synchronized(this.searchCache) { + if(this.searchCache.containsKey(id)) { + entry = this.searchCache.get(id); + } + } + PathEntity resultPath = null; + if(entry != null) { + resultPath = entry.getAdjustedPathEntity(); + if(!entry.isStillValid()) { + this.issueSearch(entity, this.d(), this.j, this.k, this.l, this.m); + } + } + if(entry == null && !this.queuingManager.hasAsyncSearchIssued()) { + resultPath = super.a(entity); + if(resultPath != null) { + entry = new SearchCacheEntryEntity(this.a, entity, resultPath); + synchronized(this.searchCache) { + SearchCacheEntry oldEntry = this.searchCache.put(id, entry); + if(oldEntry != null) { + oldEntry.cleanup(); + } + } + } + } + return resultPath; + } + + @Override + public PathEntity a(PositionPathSearchType type, double d0, double d1, double d2) { + if(!this.offloadSearches() || this.a.e(d0, d1, d2) < minimumDistanceForOffloadingSquared) { + return super.a(d0, d1, d2); + } + if(!this.l()) { + return null; + } + + int x = MathHelper.floor(d0); + int y = (int) d1; + int z = MathHelper.floor(d2); + + SearchCacheEntryPosition entry = null; + synchronized(this.positionSearchCache) { + if(this.positionSearchCache.containsKey(type)) { + entry = this.positionSearchCache.get(type); + } + } + + PathEntity resultPath = null; + if(entry != null) { + resultPath = entry.getAdjustedPathEntity(); + if(!entry.isStillValid()) { + this.issueSearch(type, x, y, z, this.d(), this.j, this.k, this.l, this.m); + } + } + if(entry == null && !this.queuingManager.hasAsyncSearchIssued()) { + resultPath = super.a(d0, d1, d2); + if(resultPath != null) { + entry = new SearchCacheEntryPosition(this.a, x, y, z, resultPath); + synchronized(this.positionSearchCache) { + SearchCacheEntry oldEntry = this.positionSearchCache.put(type, entry); + if(oldEntry != null) { + oldEntry.cleanup(); + } + } + } + } + return resultPath; + } + + private boolean offloadSearches() { + return true; + //return Migot.getConfig().isPathSearchOffloadedFor(this.b); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/AsyncPathfinder.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/AsyncPathfinder.java new file mode 100644 index 0000000..10e8ba8 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/AsyncPathfinder.java @@ -0,0 +1,21 @@ +package net.valorhcf.pathsearch; + +import net.minecraft.server.Entity; +import net.minecraft.server.IBlockAccess; +import net.minecraft.server.PathPoint; +import net.minecraft.server.Pathfinder; + +public class AsyncPathfinder extends Pathfinder { + + private IBlockAccess iblockaccess; + + public AsyncPathfinder(IBlockAccess iblockaccess, boolean flag, boolean flag1, boolean flag2, boolean flag3) { + super(iblockaccess, flag, flag1, flag2, flag3); + this.iblockaccess = iblockaccess; + } + + @Override + public int a(Entity entity, int i, int j, int k, PathPoint pathpoint) { + return this.a(this.iblockaccess, entity, i, j, k, pathpoint); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/PathSearchThrottlerThread.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/PathSearchThrottlerThread.java new file mode 100644 index 0000000..3a0209f --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/PathSearchThrottlerThread.java @@ -0,0 +1,105 @@ +package net.valorhcf.pathsearch; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import net.valorhcf.NamePriorityThreadFactory; +import net.valorhcf.pathsearch.jobs.PathSearchJob; + +public class PathSearchThrottlerThread extends ThreadPoolExecutor { + + private int queueLimit; + private LinkedHashMap filter; + private HashSet activeSearchHashes; + private static PathSearchThrottlerThread instance; + + public PathSearchThrottlerThread(int poolSize) { + super(poolSize, poolSize, 1L, TimeUnit.MINUTES, new LinkedBlockingQueue(), new NamePriorityThreadFactory(Thread.MIN_PRIORITY, "mSpigot_PathFinder")); + instance = this; + adjustPoolSize(poolSize); + this.filter = new LinkedHashMap(); + this.activeSearchHashes = new HashSet(); + } + + public boolean queuePathSearch(PathSearchJob newJob) { + boolean jobHasBeenQueued = false; + if(newJob != null) { + synchronized(this.filter) { + if(this.filter.containsKey(newJob) || this.filter.size() < 1000) { + jobHasBeenQueued = true; + PathSearchJob previousJob = this.filter.put(newJob, newJob); + if(previousJob != null) { + previousJob.cancel(); + } + } + } + if(!jobHasBeenQueued) { + newJob.cancel(); + } + } + PathSearchJob jobToExecute = null; + synchronized(this.filter) { + Iterator> iter = this.filter.entrySet().iterator(); + while(iter.hasNext() && this.getQueue().size() < this.queueLimit) { + jobToExecute = iter.next().getValue(); + if(!this.activeSearchHashes.contains(jobToExecute.getSearchHash())) { + iter.remove(); + if(jobToExecute != null) { + this.activeSearchHashes.add(jobToExecute.getSearchHash()); + this.submit(jobToExecute); + } + if(newJob != null) { + break; + } + } + } + } + return jobHasBeenQueued; + } + + @Override + public void shutdown() { + this.getQueue().clear(); + super.shutdown(); + } + + @Override + protected void afterExecute(Runnable runnable, Throwable throwable) { + super.afterExecute(runnable, throwable); + if(runnable instanceof FutureTask) { + FutureTask task = (FutureTask) runnable; + PathSearchJob job = null; + try { + job = task.get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + if(job != null) { + synchronized(this.filter) { + this.activeSearchHashes.remove(job.getSearchHash()); + } + } + } + this.queuePathSearch(null); + } + + public static void adjustPoolSize(int size) { + if(instance != null) { + if(size > instance.getMaximumPoolSize()) { + instance.setMaximumPoolSize(size); + instance.setCorePoolSize(size); + } else if(size < instance.getMaximumPoolSize()) { + instance.setCorePoolSize(size); + instance.setMaximumPoolSize(size); + } + instance.queueLimit = size * 8; + } + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/PositionPathSearchType.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/PositionPathSearchType.java new file mode 100644 index 0000000..4669a7e --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/PositionPathSearchType.java @@ -0,0 +1,16 @@ +package net.valorhcf.pathsearch; + +public enum PositionPathSearchType { + ANYOTHER, + AVOIDPLAYER, + FLEESUN, + JUMPONBLOCK, + MOVEINDOORS, + MOVETHROUGHVILLAGE, + MOVETOWARDSRESTRICTION, + MOVETOWARDSTARGET, + PANIC, + PLAY, + RANDOMSTROLL, + TAME; +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntry.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntry.java new file mode 100644 index 0000000..f10b712 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntry.java @@ -0,0 +1,81 @@ +package net.valorhcf.pathsearch.cache; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PathEntity; +import net.minecraft.server.PathPoint; + +import org.bukkit.util.BlockVector; + +public class SearchCacheEntry { + protected long tick; + protected BlockVector positionStart; + protected BlockVector positionTarget; + protected EntityInsentient entity; + private PathEntity path; + + public SearchCacheEntry(EntityInsentient entity, PathEntity path) { + this.entity = entity; + this.positionStart = this.getEntityPosition(this.entity); + this.path = path; + this.tick = this.getCurrentTick(); + } + + protected int getCurrentTick() { + return MinecraftServer.getServer().al(); + } + + protected BlockVector getEntityPosition(Entity entity) { + return new BlockVector(entity.locX, entity.locY, entity.locZ); + } + + protected BlockVector getTargetPosition(int x, int y, int z) { + return new BlockVector(x, y, z); + } + + public boolean isStillValid() { + return false; + } + + public PathEntity getPathEntity() { + return this.path; + } + + public boolean hasExpired() { + return !this.entity.isAlive() || (this.getCurrentTick() - this.tick) > 200; + } + + public boolean didSearchSucceed() { + return this.path != null; + } + + public boolean shouldBeRefreshed() { + return (this.getCurrentTick() - this.tick) > 5; + } + + public PathEntity getAdjustedPathEntity() { + if(this.path != null && (this.path.e() < this.path.d() - 1)) { + PathPoint pathpoint = this.path.a(this.path.e()); + double currentDist = this.entity.e(pathpoint.a, pathpoint.b, pathpoint.c); + while(this.path.e() < this.path.d() - 1) { + pathpoint = this.path.a(this.path.e() + 1); + double nextDist = this.entity.e(pathpoint.a, pathpoint.b, pathpoint.c); + if(nextDist < currentDist) { + currentDist = nextDist; + this.path.a(); + } else { + break; + } + } + } + return this.path; + } + + public void cleanup() { + this.positionStart = null; + this.positionTarget = null; + this.entity = null; + this.path = null; + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntryEntity.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntryEntity.java new file mode 100644 index 0000000..fc857f8 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntryEntity.java @@ -0,0 +1,37 @@ +package net.valorhcf.pathsearch.cache; + +import org.bukkit.util.BlockVector; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.PathEntity; + +public class SearchCacheEntryEntity extends SearchCacheEntry { + + private Entity target; + + public SearchCacheEntryEntity(EntityInsentient entity, Entity target, PathEntity path) { + super(entity, path); + this.target = target; + this.positionTarget = this.getEntityPosition(this.target); + } + + @Override + public boolean isStillValid() { + if(this.getCurrentTick() - this.tick > 20) { + return false; + } + BlockVector bvStart = this.getEntityPosition(this.entity); + BlockVector bvTarget = this.getEntityPosition(this.target); + if(!bvStart.equals(this.positionStart) || !bvTarget.equals(this.positionTarget)) { + return false; + } + return true; + } + + @Override + public void cleanup() { + super.cleanup(); + this.target = null; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntryPosition.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntryPosition.java new file mode 100644 index 0000000..efaad45 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/cache/SearchCacheEntryPosition.java @@ -0,0 +1,30 @@ +package net.valorhcf.pathsearch.cache; + +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.PathEntity; + +import org.bukkit.util.BlockVector; + +public class SearchCacheEntryPosition extends SearchCacheEntry { + + public SearchCacheEntryPosition(EntityInsentient entity, int x, int y, int z, PathEntity path) { + super(entity, path); + this.positionTarget = this.getTargetPosition(x, y, z); + } + + @Override + public boolean isStillValid() { + if(this.getCurrentTick() - this.tick > 20) { + return false; + } + BlockVector bvStart = this.getEntityPosition(this.entity); + if(!bvStart.equals(this.positionStart)) { + return false; + } + return true; + } + + public boolean targetEquals(BlockVector bv) { + return this.positionTarget.equals(bv); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJob.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJob.java new file mode 100644 index 0000000..690757f --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJob.java @@ -0,0 +1,71 @@ +package net.valorhcf.pathsearch.jobs; + +import java.util.concurrent.Callable; + +import net.valorhcf.WeakChunkCache; +import net.valorhcf.pathsearch.cache.SearchCacheEntry; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.MathHelper; +import net.minecraft.server.PathEntity; + +public abstract class PathSearchJob implements Callable { + + protected EntityInsentient entity; + protected WeakChunkCache chunkCache; + protected boolean issued; + protected float range; + protected boolean b1, b2, b3, b4; + protected PathEntity pathEntity; + protected int hash; + + public PathSearchJob(EntityInsentient entity, float range, boolean b1, boolean b2, boolean b3, boolean b4) { + this.entity = entity; + this.range = range; + this.b1 = b1; + this.b2 = b2; + this.b3 = b3; + this.b4 = b4; + this.issued = false; + this.hash = entity.getUniqueID().hashCode(); + this.createChunkCache(); + } + + private void createChunkCache() { + int x = MathHelper.floor(this.entity.locX); + int y = MathHelper.floor(this.entity.locY); + int z = MathHelper.floor(this.entity.locZ); + int radius = (int) (this.range + 8.0F); + int xMinor = x - radius; + int yMinor = y - radius; + int zMinor = z - radius; + int xMajor = x + radius; + int yMajor = y + radius; + int zMajor = z + radius; + this.chunkCache = new WeakChunkCache(this.entity.world, xMinor, yMinor, zMinor, xMajor, yMajor, zMajor, 0); + } + + public void cleanup() { + this.entity = null; + this.chunkCache = null; + this.pathEntity = null; + } + + public int getSearchHash() { + return this.hash; + } + + @Override + public int hashCode() { + return this.hash; + } + + public abstract Object getCacheEntryKey(); + + public abstract SearchCacheEntry getCacheEntryValue(); + + public abstract void cancel(); + + protected boolean isEntityStillValid() { + return this.entity != null && this.entity.valid && this.entity.isAlive(); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobEntity.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobEntity.java new file mode 100644 index 0000000..3d38127 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobEntity.java @@ -0,0 +1,59 @@ +package net.valorhcf.pathsearch.jobs; + +import net.valorhcf.pathsearch.AsyncPathfinder; +import net.valorhcf.pathsearch.cache.SearchCacheEntry; +import net.valorhcf.pathsearch.cache.SearchCacheEntryEntity; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityCreature; + +public class PathSearchJobEntity extends PathSearchJob { + + private Entity target; + + public PathSearchJobEntity(EntityCreature entity, Entity target, float range, boolean b1, boolean b2, boolean b3, boolean b4) { + super(entity, range, b1, b2, b3, b4); + this.target = target; + } + + @Override + public PathSearchJob call() throws Exception { + if(!this.isEntityStillValid()) { + this.cancel(); + } else if(!this.issued) { + this.issued = true; + this.pathEntity = (new AsyncPathfinder(this.chunkCache, this.b1, this.b2, this.b3, this.b4)).a(entity, this.target, this.range); + ((EntityCreature) this.entity).setSearchResult(this, this.target, this.pathEntity); + this.cleanup(); + } + return this; + } + + @Override + public void cleanup() { + super.cleanup(); + this.target = null; + } + + @Override + public Object getCacheEntryKey() { + return this.entity.getUniqueID(); + } + + @Override + public SearchCacheEntry getCacheEntryValue() { + return new SearchCacheEntryEntity(this.entity, this.target, this.pathEntity); + } + + @Override + public void cancel() { + ((EntityCreature) this.entity).cancelSearch(this); + } + + @Override + public boolean equals(Object o) { + if(o == null || !(o instanceof PathSearchJobEntity)) { + return false; + } + return this.getSearchHash() == ((PathSearchJobEntity)o).getSearchHash(); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobNavigationEntity.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobNavigationEntity.java new file mode 100644 index 0000000..1369aaf --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobNavigationEntity.java @@ -0,0 +1,60 @@ +package net.valorhcf.pathsearch.jobs; + +import java.util.UUID; + +import net.valorhcf.pathsearch.AsyncPathfinder; +import net.valorhcf.pathsearch.cache.SearchCacheEntry; +import net.valorhcf.pathsearch.cache.SearchCacheEntryEntity; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityInsentient; + +public class PathSearchJobNavigationEntity extends PathSearchJob { + + private Entity target; + + public PathSearchJobNavigationEntity(EntityInsentient entity, Entity target, float range, boolean b1, boolean b2, boolean b3, boolean b4) { + super(entity, range, b1, b2, b3, b4); + this.target = target; + } + + @Override + public PathSearchJob call() throws Exception { + if(!this.isEntityStillValid()) { + this.cancel(); + } else if(!this.issued) { + this.issued = true; + this.pathEntity = (new AsyncPathfinder(this.chunkCache, this.b1, this.b2, this.b3, this.b4)).a(entity, this.target, this.range); + this.entity.getNavigation().setSearchResult(this); + this.cleanup(); + } + return this; + } + + @Override + public void cleanup() { + super.cleanup(); + this.target = null; + } + + public UUID getCacheEntryKey() { + return this.target.getUniqueID(); + } + + public SearchCacheEntry getCacheEntryValue() { + return new SearchCacheEntryEntity(this.entity, this.target, this.pathEntity); + } + + @Override + public void cancel() { + this.entity.getNavigation().cancelSearch(this); + } + + @Override + public boolean equals(Object o) { + if(o == null || !(o instanceof PathSearchJobNavigationEntity)) { + return false; + } + return this.getSearchHash() == ((PathSearchJobNavigationEntity)o).getSearchHash(); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobNavigationPosition.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobNavigationPosition.java new file mode 100644 index 0000000..c79b8c8 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobNavigationPosition.java @@ -0,0 +1,65 @@ +package net.valorhcf.pathsearch.jobs; + +import net.valorhcf.pathsearch.AsyncPathfinder; +import net.valorhcf.pathsearch.PositionPathSearchType; +import net.valorhcf.pathsearch.cache.SearchCacheEntryPosition; +import net.minecraft.server.EntityInsentient; + +public class PathSearchJobNavigationPosition extends PathSearchJob { + + private int x, y, z; + private PositionPathSearchType type; + + public PathSearchJobNavigationPosition(PositionPathSearchType type, EntityInsentient entity, int x, int y, int z, float range, boolean b1, boolean b2, boolean b3, boolean b4) { + super(entity, range, b1, b2, b3, b4); + this.type = type; + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public PathSearchJob call() throws Exception { + if(!this.isEntityStillValid()) { + this.cancel(); + } else if(!this.issued) { + this.issued = true; + this.pathEntity = (new AsyncPathfinder(this.chunkCache, this.b1, this.b2, this.b3, this.b4)).a(entity, this.x, this.y, this.z, this.range); + this.entity.getNavigation().setSearchResult(this); + this.cleanup(); + } + return this; + } + + public PositionPathSearchType getCacheEntryKey() { + return this.type; + } + + public SearchCacheEntryPosition getCacheEntryValue() { + return new SearchCacheEntryPosition(this.entity, this.x, this.y, this.z, this.pathEntity); + } + + @Override + public int hashCode() { + return this.type.hashCode() ^ + (this.getSearchHash() << 4); + } + + @Override + public boolean equals(Object o) { + if(o == null || !(o instanceof PathSearchJobNavigationPosition)) { + return false; + } + PathSearchJobNavigationPosition other = (PathSearchJobNavigationPosition) o; + + return this.type.equals( + other.type) && + this.getSearchHash() == + other.getSearchHash(); + } + + @Override + public void cancel() { + this.entity.getNavigation().cancelSearch(this); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobPosition.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobPosition.java new file mode 100644 index 0000000..f27aca4 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchJobPosition.java @@ -0,0 +1,66 @@ +package net.valorhcf.pathsearch.jobs; + +import net.valorhcf.pathsearch.AsyncPathfinder; +import net.valorhcf.pathsearch.PositionPathSearchType; +import net.valorhcf.pathsearch.cache.SearchCacheEntry; +import net.valorhcf.pathsearch.cache.SearchCacheEntryPosition; +import net.minecraft.server.EntityCreature; + +public class PathSearchJobPosition extends PathSearchJob { + + private int x, y, z; + private PositionPathSearchType type; + + public PathSearchJobPosition(EntityCreature entity, int x, int y, int z, float range, boolean b1, boolean b2, boolean b3, boolean b4) { + super(entity, range, b1, b2, b3, b4); + this.type = PositionPathSearchType.ANYOTHER; + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public PathSearchJob call() throws Exception { + if(!this.isEntityStillValid()) { + this.cancel(); + } else if(!this.issued) { + this.issued = true; + this.pathEntity = (new AsyncPathfinder(this.chunkCache, this.b1, this.b2, this.b3, this.b4)).a(entity, this.x, this.y, this.z, this.range); + ((EntityCreature)this.entity).setSearchResult(this, this.pathEntity); + this.cleanup(); + } + return this; + } + + @Override + public Object getCacheEntryKey() { + return this.type; + } + + @Override + public SearchCacheEntry getCacheEntryValue() { + return new SearchCacheEntryPosition(this.entity, this.x, this.y, this.z, this.pathEntity); + } + + @Override + public int hashCode() { + return this.type.hashCode() ^ (this.getSearchHash() << 4); + } + + @Override + public boolean equals(Object o) { + if(o == null || !(o instanceof PathSearchJobPosition)) { + return false; + } + PathSearchJobPosition other = (PathSearchJobPosition) o; + + return this.type.equals( + other.type) && + this.getSearchHash() == other.getSearchHash(); + } + + @Override + public void cancel() { + ((EntityCreature) this.entity).cancelSearch(this); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchQueuingManager.java b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchQueuingManager.java new file mode 100644 index 0000000..b8d10be --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/pathsearch/jobs/PathSearchQueuingManager.java @@ -0,0 +1,28 @@ +package net.valorhcf.pathsearch.jobs; + +import net.valorhcf.ThreadingManager; + +public class PathSearchQueuingManager { + + private PathSearchJob lastQueuedJob; + + public PathSearchQueuingManager() { + this.lastQueuedJob = null; + } + + public synchronized boolean hasAsyncSearchIssued() { + return this.lastQueuedJob != null; + } + + public synchronized void queueSearch(PathSearchJob job) { + if(ThreadingManager.queuePathSearch(job)) { + this.lastQueuedJob = job; + } + } + + public synchronized void checkLastSearchResult(PathSearchJob pathSearch) { + if(this.lastQueuedJob == pathSearch) { + this.lastQueuedJob = null; + } + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/timings/ExtendedCustomTimingsHandler.java b/vspigot-server/src/main/java/net/valorhcf/timings/ExtendedCustomTimingsHandler.java new file mode 100644 index 0000000..4263bd1 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/timings/ExtendedCustomTimingsHandler.java @@ -0,0 +1,139 @@ +package net.valorhcf.timings; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Queue; + +import net.valorhcf.ThreadingManager; +import net.valorhcf.ThreadingManager.TaskQueueWorker; +import net.minecraft.server.MinecraftServer; + +import org.bukkit.Bukkit; +import org.spigotmc.CustomTimingsHandler; +import org.spigotmc.SpigotConfig; + +public class ExtendedCustomTimingsHandler extends CustomTimingsHandler { + + private static File path = new File("LagSpikeLog"); + private static File file; + private static TaskQueueWorker taskQueue; + + public ExtendedCustomTimingsHandler(String name) + { + this( name, null ); + } + + public ExtendedCustomTimingsHandler(String name, CustomTimingsHandler parent) + { + super(name, parent); + } + + public static void tick() { + if(Bukkit.getPluginManager().useTimings() && SpigotConfig.lagSpikeLoggerEnabled) { + for(CustomTimingsHandler handler: HANDLERS) { + if(handler.getCurrentTickTotal() > SpigotConfig.lagSpikeLoggerTickLimitNanos) { + dumpTimings(HANDLERS); + break; + } + } + } + CustomTimingsHandler.tick(); + } + + private static void dumpTimings(Queue handlers) { + ArrayList list = new ArrayList(handlers.size()); + for(CustomTimingsHandler handler: handlers) { + if(handler.getCurrentTickTotal() > 1000000L) { // greater than 1ms + list.add(new LogEntry(handler)); + } + } + if(taskQueue == null) { + taskQueue = ThreadingManager.createTaskQueue(); + } + taskQueue.queueTask(new LogDump(list)); + } + + private static class LogEntry implements Comparable { + public String name; + public long total; + + public LogEntry(CustomTimingsHandler handler) { + this.name = handler.getName(); + this.total = handler.getCurrentTickTotal(); + } + + @Override + public int compareTo(LogEntry other) { + return (int) (other.total - this.total); + } + + public String getFormatedTotal() { + double a = (double) this.total / 1000000.0D; + return String.format("%.2f", a); + } + } + + private static class LogDump implements Runnable { + + private List list; + private long serverTick; + + public LogDump(List list) { + this.list = list; + this.serverTick = MinecraftServer.currentTick; + } + + @Override + public void run() { + if(list.isEmpty()) { + return; + } + Collections.sort(this.list); + if(!path.exists()) { + path.mkdirs(); + } + SimpleDateFormat df = new SimpleDateFormat ("yyyy-MM-dd_HH-mm-ss"); + String formatedDate = df.format(new Date()); + if(file == null) { + file = new File(path, "LagSpikeLog_" + formatedDate + ".txt"); + } + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(file, true), 8 * 1024); + writer.newLine(); + writer.write("Time stamp: "); + writer.write(formatedDate); + writer.write(" Server tick: "); + writer.write(String.valueOf(this.serverTick)); + writer.newLine(); + writer.newLine(); + for(LogEntry e: list) { + writer.write(e.getFormatedTotal()); + writer.write(" -- "); + writer.write(e.name); + writer.newLine(); + } + writer.newLine(); + writer.write("============================================================"); + writer.newLine(); + writer.flush(); + } catch (IOException e) { + System.out.println("Failed to dump the timings of a lag spike: " + e.toString()); + e.printStackTrace(); + } finally { + if(writer != null) { + try { + writer.close(); + } catch (IOException e) {} + } + } + } + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/util/BlockUtil.java b/vspigot-server/src/main/java/net/valorhcf/util/BlockUtil.java new file mode 100644 index 0000000..f68d701 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/util/BlockUtil.java @@ -0,0 +1,244 @@ +package net.valorhcf.util; + +import java.util.HashSet; +import java.util.Set; + +import org.bukkit.Location; +import org.bukkit.World; + +public class BlockUtil { + private static Set blockSolidPassSet; + private static Set blockStairsSet; + private static Set blockLiquidsSet; + private static Set blockWebsSet; + private static Set blockIceSet; + + @SuppressWarnings("deprecation") + public static boolean isOnGround(final Location location, final int down) { + final double posX = location.getX(); + final double posZ = location.getZ(); + final double fracX = (posX % 1.0 > 0.0) ? Math.abs(posX % 1.0) : (1.0 - Math.abs(posX % 1.0)); + final double fracZ = (posZ % 1.0 > 0.0) ? Math.abs(posZ % 1.0) : (1.0 - Math.abs(posZ % 1.0)); + final int blockX = location.getBlockX(); + final int blockY = location.getBlockY() - down; + final int blockZ = location.getBlockZ(); + final World world = location.getWorld(); + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX, blockY, blockZ).getTypeId())) { + return true; + } + if (fracX < 0.3) { + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ).getTypeId())) { + return true; + } + if (fracZ < 0.3) { + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + } else if (fracZ > 0.7) { + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + } + } else if (fracX > 0.7) { + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ).getTypeId())) { + return true; + } + if (fracZ < 0.3) { + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + } else if (fracZ > 0.7) { + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + } + } else if (fracZ < 0.3) { + if (!BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX, blockY, blockZ - 1).getTypeId())) { + return true; + } + } else if (fracZ > 0.7 && !BlockUtil.blockSolidPassSet.contains((byte) world.getBlockAt(blockX, blockY, blockZ + 1).getTypeId())) { + return true; + } + return false; + } + + public static boolean isOnStairs(final Location location, final int down) { + return isUnderBlock(location, BlockUtil.blockStairsSet, down); + } + + @SuppressWarnings("deprecation") + private static boolean isUnderBlock(final Location location, final Set itemIDs, final int down) { + final double posX = location.getX(); + final double posZ = location.getZ(); + final double fracX = (posX % 1.0 > 0.0) ? Math.abs(posX % 1.0) : (1.0 - Math.abs(posX % 1.0)); + final double fracZ = (posZ % 1.0 > 0.0) ? Math.abs(posZ % 1.0) : (1.0 - Math.abs(posZ % 1.0)); + final int blockX = location.getBlockX(); + final int blockY = location.getBlockY() - down; + final int blockZ = location.getBlockZ(); + final World world = location.getWorld(); + if (itemIDs.contains((byte) world.getBlockAt(blockX, blockY, blockZ).getTypeId())) { + return true; + } + if (fracX < 0.3) { + if (itemIDs.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ).getTypeId())) { + return true; + } + if (fracZ < 0.3) { + if (itemIDs.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + } else if (fracZ > 0.7) { + if (itemIDs.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + } + } else if (fracX > 0.7) { + if (itemIDs.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ).getTypeId())) { + return true; + } + if (fracZ < 0.3) { + if (itemIDs.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX, blockY, blockZ - 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ - 1).getTypeId())) { + return true; + } + } else if (fracZ > 0.7) { + if (itemIDs.contains((byte) world.getBlockAt(blockX - 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX, blockY, blockZ + 1).getTypeId())) { + return true; + } + if (itemIDs.contains((byte) world.getBlockAt(blockX + 1, blockY, blockZ + 1).getTypeId())) { + return true; + } + } + } else if (fracZ < 0.3) { + if (itemIDs.contains((byte) world.getBlockAt(blockX, blockY, blockZ - 1).getTypeId())) { + return true; + } + } else if (fracZ > 0.7 && itemIDs.contains((byte) world.getBlockAt(blockX, blockY, blockZ + 1).getTypeId())) { + return true; + } + return false; + } + + public static boolean isOnLiquid(final Location location, final int down) { + return isUnderBlock(location, BlockUtil.blockLiquidsSet, down); + } + + public static boolean isOnWeb(final Location location, final int down) { + return isUnderBlock(location, BlockUtil.blockWebsSet, down); + } + + public static boolean isOnIce(final Location location, final int down) { + return isUnderBlock(location, BlockUtil.blockIceSet, down); + } + + static { + BlockUtil.blockSolidPassSet = new HashSet(); + BlockUtil.blockStairsSet = new HashSet(); + BlockUtil.blockLiquidsSet = new HashSet(); + BlockUtil.blockWebsSet = new HashSet(); + BlockUtil.blockIceSet = new HashSet(); + BlockUtil.blockSolidPassSet.add((byte) 0); + BlockUtil.blockSolidPassSet.add((byte) 6); + BlockUtil.blockSolidPassSet.add((byte) 8); + BlockUtil.blockSolidPassSet.add((byte) 9); + BlockUtil.blockSolidPassSet.add((byte) 10); + BlockUtil.blockSolidPassSet.add((byte) 11); + BlockUtil.blockSolidPassSet.add((byte) 27); + BlockUtil.blockSolidPassSet.add((byte) 28); + BlockUtil.blockSolidPassSet.add((byte) 30); + BlockUtil.blockSolidPassSet.add((byte) 31); + BlockUtil.blockSolidPassSet.add((byte) 32); + BlockUtil.blockSolidPassSet.add((byte) 37); + BlockUtil.blockSolidPassSet.add((byte) 38); + BlockUtil.blockSolidPassSet.add((byte) 39); + BlockUtil.blockSolidPassSet.add((byte) 40); + BlockUtil.blockSolidPassSet.add((byte) 50); + BlockUtil.blockSolidPassSet.add((byte) 51); + BlockUtil.blockSolidPassSet.add((byte) 55); + BlockUtil.blockSolidPassSet.add((byte) 59); + BlockUtil.blockSolidPassSet.add((byte) 63); + BlockUtil.blockSolidPassSet.add((byte) 66); + BlockUtil.blockSolidPassSet.add((byte) 68); + BlockUtil.blockSolidPassSet.add((byte) 69); + BlockUtil.blockSolidPassSet.add((byte) 70); + BlockUtil.blockSolidPassSet.add((byte) 72); + BlockUtil.blockSolidPassSet.add((byte) 75); + BlockUtil.blockSolidPassSet.add((byte) 76); + BlockUtil.blockSolidPassSet.add((byte) 77); + BlockUtil.blockSolidPassSet.add((byte) 78); + BlockUtil.blockSolidPassSet.add((byte) 83); + BlockUtil.blockSolidPassSet.add((byte) 90); + BlockUtil.blockSolidPassSet.add((byte) 104); + BlockUtil.blockSolidPassSet.add((byte) 105); + BlockUtil.blockSolidPassSet.add((byte) 115); + BlockUtil.blockSolidPassSet.add((byte) 119); + BlockUtil.blockSolidPassSet.add((byte) (-124)); + BlockUtil.blockSolidPassSet.add((byte) (-113)); + BlockUtil.blockSolidPassSet.add((byte) (-81)); + BlockUtil.blockStairsSet.add((byte) 53); + BlockUtil.blockStairsSet.add((byte) 67); + BlockUtil.blockStairsSet.add((byte) 108); + BlockUtil.blockStairsSet.add((byte) 109); + BlockUtil.blockStairsSet.add((byte) 114); + BlockUtil.blockStairsSet.add((byte) (-128)); + BlockUtil.blockStairsSet.add((byte) (-122)); + BlockUtil.blockStairsSet.add((byte) (-121)); + BlockUtil.blockStairsSet.add((byte) (-120)); + BlockUtil.blockStairsSet.add((byte) (-100)); + BlockUtil.blockStairsSet.add((byte) (-93)); + BlockUtil.blockStairsSet.add((byte) (-92)); + BlockUtil.blockStairsSet.add((byte) 126); + BlockUtil.blockStairsSet.add((byte) (-76)); + BlockUtil.blockLiquidsSet.add((byte) 8); + BlockUtil.blockLiquidsSet.add((byte) 9); + BlockUtil.blockLiquidsSet.add((byte) 10); + BlockUtil.blockLiquidsSet.add((byte) 11); + BlockUtil.blockWebsSet.add((byte) 30); + BlockUtil.blockIceSet.add((byte) 79); + BlockUtil.blockIceSet.add((byte) (-82)); + } +} diff --git a/vspigot-server/src/main/java/net/valorhcf/util/IndexedLinkedHashSet.java b/vspigot-server/src/main/java/net/valorhcf/util/IndexedLinkedHashSet.java new file mode 100644 index 0000000..ccb8ba8 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/util/IndexedLinkedHashSet.java @@ -0,0 +1,97 @@ +package net.valorhcf.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public final class IndexedLinkedHashSet implements Set { + + private final ArrayList list = new ArrayList(); + private final HashSet set = new HashSet(); + + public boolean add(E e) { + if (set.add(e)) { + return list.add(e); + } + return false; + } + + public boolean remove(Object o) { + if (set.remove(o)) { + return list.remove(o); + } + return false; + } + + @Override + public boolean containsAll(Collection c) { + return set.containsAll(c); + } + + public void clear() { + set.clear(); + list.clear(); + } + + public E get(int index) { + return list.get(index); + } + + public boolean removeAll(Collection c) { + if (set.removeAll(c)) { + return list.removeAll(c); + } + return true; + } + + public boolean retainAll(Collection c) { + if (set.retainAll(c)) { + return list.retainAll(c); + } + return false; + } + + public boolean addAll(Collection c) { + boolean modified = false; + for (E e : c) + if (add(e)) + modified = true; + return modified; + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return set.contains(o); + } + + @Override + public Iterator iterator() { + return list.iterator(); + } + + @Override + public Object[] toArray() { + return list.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return list.toArray(a); + } + + public int indexOf(Object o) { + return list.indexOf(o); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/net/valorhcf/util/WrappedArrayMap.java b/vspigot-server/src/main/java/net/valorhcf/util/WrappedArrayMap.java new file mode 100644 index 0000000..34d7eb4 --- /dev/null +++ b/vspigot-server/src/main/java/net/valorhcf/util/WrappedArrayMap.java @@ -0,0 +1,144 @@ +package net.valorhcf.util; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.google.common.base.Objects; + +import net.minecraft.server.ItemStack; +import net.minecraft.server.WatchableObject; + +public class WrappedArrayMap implements Map { + + private WatchableObject[] dataValues = new WatchableObject[23]; + + @Override + public int size() { + return dataValues.length; + } + + @Override + public boolean isEmpty() { + for (int i = 0; i < dataValues.length; i++) { + if (dataValues[i] != null) return false; + } + + return true; + } + + public boolean containsKey(int i) { + return dataValues[i] != null; + } + + @Override + public boolean containsKey(Object key) { + return key instanceof Integer && dataValues[(Integer) key] != null; + } + + @Override + public boolean containsValue(Object value) { + for (int i = 0; i < dataValues.length; i++) { + if (Objects.equal(dataValues[i], value)) return true; + } + + return false; + } + + public WatchableObject get(int i) { + return dataValues[i]; + } + + @Override + public WatchableObject get(Object key) { + return key instanceof Integer ? dataValues[(Integer) key] : null; + } + + public void put(int i, WatchableObject watchableObject) { + dataValues[i] = watchableObject; + } + + @Override + public WatchableObject put(Integer key, WatchableObject value) { + int index = key.intValue(); + WatchableObject old = dataValues[index]; + dataValues[index] = value; + return old; + } + + @Override + public WatchableObject remove(Object key) { + if (!(key instanceof Integer)) return null; + + int index = ((Integer) key).intValue(); + WatchableObject old = dataValues[index]; + dataValues[index] = null; + return old; + } + + @Override + public void putAll(Map m) { + for (Object object : m.entrySet()) { + if (!(object instanceof Entry)) continue; + Entry entry = (Entry) object; + Object key = entry.getKey(); + Object value = entry.getValue(); + + if (!(key instanceof Integer) || (!(value instanceof WatchableObject))) continue; + + put((Integer) key, (WatchableObject) value); + } + } + + @Override + public void clear() { + this.dataValues = new WatchableObject[23]; + } + + @Override + public Set keySet() { + Set set = new HashSet(); + for (int i = 0; i < dataValues.length; i++) { + if (dataValues[i] != null) set.add(Integer.valueOf(i)); + } + + return set; + } + + @Override + public Collection values() { + Set set = new HashSet(); + for (int i = 0; i < dataValues.length; i++) { + if (dataValues[i] != null) set.add(dataValues[i]); + } + + return set; + } + + @Override + public Set> entrySet() { + Set> set = new HashSet>(); + for (int i = 0; i < dataValues.length; i++) { + WatchableObject watchableObject = dataValues[i]; + if (watchableObject != null) { + set.add(new AbstractMap.SimpleEntry(Integer.valueOf(i), watchableObject)); + } + } + + return set; + } + + // Clone the WatchableObjects and deep clone ItemStacks if there are any + public WrappedArrayMap clone() { + WrappedArrayMap wrappedArrayMap = new WrappedArrayMap(); + for (int i = 0; i < this.dataValues.length; i++) { + WatchableObject watchableObject = this.dataValues[i]; + if (watchableObject != null) { + wrappedArrayMap.dataValues[i] = watchableObject.b() instanceof ItemStack ? new WatchableObject(watchableObject.c(), watchableObject.a(), ((ItemStack) watchableObject.b()).cloneItemStack()) : watchableObject.clone(); + } + } + return wrappedArrayMap; + } +} diff --git a/vspigot-server/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java b/vspigot-server/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java new file mode 100644 index 0000000..341eaa3 --- /dev/null +++ b/vspigot-server/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.appender; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.nio.charset.Charset; + +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.helpers.Booleans; +import org.apache.logging.log4j.core.helpers.Loader; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.util.PropertiesUtil; + +/** + * ConsoleAppender appends log events to System.out or + * System.err using a layout specified by the user. The + * default target is System.out. + * @doubt accessing System.out or .err as a byte stream instead of a writer + * bypasses the JVM's knowledge of the proper encoding. (RG) Encoding + * is handled within the Layout. Typically, a Layout will generate a String + * and then call getBytes which may use a configured encoding or the system + * default. OTOH, a Writer cannot print byte streams. + */ +@Plugin(name = "Console", category = "Core", elementType = "appender", printObject = true) +public final class ConsoleAppender extends AbstractOutputStreamAppender { + + private static final String JANSI_CLASS = "org.fusesource.jansi.WindowsAnsiOutputStream"; + private static ConsoleManagerFactory factory = new ConsoleManagerFactory(); + + /** + * Enumeration of console destinations. + */ + public enum Target { + /** Standard output. */ + SYSTEM_OUT, + /** Standard error output. */ + SYSTEM_ERR + } + + private ConsoleAppender(final String name, final Layout layout, final Filter filter, + final OutputStreamManager manager, + final boolean ignoreExceptions) { + super(name, layout, filter, ignoreExceptions, true, manager); + } + + /** + * Create a Console Appender. + * @param layout The layout to use (required). + * @param filter The Filter or null. + * @param t The target ("SYSTEM_OUT" or "SYSTEM_ERR"). The default is "SYSTEM_OUT". + * @param follow If true will follow changes to the underlying output stream. + * @param name The name of the Appender (required). + * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise + * they are propagated to the caller. + * @return The ConsoleAppender. + */ + @PluginFactory + public static ConsoleAppender createAppender( + @PluginElement("Layout") Layout layout, + @PluginElement("Filters") final Filter filter, + @PluginAttribute("target") final String t, + @PluginAttribute("name") final String name, + @PluginAttribute("follow") final String follow, + @PluginAttribute("ignoreExceptions") final String ignore) { + if (name == null) { + LOGGER.error("No name provided for ConsoleAppender"); + return null; + } + if (layout == null) { + layout = PatternLayout.createLayout(null, null, null, null, null); + } + final boolean isFollow = Boolean.parseBoolean(follow); + final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true); + final Target target = t == null ? Target.SYSTEM_OUT : Target.valueOf(t); + return new ConsoleAppender(name, layout, filter, getManager(isFollow, target, layout), ignoreExceptions); + } + + private static OutputStreamManager getManager(final boolean follow, final Target target, final Layout layout) { + final String type = target.name(); + final OutputStream os = getOutputStream(follow, target); + return OutputStreamManager.getManager(target.name() + "." + follow, new FactoryData(os, type, layout), factory); + } + + private static OutputStream getOutputStream(final boolean follow, final Target target) { + final String enc = Charset.defaultCharset().name(); + PrintStream printStream = null; + try { + printStream = target == Target.SYSTEM_OUT ? + follow ? new PrintStream(new SystemOutStream(), true, enc) : System.out : + follow ? new PrintStream(new SystemErrStream(), true, enc) : System.err; + } catch (final UnsupportedEncodingException ex) { // should never happen + throw new IllegalStateException("Unsupported default encoding " + enc, ex); + } + final PropertiesUtil propsUtil = PropertiesUtil.getProperties(); + if (!propsUtil.getStringProperty("os.name").startsWith("Windows") || + propsUtil.getBooleanProperty("log4j.skipJansi")) { + return printStream; + } + try { + final ClassLoader loader = Loader.getClassLoader(); + // We type the parameter as a wildcard to avoid a hard reference to Jansi. + final Class clazz = loader.loadClass(JANSI_CLASS); + final Constructor constructor = clazz.getConstructor(OutputStream.class); + return (OutputStream) constructor.newInstance(printStream); + } catch (final ClassNotFoundException cnfe) { + LOGGER.debug("Jansi is not installed, cannot find {}", JANSI_CLASS); + } catch (final NoSuchMethodException nsme) { + LOGGER.warn("{} is missing the proper constructor", JANSI_CLASS); + } catch (final Throwable ex) { // CraftBukkit - Exception -> Throwable + LOGGER.warn("Unable to instantiate {}", JANSI_CLASS); + } + return printStream; + } + + /** + * An implementation of OutputStream that redirects to the current System.err. + */ + private static class SystemErrStream extends OutputStream { + public SystemErrStream() { + } + + @Override + public void close() { + // do not close sys err! + } + + @Override + public void flush() { + System.err.flush(); + } + + @Override + public void write(final byte[] b) throws IOException { + System.err.write(b); + } + + @Override + public void write(final byte[] b, final int off, final int len) + throws IOException { + System.err.write(b, off, len); + } + + @Override + public void write(final int b) { + System.err.write(b); + } + } + + /** + * An implementation of OutputStream that redirects to the current System.out. + */ + private static class SystemOutStream extends OutputStream { + public SystemOutStream() { + } + + @Override + public void close() { + // do not close sys out! + } + + @Override + public void flush() { + System.out.flush(); + } + + @Override + public void write(final byte[] b) throws IOException { + System.out.write(b); + } + + @Override + public void write(final byte[] b, final int off, final int len) + throws IOException { + System.out.write(b, off, len); + } + + @Override + public void write(final int b) throws IOException { + System.out.write(b); + } + } + + /** + * Data to pass to factory method. + */ + private static class FactoryData { + private final OutputStream os; + private final String type; + private final Layout layout; + + /** + * Constructor. + * @param os The OutputStream. + * @param type The name of the target. + * @param layout A Serializable layout + */ + public FactoryData(final OutputStream os, final String type, final Layout layout) { + this.os = os; + this.type = type; + this.layout = layout; + } + } + + /** + * Factory to create the Appender. + */ + private static class ConsoleManagerFactory implements ManagerFactory { + + /** + * Create an OutputStreamManager. + * @param name The name of the entity to manage. + * @param data The data required to create the entity. + * @return The OutputStreamManager + */ + @Override + public OutputStreamManager createManager(final String name, final FactoryData data) { + return new OutputStreamManager(data.os, data.type, data.layout); + } + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftArt.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftArt.java new file mode 100644 index 0000000..f617e9e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftArt.java @@ -0,0 +1,74 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.EnumArt; +import org.bukkit.Art; + +// Safety class, will break if either side changes +public class CraftArt { + + public static Art NotchToBukkit(EnumArt art) { + switch (art) { + case KEBAB: return Art.KEBAB; + case AZTEC: return Art.AZTEC; + case ALBAN: return Art.ALBAN; + case AZTEC2: return Art.AZTEC2; + case BOMB: return Art.BOMB; + case PLANT: return Art.PLANT; + case WASTELAND: return Art.WASTELAND; + case POOL: return Art.POOL; + case COURBET: return Art.COURBET; + case SEA: return Art.SEA; + case SUNSET: return Art.SUNSET; + case CREEBET: return Art.CREEBET; + case WANDERER: return Art.WANDERER; + case GRAHAM: return Art.GRAHAM; + case MATCH: return Art.MATCH; + case BUST: return Art.BUST; + case STAGE: return Art.STAGE; + case VOID: return Art.VOID; + case SKULL_AND_ROSES: return Art.SKULL_AND_ROSES; + case FIGHTERS: return Art.FIGHTERS; + case POINTER: return Art.POINTER; + case PIGSCENE: return Art.PIGSCENE; + case BURNINGSKULL: return Art.BURNINGSKULL; + case SKELETON: return Art.SKELETON; + case DONKEYKONG: return Art.DONKEYKONG; + case WITHER: return Art.WITHER; + default: + throw new AssertionError(art); + } + } + + public static EnumArt BukkitToNotch(Art art) { + switch (art) { + case KEBAB: return EnumArt.KEBAB; + case AZTEC: return EnumArt.AZTEC; + case ALBAN: return EnumArt.ALBAN; + case AZTEC2: return EnumArt.AZTEC2; + case BOMB: return EnumArt.BOMB; + case PLANT: return EnumArt.PLANT; + case WASTELAND: return EnumArt.WASTELAND; + case POOL: return EnumArt.POOL; + case COURBET: return EnumArt.COURBET; + case SEA: return EnumArt.SEA; + case SUNSET: return EnumArt.SUNSET; + case CREEBET: return EnumArt.CREEBET; + case WANDERER: return EnumArt.WANDERER; + case GRAHAM: return EnumArt.GRAHAM; + case MATCH: return EnumArt.MATCH; + case BUST: return EnumArt.BUST; + case STAGE: return EnumArt.STAGE; + case VOID: return EnumArt.VOID; + case SKULL_AND_ROSES: return EnumArt.SKULL_AND_ROSES; + case FIGHTERS: return EnumArt.FIGHTERS; + case POINTER: return EnumArt.POINTER; + case PIGSCENE: return EnumArt.PIGSCENE; + case BURNINGSKULL: return EnumArt.BURNINGSKULL; + case SKELETON: return EnumArt.SKELETON; + case DONKEYKONG: return EnumArt.DONKEYKONG; + case WITHER: return EnumArt.WITHER; + default: + throw new AssertionError(art); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftChunk.java new file mode 100644 index 0000000..a257373 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -0,0 +1,395 @@ +package org.bukkit.craftbukkit; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Map; + +import net.minecraft.server.BiomeBase; +import net.minecraft.server.ChunkPosition; +import net.minecraft.server.ChunkSection; +import net.minecraft.server.EmptyChunk; +import net.minecraft.server.IInventory; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.TileEntity; +import net.minecraft.server.WorldChunkManager; +import net.minecraft.server.WorldServer; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.Entity; +import org.bukkit.ChunkSnapshot; +import org.bukkit.entity.HumanEntity; + +public class CraftChunk implements Chunk { + private WeakReference weakChunk; + private final WorldServer worldServer; + private final int x; + private final int z; + private static final byte[] emptyData = new byte[2048]; + private static final short[] emptyBlockIDs = new short[4096]; + private static final byte[] emptySkyLight = new byte[2048]; + + public CraftChunk(net.minecraft.server.Chunk chunk) { + if (!(chunk instanceof EmptyChunk)) { + this.weakChunk = new WeakReference(chunk); + } + + worldServer = (WorldServer) getHandle().world; + x = getHandle().locX; + z = getHandle().locZ; + } + + public World getWorld() { + return worldServer.getWorld(); + } + + public CraftWorld getCraftWorld() { + return (CraftWorld) getWorld(); + } + + public net.minecraft.server.Chunk getHandle() { + net.minecraft.server.Chunk c = weakChunk.get(); + + if (c == null) { + c = worldServer.getChunkAt(x, z); + + if (!(c instanceof EmptyChunk)) { + weakChunk = new WeakReference(c); + } + } + + return c; + } + + void breakLink() { + weakChunk.clear(); + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + @Override + public String toString() { + return "CraftChunk{" + "x=" + getX() + "z=" + getZ() + '}'; + } + + public Block getBlock(int x, int y, int z) { + return new CraftBlock(this, (getX() << 4) | (x & 0xF), y & 0xFF, (getZ() << 4) | (z & 0xF)); + } + + public Entity[] getEntities() { + int count = 0, index = 0; + net.minecraft.server.Chunk chunk = getHandle(); + + for (int i = 0; i < 16; i++) { + count += chunk.entitySlices[i].size(); + } + + Entity[] entities = new Entity[count]; + + for (int i = 0; i < 16; i++) { + for (Object obj : chunk.entitySlices[i].toArray()) { + if (!(obj instanceof net.minecraft.server.Entity)) { + continue; + } + + entities[index++] = ((net.minecraft.server.Entity) obj).getBukkitEntity(); + } + } + + return entities; + } + + public BlockState[] getTileEntities() { + int index = 0; + net.minecraft.server.Chunk chunk = getHandle(); + BlockState[] entities = new BlockState[chunk.tileEntities.size()]; + + for (Object obj : chunk.tileEntities.keySet().toArray()) { + if (!(obj instanceof ChunkPosition)) { + continue; + } + + ChunkPosition position = (ChunkPosition) obj; + entities[index++] = worldServer.getWorld().getBlockAt(position.x + (chunk.locX << 4), position.y, position.z + (chunk.locZ << 4)).getState(); + } + return entities; + } + + public boolean isLoaded() { + return getWorld().isChunkLoaded(this); + } + + public boolean load() { + return getWorld().loadChunk(getX(), getZ(), true); + } + + public boolean load(boolean generate) { + return getWorld().loadChunk(getX(), getZ(), generate); + } + + public boolean unload() { + return getWorld().unloadChunk(getX(), getZ()); + } + + public boolean unload(boolean save) { + return getWorld().unloadChunk(getX(), getZ(), save); + } + + public boolean unload(boolean save, boolean safe) { + return getWorld().unloadChunk(getX(), getZ(), save, safe); + } + + public ChunkSnapshot getChunkSnapshot() { + return getChunkSnapshot(true, false, false); + } + + public ChunkSnapshot getChunkSnapshot(boolean includeMaxBlockY, boolean includeBiome, boolean includeBiomeTempRain) { + net.minecraft.server.Chunk chunk = getHandle(); + + ChunkSection[] cs = chunk.getSections(); + short[][] sectionBlockIDs = new short[cs.length][]; + byte[][] sectionBlockData = new byte[cs.length][]; + byte[][] sectionSkyLights = new byte[cs.length][]; + byte[][] sectionEmitLights = new byte[cs.length][]; + boolean[] sectionEmpty = new boolean[cs.length]; + + for (int i = 0; i < cs.length; i++) { + if (cs[i] == null) { /* Section is empty? */ + sectionBlockIDs[i] = emptyBlockIDs; + sectionBlockData[i] = emptyData; + sectionSkyLights[i] = emptySkyLight; + sectionEmitLights[i] = emptyData; + sectionEmpty[i] = true; + } else { /* Not empty */ + short[] blockids = new short[4096]; + byte[] baseids = cs[i].getIdArray(); + + /* Copy base IDs */ + for (int j = 0; j < 4096; j++) { + blockids[j] = (short) (baseids[j] & 0xFF); + } + + // MineHQ start - 1.7 has no extended block IDs + /* + if (cs[i].getExtendedIdArray() != null) { /* If we've got extended IDs *//* + byte[] extids = cs[i].getExtendedIdArray().a; + + for (int j = 0; j < 2048; j++) { + short b = (short) (extids[j] & 0xFF); + + if (b == 0) { + continue; + } + + blockids[j<<1] |= (b & 0x0F) << 8; + blockids[(j<<1)+1] |= (b & 0xF0) << 4; + } + } + */ + // MineHQ end + sectionBlockIDs[i] = blockids; + + /* Get block data nibbles */ + sectionBlockData[i] = new byte[2048]; + System.arraycopy(cs[i].getDataArray().a, 0, sectionBlockData[i], 0, 2048); + if (cs[i].getSkyLightArray() == null) { + sectionSkyLights[i] = emptyData; + } else { + sectionSkyLights[i] = new byte[2048]; + System.arraycopy(cs[i].getSkyLightArray().a, 0, sectionSkyLights[i], 0, 2048); + } + sectionEmitLights[i] = new byte[2048]; + System.arraycopy(cs[i].getEmittedLightArray().a, 0, sectionEmitLights[i], 0, 2048); + } + } + + int[] hmap = null; + + if (includeMaxBlockY) { + hmap = new int[256]; // Get copy of height map + System.arraycopy(chunk.heightMap, 0, hmap, 0, 256); + } + + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = chunk.world.getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = chunk.getBiome(i & 0xF, i >> 4, wcm); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + float[] dat = getTemperatures(wcm, getX() << 4, getZ() << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + + dat = wcm.getWetness(null, getX() << 4, getZ() << 4, 16, 16); + + for (int i = 0; i < 256; i++) { + biomeRain[i] = dat[i]; + } + } + } + + World world = getWorld(); + return new CraftChunkSnapshot(getX(), getZ(), world.getName(), world.getFullTime(), sectionBlockIDs, sectionBlockData, sectionSkyLights, sectionEmitLights, sectionEmpty, hmap, biome, biomeTemp, biomeRain); + } + + public static ChunkSnapshot getEmptyChunkSnapshot(int x, int z, CraftWorld world, boolean includeBiome, boolean includeBiomeTempRain) { + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = world.getHandle().getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = world.getHandle().getBiome((x << 4) + (i & 0xF), (z << 4) + (i >> 4)); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + float[] dat = getTemperatures(wcm, x << 4, z << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + + dat = wcm.getWetness(null, x << 4, z << 4, 16, 16); + + for (int i = 0; i < 256; i++) { + biomeRain[i] = dat[i]; + } + } + } + + /* Fill with empty data */ + int hSection = world.getMaxHeight() >> 4; + short[][] blockIDs = new short[hSection][]; + byte[][] skyLight = new byte[hSection][]; + byte[][] emitLight = new byte[hSection][]; + byte[][] blockData = new byte[hSection][]; + boolean[] empty = new boolean[hSection]; + + for (int i = 0; i < hSection; i++) { + blockIDs[i] = emptyBlockIDs; + skyLight[i] = emptySkyLight; + emitLight[i] = emptyData; + blockData[i] = emptyData; + empty[i] = true; + } + + return new CraftChunkSnapshot(x, z, world.getName(), world.getFullTime(), blockIDs, blockData, skyLight, emitLight, empty, new int[256], biome, biomeTemp, biomeRain); + } + + private static float[] getTemperatures(WorldChunkManager chunkmanager, int chunkX, int chunkZ) { + BiomeBase[] biomes = chunkmanager.getBiomes(null, chunkX, chunkZ, 16, 16); + float[] temps = new float[biomes.length]; + + for (int i = 0; i < biomes.length; i++) { + float temp = biomes[i].temperature; // Vanilla of olde: ((int) biomes[i].temperature * 65536.0F) / 65536.0F + + if (temp > 1F) { + temp = 1F; + } + + temps[i] = temp; + } + + return temps; + } + + static { + Arrays.fill(emptySkyLight, (byte) 0xFF); + } + + // MineHQ start - chunk snapshot api + @Override + public net.valorhcf.ChunkSnapshot takeSnapshot() { + net.minecraft.server.Chunk handle = getHandle(); + net.valorhcf.CraftChunkSnapshot snap = new net.valorhcf.CraftChunkSnapshot(); + + // save chunk sections to snapshot + for (int i = 0; i < 16; i++) { + if (handle.getSections()[i] != null) { + snap.getSections()[i] = handle.getSections()[i].createSnapshot(); + } + } + + // save tile entities to snapshot + for (Map.Entry entry : handle.tileEntities.entrySet()) { + NBTTagCompound nbt = new NBTTagCompound(); + entry.getValue().b(nbt); // writeToNBT + snap.getTileEntities().add(nbt); + } + return snap; + } + + @Override + public void restoreSnapshot(net.valorhcf.ChunkSnapshot snapshot) { + net.valorhcf.CraftChunkSnapshot snap = (net.valorhcf.CraftChunkSnapshot) snapshot; + net.minecraft.server.Chunk handle = getHandle(); + + // add chunk sections from snapshot + for (int i = 0; i < 16; i++) { + if (snap.getSections()[i] == null) { + handle.getSections()[i] = null; + } else { + handle.getSections()[i] = new ChunkSection(i << 4, !worldServer.worldProvider.g); + handle.getSections()[i].restoreSnapshot(snap.getSections()[i]); + } + } + + // clear tile entities currently in the chunk + for (TileEntity tileEntity : handle.tileEntities.values()) { + if (tileEntity instanceof IInventory) { + for (HumanEntity h : new ArrayList(((IInventory) tileEntity).getViewers())) { + if (h instanceof CraftHumanEntity) { + ((CraftHumanEntity) h).getHandle().closeInventory(); + } + } + } + worldServer.a(tileEntity); + } + handle.tileEntities.clear(); + + // add tile entities from snapshot + for (NBTTagCompound nbt : snap.getTileEntities()) { + // deserialize nbt to new tile entity instance + TileEntity tileEntity = TileEntity.c(nbt); + // move the tile entity into this chunk's space + tileEntity.x = (tileEntity.x & 15) | handle.locX << 4; + tileEntity.z = (tileEntity.z & 15) | handle.locZ << 4; + // add it + handle.a(tileEntity); + } + handle.n = true; // needs saving flag + worldServer.getPlayerChunkMap().resend(x, z); + } + // MineHQ end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java new file mode 100644 index 0000000..edf701b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java @@ -0,0 +1,97 @@ +package org.bukkit.craftbukkit; + +import org.bukkit.ChunkSnapshot; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.block.CraftBlock; + +import net.minecraft.server.BiomeBase; + +/** + * Represents a static, thread-safe snapshot of chunk of blocks + * 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 class CraftChunkSnapshot implements ChunkSnapshot { + private final int x, z; + private final String worldname; + private final short[][] blockids; /* Block IDs, by section */ + private final byte[][] blockdata; + private final byte[][] skylight; + private final byte[][] emitlight; + private final boolean[] empty; + private final int[] hmap; // Height map + private final long captureFulltime; + private final BiomeBase[] biome; + private final double[] biomeTemp; + private final double[] biomeRain; + + CraftChunkSnapshot(int x, int z, String wname, long wtime, short[][] sectionBlockIDs, byte[][] sectionBlockData, byte[][] sectionSkyLights, byte[][] sectionEmitLights, boolean[] sectionEmpty, int[] hmap, BiomeBase[] biome, double[] biomeTemp, double[] biomeRain) { + this.x = x; + this.z = z; + this.worldname = wname; + this.captureFulltime = wtime; + this.blockids = sectionBlockIDs; + this.blockdata = sectionBlockData; + this.skylight = sectionSkyLights; + this.emitlight = sectionEmitLights; + this.empty = sectionEmpty; + this.hmap = hmap; + this.biome = biome; + this.biomeTemp = biomeTemp; + this.biomeRain = biomeRain; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public String getWorldName() { + return worldname; + } + + public final int getBlockTypeId(int x, int y, int z) { + return blockids[y >> 4][((y & 0xF) << 8) | (z << 4) | x]; + } + + public final int getBlockData(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (blockdata[y >> 4][off] >> ((x & 1) << 2)) & 0xF; + } + + public final int getBlockSkyLight(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (skylight[y >> 4][off] >> ((x & 1) << 2)) & 0xF; + } + + public final int getBlockEmittedLight(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (emitlight[y >> 4][off] >> ((x & 1) << 2)) & 0xF; + } + + public final int getHighestBlockYAt(int x, int z) { + return hmap[z << 4 | x]; + } + + public final Biome getBiome(int x, int z) { + return CraftBlock.biomeBaseToBiome(biome[z << 4 | x]); + } + + public final double getRawBiomeTemperature(int x, int z) { + return biomeTemp[z << 4 | x]; + } + + public final double getRawBiomeRainfall(int x, int z) { + return biomeRain[z << 4 | x]; + } + + public final long getCaptureFullTime() { + return captureFulltime; + } + + public final boolean isSectionEmpty(int sy) { + return empty[sy]; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftCrashReport.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftCrashReport.java new file mode 100644 index 0000000..e08afce --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftCrashReport.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; + +import net.minecraft.server.MinecraftServer; + +public class CraftCrashReport implements Callable { + + public Object call() throws Exception { + StringWriter value = new StringWriter(); + try { + value.append("\n Running: ").append(Bukkit.getName()).append(" version ").append(Bukkit.getVersion()).append(" (Implementing API version ").append(Bukkit.getBukkitVersion()).append(") ").append(String.valueOf(MinecraftServer.getServer().getOnlineMode())); + value.append("\n Plugins: {"); + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + PluginDescriptionFile description = plugin.getDescription(); + value.append(' ').append(description.getFullName()).append(' ').append(description.getMain()).append(' ').append(Arrays.toString(description.getAuthors().toArray())).append(','); + } + value.append("}\n Warnings: ").append(Bukkit.getWarningState().name()); + value.append("\n Threads: {"); + for (Map.Entry entry : Thread.getAllStackTraces().entrySet()) { + value.append(' ').append(entry.getKey().getState().name()).append(' ').append(entry.getKey().getName()).append(": ").append(Arrays.toString(entry.getValue())).append(','); + } + value.append("}\n ").append(Bukkit.getScheduler().toString()); + } catch (Throwable t) { + value.append("\n Failed to handle CraftCrashReport:\n"); + PrintWriter writer = new PrintWriter(value); + t.printStackTrace(writer); + writer.flush(); + } + return value.toString(); + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftEffect.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftEffect.java new file mode 100644 index 0000000..7eca388 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftEffect.java @@ -0,0 +1,65 @@ +package org.bukkit.craftbukkit; + +import org.apache.commons.lang.Validate; +import org.bukkit.Effect; +import org.bukkit.Material; +import org.bukkit.block.BlockFace; +import org.bukkit.potion.Potion; + +public class CraftEffect { + public static int getDataValue(Effect effect, T data) { + int datavalue; + switch(effect) { + case POTION_BREAK: + datavalue = ((Potion) data).toDamageValue() & 0x3F; + break; + case RECORD_PLAY: + Validate.isTrue(((Material) data).isRecord(), "Invalid record type!"); + datavalue = ((Material) data).getId(); + break; + case SMOKE: + switch((BlockFace) data) { // TODO: Verify (Where did these values come from...?) + case SOUTH_EAST: + datavalue = 0; + break; + case SOUTH: + datavalue = 1; + break; + case SOUTH_WEST: + datavalue = 2; + break; + case EAST: + datavalue = 3; + break; + case UP: + case SELF: + datavalue = 4; + break; + case WEST: + datavalue = 5; + break; + case NORTH_EAST: + datavalue = 6; + break; + case NORTH: + datavalue = 7; + break; + case NORTH_WEST: + datavalue = 8; + break; + default: + throw new IllegalArgumentException("Bad smoke direction!"); + } + break; + case STEP_SOUND: + Validate.isTrue(((Material) data).isBlock(), "Material is not a block!"); + datavalue = ((Material) data).getId(); + break; + case ITEM_BREAK: + datavalue = ((Material) data).getId(); + default: + datavalue = 0; + } + return datavalue; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftIpBanEntry.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftIpBanEntry.java new file mode 100644 index 0000000..42085f8 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftIpBanEntry.java @@ -0,0 +1,86 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.IpBanEntry; +import net.minecraft.server.IpBanList; +import net.minecraft.server.MinecraftServer; + +import java.io.IOException; +import java.util.Date; + +public final class CraftIpBanEntry implements org.bukkit.BanEntry { + private final IpBanList list; + private final String target; + private Date created; + private String source; + private Date expiration; + private String reason; + + public CraftIpBanEntry(String target, IpBanEntry entry, IpBanList list) { + this.list = list; + this.target = target; + this.created = entry.getCreated() != null ? new Date(entry.getCreated().getTime()) : null; + this.source = entry.getSource(); + this.expiration = entry.getExpires() != null ? new Date(entry.getExpires().getTime()) : null; + this.reason = entry.getReason(); + } + + @Override + public String getTarget() { + return this.target; + } + + @Override + public Date getCreated() { + return this.created == null ? null : (Date) this.created.clone(); + } + + @Override + public void setCreated(Date created) { + this.created = created; + } + + @Override + public String getSource() { + return this.source; + } + + @Override + public void setSource(String source) { + this.source = source; + } + + @Override + public Date getExpiration() { + return this.expiration == null ? null : (Date) this.expiration.clone(); + } + + @Override + public void setExpiration(Date expiration) { + if (expiration != null && expiration.getTime() == new Date(0, 0, 0, 0, 0, 0).getTime()) { + expiration = null; // Forces "forever" + } + + this.expiration = expiration; + } + + @Override + public String getReason() { + return this.reason; + } + + @Override + public void setReason(String reason) { + this.reason = reason; + } + + @Override + public void save() { + IpBanEntry entry = new IpBanEntry(target, this.created, this.source, this.expiration, this.reason); + this.list.add(entry); + try { + this.list.save(); + } catch (IOException ex) { + MinecraftServer.getLogger().error("Failed to save banned-ips.json, " + ex.getMessage()); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftIpBanList.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftIpBanList.java new file mode 100644 index 0000000..feb679c --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftIpBanList.java @@ -0,0 +1,77 @@ +package org.bukkit.craftbukkit; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Date; +import java.util.Set; + +import net.minecraft.server.IpBanEntry; +import net.minecraft.server.IpBanList; +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; + +import com.google.common.collect.ImmutableSet; + +public class CraftIpBanList implements org.bukkit.BanList { + private final IpBanList list; + + public CraftIpBanList(IpBanList list) { + this.list = list; + } + + @Override + public org.bukkit.BanEntry getBanEntry(String target) { + Validate.notNull(target, "Target cannot be null"); + + IpBanEntry entry = (IpBanEntry) list.get(target); + if (entry == null) { + return null; + } + + return new CraftIpBanEntry(target, entry, list); + } + + @Override + public org.bukkit.BanEntry addBan(String target, String reason, Date expires, String source) { + Validate.notNull(target, "Ban target cannot be null"); + + IpBanEntry entry = new IpBanEntry(target, new Date(), + StringUtils.isBlank(source) ? null : source, expires, + StringUtils.isBlank(reason) ? null : reason); + + list.add(entry); + + try { + list.save(); + } catch (IOException ex) { + MinecraftServer.getLogger().error("Failed to save banned-ips.json, " + ex.getMessage()); + } + + return new CraftIpBanEntry(target, entry, list); + } + + @Override + public Set getBanEntries() { + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (String target : list.getEntries()) { + builder.add(new CraftIpBanEntry(target, (IpBanEntry) list.get(target), list)); + } + + return builder.build(); + } + + @Override + public boolean isBanned(String target) { + Validate.notNull(target, "Target cannot be null"); + + return list.isBanned(InetSocketAddress.createUnresolved(target, 0)); + } + + @Override + public void pardon(String target) { + Validate.notNull(target, "Target cannot be null"); + + list.remove(target); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java new file mode 100644 index 0000000..f1fa713 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -0,0 +1,264 @@ +package org.bukkit.craftbukkit; + +import java.io.File; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.WorldNBTStorage; + +import net.minecraft.util.com.mojang.authlib.GameProfile; +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.entity.Player; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; + +@SerializableAs("Player") +public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable { + private final GameProfile profile; + private final CraftServer server; + private final WorldNBTStorage storage; + + protected CraftOfflinePlayer(CraftServer server, GameProfile profile) { + this.server = server; + this.profile = profile; + this.storage = (WorldNBTStorage) (server.console.worlds.get(0).getDataManager()); + + } + + public GameProfile getProfile() { + return profile; + } + + public boolean isOnline() { + return getPlayer() != null; + } + + public String getName() { + Player player = getPlayer(); + if (player != null) { + return player.getName(); + } + + // This might not match lastKnownName but if not it should be more correct + if (profile.getName() != null) { + return profile.getName(); + } + + NBTTagCompound data = getBukkitData(); + + if (data != null) { + if (data.hasKey("lastKnownName")) { + return data.getString("lastKnownName"); + } + } + + return null; + } + + public UUID getUniqueId() { + return profile.getId(); + } + + public Server getServer() { + return server; + } + + public boolean isOp() { + return server.getHandle().isOp(profile); + } + + public void setOp(boolean value) { + if (value == isOp()) { + return; + } + + if (value) { + server.getHandle().addOp(profile); + } else { + server.getHandle().removeOp(profile); + } + } + + public boolean isBanned() { + if (getName() == null) { + return false; + } + + return server.getBanList(BanList.Type.NAME).isBanned(getName()); + } + + public void setBanned(boolean value) { + if (getName() == null) { + return; + } + + if (value) { + server.getBanList(BanList.Type.NAME).addBan(getName(), null, null, null); + } else { + server.getBanList(BanList.Type.NAME).pardon(getName()); + } + } + + public boolean isWhitelisted() { + return server.getHandle().getWhitelist().isWhitelisted(profile); + } + + public void setWhitelisted(boolean value) { + if (value) { + server.getHandle().addWhitelist(profile); + } else { + server.getHandle().removeWhitelist(profile); + } + } + + public Map serialize() { + Map result = new LinkedHashMap(); + + result.put("UUID", profile.getId().toString()); + + return result; + } + + public static OfflinePlayer deserialize(Map args) { + // Backwards comparability + if (args.get("name") != null) { + return Bukkit.getServer().getOfflinePlayer((String) args.get("name")); + } + + return Bukkit.getServer().getOfflinePlayer(UUID.fromString((String) args.get("UUID"))); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[UUID=" + profile.getId() + "]"; + } + + public Player getPlayer() { + // PaperSpigot - Improved player lookup, replace entire method + final EntityPlayer playerEntity = server.getHandle().uuidMap.get(getUniqueId()); + return playerEntity != null ? playerEntity.getBukkitEntity() : null; + // PaperSpigot end + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof OfflinePlayer)) { + return false; + } + + OfflinePlayer other = (OfflinePlayer) obj; + if ((this.getUniqueId() == null) || (other.getUniqueId() == null)) { + return false; + } + + return this.getUniqueId().equals(other.getUniqueId()); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + (this.getUniqueId() != null ? this.getUniqueId().hashCode() : 0); + return hash; + } + + private NBTTagCompound getData() { + return storage.getPlayerData(getUniqueId().toString()); + } + + private NBTTagCompound getBukkitData() { + NBTTagCompound result = getData(); + + if (result != null) { + if (!result.hasKey("bukkit")) { + result.set("bukkit", new NBTTagCompound()); + } + result = result.getCompound("bukkit"); + } + + return result; + } + + private File getDataFile() { + return new File(storage.getPlayerDir(), getUniqueId() + ".dat"); + } + + public long getFirstPlayed() { + Player player = getPlayer(); + if (player != null) return player.getFirstPlayed(); + + NBTTagCompound data = getBukkitData(); + + if (data != null) { + if (data.hasKey("firstPlayed")) { + return data.getLong("firstPlayed"); + } else { + File file = getDataFile(); + return file.lastModified(); + } + } else { + return 0; + } + } + + public long getLastPlayed() { + Player player = getPlayer(); + if (player != null) return player.getLastPlayed(); + + NBTTagCompound data = getBukkitData(); + + if (data != null) { + if (data.hasKey("lastPlayed")) { + return data.getLong("lastPlayed"); + } else { + File file = getDataFile(); + return file.lastModified(); + } + } else { + return 0; + } + } + + public boolean hasPlayedBefore() { + return getData() != null; + } + + public Location getBedSpawnLocation() { + NBTTagCompound data = getData(); + if (data == null) return null; + + if (data.hasKey("SpawnX") && data.hasKey("SpawnY") && data.hasKey("SpawnZ")) { + String spawnWorld = data.getString("SpawnWorld"); + if (spawnWorld.equals("")) { + spawnWorld = server.getWorlds().get(0).getName(); + } + return new Location(server.getWorld(spawnWorld), data.getInt("SpawnX"), data.getInt("SpawnY"), data.getInt("SpawnZ")); + } + return null; + } + + public void setMetadata(String metadataKey, MetadataValue metadataValue) { + server.getPlayerMetadata().setMetadata(this, metadataKey, metadataValue); + } + + public List getMetadata(String metadataKey) { + return server.getPlayerMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return server.getPlayerMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin plugin) { + server.getPlayerMetadata().removeMetadata(this, metadataKey, plugin); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftProfileBanEntry.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftProfileBanEntry.java new file mode 100644 index 0000000..7ec0006 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftProfileBanEntry.java @@ -0,0 +1,87 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.GameProfileBanEntry; +import net.minecraft.server.GameProfileBanList; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +import java.io.IOException; +import java.util.Date; + +public final class CraftProfileBanEntry implements org.bukkit.BanEntry { + private final GameProfileBanList list; + private final GameProfile profile; + private Date created; + private String source; + private Date expiration; + private String reason; + + public CraftProfileBanEntry(GameProfile profile, GameProfileBanEntry entry, GameProfileBanList list) { + this.list = list; + this.profile = profile; + this.created = entry.getCreated() != null ? new Date(entry.getCreated().getTime()) : null; + this.source = entry.getSource(); + this.expiration = entry.getExpires() != null ? new Date(entry.getExpires().getTime()) : null; + this.reason = entry.getReason(); + } + + @Override + public String getTarget() { + return this.profile.getName(); + } + + @Override + public Date getCreated() { + return this.created == null ? null : (Date) this.created.clone(); + } + + @Override + public void setCreated(Date created) { + this.created = created; + } + + @Override + public String getSource() { + return this.source; + } + + @Override + public void setSource(String source) { + this.source = source; + } + + @Override + public Date getExpiration() { + return this.expiration == null ? null : (Date) this.expiration.clone(); + } + + @Override + public void setExpiration(Date expiration) { + if (expiration != null && expiration.getTime() == new Date(0, 0, 0, 0, 0, 0).getTime()) { + expiration = null; // Forces "forever" + } + + this.expiration = expiration; + } + + @Override + public String getReason() { + return this.reason; + } + + @Override + public void setReason(String reason) { + this.reason = reason; + } + + @Override + public void save() { + GameProfileBanEntry entry = new GameProfileBanEntry(profile, this.created, this.source, this.expiration, this.reason); + this.list.add(entry); + try { + this.list.save(); + } catch (IOException ex) { + MinecraftServer.getLogger().error("Failed to save banned-players.json, " + ex.getMessage()); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftProfileBanList.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftProfileBanList.java new file mode 100644 index 0000000..fad6a96 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftProfileBanList.java @@ -0,0 +1,96 @@ +package org.bukkit.craftbukkit; + +import java.io.IOException; +import java.util.Date; +import java.util.Set; + +import net.minecraft.server.GameProfileBanEntry; +import net.minecraft.server.GameProfileBanList; +import net.minecraft.server.JsonListEntry; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; + +import com.google.common.collect.ImmutableSet; + +public class CraftProfileBanList implements org.bukkit.BanList { + private final GameProfileBanList list; + + public CraftProfileBanList(GameProfileBanList list){ + this.list = list; + } + + @Override + public org.bukkit.BanEntry getBanEntry(String target) { + Validate.notNull(target, "Target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + if (profile == null) { + return null; + } + + GameProfileBanEntry entry = (GameProfileBanEntry) list.get(profile); + if (entry == null) { + return null; + } + + return new CraftProfileBanEntry(profile, entry, list); + } + + @Override + public org.bukkit.BanEntry addBan(String target, String reason, Date expires, String source) { + Validate.notNull(target, "Ban target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + if (profile == null) { + return null; + } + + GameProfileBanEntry entry = new GameProfileBanEntry(profile, new Date(), + StringUtils.isBlank(source) ? null : source, expires, + StringUtils.isBlank(reason) ? null : reason); + + list.add(entry); + + try { + list.save(); + } catch (IOException ex) { + MinecraftServer.getLogger().error("Failed to save banned-players.json, " + ex.getMessage()); + } + + return new CraftProfileBanEntry(profile, entry, list); + } + + @Override + public Set getBanEntries() { + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (JsonListEntry entry : list.getValues()) { + GameProfile profile = (GameProfile) entry.getKey(); + builder.add(new CraftProfileBanEntry(profile, (GameProfileBanEntry) entry, list)); + } + + return builder.build(); + } + + @Override + public boolean isBanned(String target) { + Validate.notNull(target, "Target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + if (profile == null) { + return false; + } + + return list.isBanned(profile); + } + + @Override + public void pardon(String target) { + Validate.notNull(target, "Target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + list.remove(profile); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java new file mode 100644 index 0000000..3781800 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -0,0 +1,1982 @@ +package org.bukkit.craftbukkit; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import javax.imageio.ImageIO; + +import net.md_5.bungee.api.chat.BaseComponent; +import net.minecraft.server.ChunkCoordinates; +import net.minecraft.server.CommandAchievement; +import net.minecraft.server.CommandBan; +import net.minecraft.server.CommandBanIp; +import net.minecraft.server.CommandBanList; +import net.minecraft.server.CommandClear; +import net.minecraft.server.CommandDeop; +import net.minecraft.server.CommandDifficulty; +import net.minecraft.server.CommandEffect; +import net.minecraft.server.CommandEnchant; +import net.minecraft.server.CommandGamemode; +import net.minecraft.server.CommandGamemodeDefault; +import net.minecraft.server.CommandGamerule; +import net.minecraft.server.CommandGive; +import net.minecraft.server.CommandHelp; +import net.minecraft.server.CommandIdleTimeout; +import net.minecraft.server.CommandKick; +import net.minecraft.server.CommandKill; +import net.minecraft.server.CommandList; +import net.minecraft.server.CommandMe; +import net.minecraft.server.CommandNetstat; +import net.minecraft.server.CommandOp; +import net.minecraft.server.CommandPardon; +import net.minecraft.server.CommandPardonIP; +import net.minecraft.server.CommandPlaySound; +import net.minecraft.server.CommandSay; +import net.minecraft.server.CommandScoreboard; +import net.minecraft.server.CommandSeed; +import net.minecraft.server.CommandSetBlock; +import net.minecraft.server.CommandSetWorldSpawn; +import net.minecraft.server.CommandSpawnpoint; +import net.minecraft.server.CommandSpreadPlayers; +import net.minecraft.server.CommandSummon; +import net.minecraft.server.CommandTell; +import net.minecraft.server.CommandTellRaw; +import net.minecraft.server.CommandTestFor; +import net.minecraft.server.CommandTestForBlock; +import net.minecraft.server.CommandTime; +import net.minecraft.server.CommandToggleDownfall; +import net.minecraft.server.CommandTp; +import net.minecraft.server.CommandWeather; +import net.minecraft.server.CommandWhitelist; +import net.minecraft.server.CommandXp; +import net.minecraft.server.Convertable; +import net.minecraft.server.ConvertProgressUpdater; +import net.minecraft.server.CraftingManager; +import net.minecraft.server.DedicatedPlayerList; +import net.minecraft.server.DedicatedServer; +import net.minecraft.server.Enchantment; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.EntityTracker; +import net.minecraft.server.EnumDifficulty; +import net.minecraft.server.EnumGamemode; +import net.minecraft.server.ExceptionWorldConflict; +import net.minecraft.server.Items; +import net.minecraft.server.JsonListEntry; +import net.minecraft.server.PlayerList; +import net.minecraft.server.RecipesFurnace; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.MobEffectList; +import net.minecraft.server.PropertyManager; +import net.minecraft.server.ServerCommand; +import net.minecraft.server.RegionFile; +import net.minecraft.server.RegionFileCache; +import net.minecraft.server.ServerNBTManager; +import net.minecraft.server.WorldLoaderServer; +import net.minecraft.server.WorldManager; +import net.minecraft.server.WorldMap; +import net.minecraft.server.PersistentCollection; +import net.minecraft.server.WorldNBTStorage; +import net.minecraft.server.WorldServer; +import net.minecraft.server.WorldSettings; +import net.minecraft.server.WorldType; +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.buffer.ByteBufOutputStream; +import net.minecraft.util.io.netty.buffer.Unpooled; +import net.minecraft.util.io.netty.handler.codec.base64.Base64; + +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.Warning.WarningState; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.WorldCreator; +import org.bukkit.command.Command; +import org.bukkit.command.CommandException; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.command.defaults.VanillaCommand; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.conversations.Conversable; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.help.SimpleHelpMap; +import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe; +import org.bukkit.craftbukkit.inventory.CraftInventoryCustom; +import org.bukkit.craftbukkit.inventory.CraftItemFactory; +import org.bukkit.craftbukkit.inventory.CraftRecipe; +import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; +import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; +import org.bukkit.craftbukkit.inventory.RecipeIterator; +import org.bukkit.craftbukkit.map.CraftMapView; +import org.bukkit.craftbukkit.metadata.EntityMetadataStore; +import org.bukkit.craftbukkit.metadata.PlayerMetadataStore; +import org.bukkit.craftbukkit.metadata.WorldMetadataStore; +import org.bukkit.craftbukkit.potion.CraftPotionBrewer; +import org.bukkit.craftbukkit.scheduler.CraftScheduler; +import org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager; +import org.bukkit.craftbukkit.updater.AutoUpdater; +import org.bukkit.craftbukkit.updater.BukkitDLUpdaterService; +import org.bukkit.craftbukkit.util.CraftIconCache; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.DatFileFilter; +import org.bukkit.craftbukkit.util.Versioning; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerChatTabCompleteEvent; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldSaveEvent; +import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.help.HelpMap; +import org.bukkit.inventory.FurnaceRecipe; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.Recipe; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.ShapelessRecipe; +import org.bukkit.permissions.Permissible; +import org.bukkit.permissions.Permission; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginLoadOrder; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.ServicesManager; +import org.bukkit.plugin.SimplePluginManager; +import org.bukkit.plugin.SimpleServicesManager; +import org.bukkit.plugin.java.JavaPluginLoader; +import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.potion.Potion; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.scheduler.BukkitWorker; +import org.bukkit.util.StringUtil; +import org.bukkit.util.permissions.DefaultPermissions; +import org.spigotmc.SpigotConfig; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.error.MarkedYAMLException; +import org.apache.commons.lang.Validate; + +import com.avaje.ebean.config.DataSourceConfig; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.SQLitePlatform; +import com.avaje.ebeaninternal.server.lib.sql.TransactionIsolation; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.MapMaker; + +import jline.console.ConsoleReader; + +public final class CraftServer implements Server { + private static final Player[] EMPTY_PLAYER_ARRAY = new Player[0]; + private final String serverName = "CraftBukkit"; + private String serverGroup = "Dev"; // PowerSpigot + private final String serverVersion; + private final String bukkitVersion = Versioning.getBukkitVersion(); + private final Logger logger = Logger.getLogger("Minecraft"); + private final ServicesManager servicesManager = new SimpleServicesManager(); + private final CraftScheduler scheduler = new CraftScheduler(); + private final SimpleCommandMap commandMap = new SimpleCommandMap(this); + private final SimpleHelpMap helpMap = new SimpleHelpMap(this); + private final StandardMessenger messenger = new StandardMessenger(); + private final PluginManager pluginManager = new SimplePluginManager(this, commandMap); + protected final MinecraftServer console; + protected final DedicatedPlayerList playerList; + private final Map worlds = new LinkedHashMap(); + private YamlConfiguration configuration; + private YamlConfiguration commandsConfiguration; + private final Yaml yaml = new Yaml(new SafeConstructor()); + private final Map offlinePlayers = new MapMaker().softValues().makeMap(); + private final AutoUpdater updater; + private final EntityMetadataStore entityMetadata = new EntityMetadataStore(); + private final PlayerMetadataStore playerMetadata = new PlayerMetadataStore(); + private final WorldMetadataStore worldMetadata = new WorldMetadataStore(); + private int monsterSpawn = -1; + private int animalSpawn = -1; + private int waterAnimalSpawn = -1; + private int ambientSpawn = -1; + public int chunkGCPeriod = -1; + public int chunkGCLoadThresh = 0; + private File container; + private WarningState warningState = WarningState.DEFAULT; + private final BooleanWrapper online = new BooleanWrapper(); + public CraftScoreboardManager scoreboardManager; + public boolean playerCommandState; + private boolean printSaveWarning; + private CraftIconCache icon; + private boolean overrideAllCommandBlockCommands = false; + private final Pattern validUserPattern = Pattern.compile("^[a-zA-Z0-9_]{2,16}$"); + private final UUID invalidUserUUID = UUID.nameUUIDFromBytes("InvalidUsername".getBytes(Charsets.UTF_8)); + private final List playerView; + + private final class BooleanWrapper { + private boolean value = true; + } + + static { + ConfigurationSerialization.registerClass(CraftOfflinePlayer.class); + CraftItemFactory.instance(); + } + + public CraftServer(MinecraftServer console, PlayerList playerList) { + this.console = console; + this.playerList = (DedicatedPlayerList) playerList; + this.playerView = Collections.unmodifiableList(net.minecraft.util.com.google.common.collect.Lists.transform(playerList.players, new net.minecraft.util.com.google.common.base.Function() { + @Override + public CraftPlayer apply(EntityPlayer player) { + return player.getBukkitEntity(); + } + })); + this.serverVersion = CraftServer.class.getPackage().getImplementationVersion(); + online.value = console.getPropertyManager().getBoolean("online-mode", true); + + Bukkit.setServer(this); + + // Register all the Enchantments and PotionTypes now so we can stop new registration immediately after + Enchantment.DAMAGE_ALL.getClass(); + org.bukkit.enchantments.Enchantment.stopAcceptingRegistrations(); + + Potion.setPotionBrewer(new CraftPotionBrewer()); + MobEffectList.BLINDNESS.getClass(); + PotionEffectType.stopAcceptingRegistrations(); + // Ugly hack :( + + if (!Main.useConsole) { + getLogger().info("Console input is disabled due to --noconsole command argument"); + } + + configuration = YamlConfiguration.loadConfiguration(getConfigFile()); + configuration.options().copyDefaults(true); + configuration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/bukkit.yml"), Charsets.UTF_8))); + ConfigurationSection legacyAlias = null; + if (!configuration.isString("aliases")) { + legacyAlias = configuration.getConfigurationSection("aliases"); + configuration.set("aliases", "now-in-commands.yml"); + } + saveConfig(); + if (getCommandsConfigFile().isFile()) { + legacyAlias = null; + } + commandsConfiguration = YamlConfiguration.loadConfiguration(getCommandsConfigFile()); + commandsConfiguration.options().copyDefaults(true); + commandsConfiguration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/commands.yml"), Charsets.UTF_8))); + saveCommandsConfig(); + + // Migrate aliases from old file and add previously implicit $1- to pass all arguments + if (legacyAlias != null) { + ConfigurationSection aliases = commandsConfiguration.createSection("aliases"); + for (String key : legacyAlias.getKeys(false)) { + ArrayList commands = new ArrayList(); + + if (legacyAlias.isList(key)) { + for (String command : legacyAlias.getStringList(key)) { + commands.add(command + " $1-"); + } + } else { + commands.add(legacyAlias.getString(key) + " $1-"); + } + + aliases.set(key, commands); + } + } + + saveCommandsConfig(); + overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*"); + ((SimplePluginManager) pluginManager).useTimings(configuration.getBoolean("settings.plugin-profiling")); + monsterSpawn = configuration.getInt("spawn-limits.monsters"); + animalSpawn = configuration.getInt("spawn-limits.animals"); + waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals"); + ambientSpawn = configuration.getInt("spawn-limits.ambient"); + console.autosavePeriod = configuration.getInt("ticks-per.autosave"); + warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); + chunkGCPeriod = configuration.getInt("chunk-gc.period-in-ticks"); + chunkGCLoadThresh = configuration.getInt("chunk-gc.load-threshold"); + loadIcon(); + + updater = new AutoUpdater(new BukkitDLUpdaterService(configuration.getString("auto-updater.host")), getLogger(), configuration.getString("auto-updater.preferred-channel")); + updater.setEnabled(false); // Spigot + updater.setSuggestChannels(configuration.getBoolean("auto-updater.suggest-channels")); + updater.getOnBroken().addAll(configuration.getStringList("auto-updater.on-broken")); + updater.getOnUpdate().addAll(configuration.getStringList("auto-updater.on-update")); + updater.check(serverVersion); + + // Spigot Start - Moved to old location of new DedicatedPlayerList in DedicatedServer + // loadPlugins(); + // enablePlugins(PluginLoadOrder.STARTUP); + // Spigot End + } + + public boolean getCommandBlockOverride(String command) { + return overrideAllCommandBlockCommands || commandsConfiguration.getStringList("command-block-overrides").contains(command); + } + + private File getConfigFile() { + return (File) console.options.valueOf("bukkit-settings"); + } + + private File getCommandsConfigFile() { + return (File) console.options.valueOf("commands-settings"); + } + + private void saveConfig() { + try { + configuration.save(getConfigFile()); + } catch (IOException ex) { + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + getConfigFile(), ex); + } + } + + private void saveCommandsConfig() { + try { + commandsConfiguration.save(getCommandsConfigFile()); + } catch (IOException ex) { + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + getCommandsConfigFile(), ex); + } + } + + // SportBukkit start + public boolean getRequireAllPlugins() { + return this.configuration.getBoolean("settings.require-all-plugins"); + } + + private void pluginFailedToLoad(Plugin plugin) { + if(getRequireAllPlugins()) { + throw new RuntimeException("Required plugin " + plugin.getDescription().getFullName() + " failed to load (server will shutdown)"); + } + } + // SportBukkit end + + public void loadPlugins() { + pluginManager.registerInterface(JavaPluginLoader.class); + + File pluginFolder = (File) console.options.valueOf("plugins"); + + if (pluginFolder.exists()) { + Plugin[] plugins = pluginManager.loadPlugins(pluginFolder); + for (Plugin plugin : plugins) { + try { + String message = String.format("Loading %s", plugin.getDescription().getFullName()); + plugin.getLogger().info(message); + plugin.onLoad(); + } catch (RuntimeException ex) { // SportBukkit + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, ex.getMessage() + " initializing " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + pluginFailedToLoad(plugin); // SportBukkit + } + } + } else { + pluginFolder.mkdir(); + } + } + + public void enablePlugins(PluginLoadOrder type) { + if (type == PluginLoadOrder.STARTUP) { + helpMap.clear(); + helpMap.initializeGeneralTopics(); + } + + Plugin[] plugins = pluginManager.getPlugins(); + + for (Plugin plugin : plugins) { + if ((!plugin.isEnabled()) && (plugin.getDescription().getLoad() == type)) { + loadPlugin(plugin); + } + } + + if (type == PluginLoadOrder.POSTWORLD) { + // Spigot start - Allow vanilla commands to be forced to be the main command + setVanillaCommands(true); + commandMap.setFallbackCommands(); + setVanillaCommands(false); + // Spigot end + commandMap.registerServerAliases(); + loadCustomPermissions(); + DefaultPermissions.registerCorePermissions(); + helpMap.initializeCommands(); + } + } + + public void disablePlugins() { + pluginManager.disablePlugins(); + } + + // Spigot start + private void tryRegister(VanillaCommandWrapper commandWrapper, boolean first) { + if (org.spigotmc.SpigotConfig.replaceCommands.contains( commandWrapper.getName() ) ) { + if (first) { + commandMap.register( "minecraft", commandWrapper ); + } + } else if (!first) { + commandMap.register( "minecraft", commandWrapper ); + } + } + + private void setVanillaCommands(boolean first) + { + tryRegister( new VanillaCommandWrapper( new CommandAchievement(), "/achievement give [player]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandBan(), "/ban [reason]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandBanIp(), "/ban-ip " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandBanList(), "/banlist [ips]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandClear(), "/clear [item] [metadata]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandGamemodeDefault(), "/defaultgamemode " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandDeop(), "/deop " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandDifficulty(), "/difficulty " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandEffect(), "/effect [seconds] [amplifier]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandEnchant(), "/enchant [enchantment level]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandGamemode(), "/gamemode [player]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandGamerule(), "/gamerule [true|false]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandGive(), "/give [amount] [metadata] [dataTag]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandHelp(), "/help [page|commandname]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandIdleTimeout(), "/setidletimeout " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandKick(), "/kick [reason]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandKill(), "/kill [playername]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandList(), "/list" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandMe(), "/me " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandOp(), "/op " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandPardon(), "/pardon " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandPardonIP(), "/pardon-ip " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandPlaySound(), "/playsound [x] [y] [z] [volume] [pitch] [minimumVolume]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandSay(), "/say " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandScoreboard(), "/scoreboard" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandSeed(), "/seed" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandSetBlock(), "/setblock [datavalue] [oldblockHandling] [dataTag]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandSetWorldSpawn(), "/setworldspawn [x] [y] [z]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandSpawnpoint(), "/spawnpoint [x] [y] [z]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandSpreadPlayers(), "/spreadplayers [spreadDistance] [maxRange] [respectTeams] " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandSummon(), "/summon [x] [y] [z] [dataTag]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandTp(), "/tp [player] \n/tp [player] " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandTell(), "/tell " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandTellRaw(), "/tellraw " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandTestFor(), "/testfor [dataTag]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandTestForBlock(), "/testforblock [datavalue] [dataTag]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandTime(), "/time set \n/time add " ), first ); + tryRegister( new VanillaCommandWrapper( new CommandToggleDownfall(), "/toggledownfall" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandWeather(), "/weather [duration in seconds]" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandWhitelist(), "/whitelist (add|remove) \n/whitelist (on|off|list|reload)" ), first ); + tryRegister( new VanillaCommandWrapper( new CommandXp(), "/xp [player]\n/xp L [player]" ), first ); + // This is what is in the lang file, I swear. + tryRegister( new VanillaCommandWrapper(new CommandNetstat(), "/list"), first ); + } + // Spigot end + + private void loadPlugin(Plugin plugin) { + try { + pluginManager.enablePlugin(plugin); + + List perms = plugin.getDescription().getPermissions(); + + for (Permission perm : perms) { + try { + pluginManager.addPermission(perm); + } catch (IllegalArgumentException ex) { + getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex); + } + } + } catch (RuntimeException ex) { // SportBukkit + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, ex.getMessage() + " loading " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + + // SportBukkit start + if(!plugin.isEnabled()) { + pluginFailedToLoad(plugin); + } + // SportBukkit end + } + + @Override + public String getName() { + return serverName; + } + + @Override + public String getVersion() { + return serverVersion + " (MC: " + console.getVersion() + ")"; + } + + @Override + public String getBukkitVersion() { + return bukkitVersion; + } + + @Override + @Deprecated + @SuppressWarnings("unchecked") + public Player[] _INVALID_getOnlinePlayers() { + return getOnlinePlayers().toArray(EMPTY_PLAYER_ARRAY); + } + + @Override + public List getOnlinePlayers() { + return this.playerView; + } + + @Override + @Deprecated + public Player getPlayer(final String name) { + Validate.notNull(name, "Name cannot be null"); + + // PaperSpigot start - Improved player lookup changes + Player found = getPlayerExact(name); + if (found != null) { + return found; + } + // PaperSpigot end + String lowerName = name.toLowerCase(); + int delta = Integer.MAX_VALUE; + for (Player player : getOnlinePlayers()) { + if (player.getName().toLowerCase().startsWith(lowerName)) { + int curDelta = player.getName().length() - lowerName.length(); + if (curDelta < delta) { + found = player; + delta = curDelta; + } + if (curDelta == 0) break; + } + } + return found; + } + + @Override + @Deprecated + public Player getPlayerExact(String name) { + // MineHQ start - Take disguises into account, replace whole method. + Validate.notNull(name, "Name cannot be null"); + + // PaperSpigot's optimization start + EntityPlayer player = playerList.playerMap.get(name); + if (player != null) { + return player.getBukkitEntity(); + } + // PaperSpigot's optimization end + + player = playerList.disguisePlayerMap.get(name); + + return player != null ? player.getBukkitEntity() : null; + // MineHQ end + } + + @Override + public Player getPlayerExactByDisguise(String name) { + EntityPlayer player = playerList.disguisePlayerMap.get(name); + return player != null ? player.getBukkitEntity() : null; + } + // MineHQ end + + @Override + public Player getPlayer(UUID id) { + EntityPlayer player = playerList.uuidMap.get(id); + return player != null ? player.getBukkitEntity() : null; + } + + @Override + public int broadcastMessage(String message) { + int userCount = 0; + + for (CraftPlayer onlinePlayer : this.getOnlinePlayers()) { + onlinePlayer.sendMessage(message); + userCount++; + } + + return userCount; + } + + @Override + public int broadcastTranslate(String message) { + int userCount = 0; + + for (CraftPlayer onlinePlayer : this.getOnlinePlayers()) { + onlinePlayer.sendMessage(message); + userCount++; + } + + return userCount; + } + + public Player getPlayer(final EntityPlayer entity) { + return entity.playerConnection.getPlayer(); + } + + @Override + @Deprecated + public List matchPlayer(String partialName) { + Validate.notNull(partialName, "PartialName cannot be null"); + + List matchedPlayers = new ArrayList(); + + for (Player iterPlayer : this.getOnlinePlayers()) { + String iterPlayerName = iterPlayer.getName(); + + if (partialName.equalsIgnoreCase(iterPlayerName)) { + // Exact match + matchedPlayers.clear(); + matchedPlayers.add(iterPlayer); + break; + } + if (iterPlayerName.toLowerCase().contains(partialName.toLowerCase())) { + // Partial match + matchedPlayers.add(iterPlayer); + } + } + + return matchedPlayers; + } + + @Override + public int getMaxPlayers() { + return playerList.getMaxPlayers(); + } + + // NOTE: These are dependent on the corrisponding call in MinecraftServer + // so if that changes this will need to as well + @Override + public int getPort() { + return this.getConfigInt("server-port", 25565); + } + + @Override + public int getViewDistance() { + return this.getConfigInt("view-distance", 10); + } + + @Override + public String getIp() { + return this.getConfigString("server-ip", ""); + } + + @Override + public String getServerName() { + return this.getConfigString("server-name", "Unknown Server"); + } + + @Override + public String getServerGroup() { + return serverGroup; + } + + public void setServerGroup(String serverGroup) { + this.serverGroup = serverGroup; + } + + @Override + public String getServerId() { + return this.getConfigString("server-id", "unnamed"); + } + + @Override + public String getWorldType() { + return this.getConfigString("level-type", "DEFAULT"); + } + + @Override + public boolean getGenerateStructures() { + return this.getConfigBoolean("generate-structures", true); + } + + @Override + public boolean getAllowEnd() { + return this.configuration.getBoolean("settings.allow-end"); + } + + @Override + public boolean getAllowNether() { + return this.getConfigBoolean("allow-nether", true); + } + + public boolean getWarnOnOverload() { + return this.configuration.getBoolean("settings.warn-on-overload"); + } + + public boolean getQueryPlugins() { + return this.configuration.getBoolean("settings.query-plugins"); + } + + @Override + public boolean hasWhitelist() { + return this.getConfigBoolean("white-list", false); + } + + // NOTE: Temporary calls through to server.properies until its replaced + private String getConfigString(String variable, String defaultValue) { + return this.console.getPropertyManager().getString(variable, defaultValue); + } + + private int getConfigInt(String variable, int defaultValue) { + return this.console.getPropertyManager().getInt(variable, defaultValue); + } + + private boolean getConfigBoolean(String variable, boolean defaultValue) { + return this.console.getPropertyManager().getBoolean(variable, defaultValue); + } + + // End Temporary calls + + @Override + public String getUpdateFolder() { + return this.configuration.getString("settings.update-folder", "update"); + } + + @Override + public File getUpdateFolderFile() { + return new File((File) console.options.valueOf("plugins"), this.configuration.getString("settings.update-folder", "update")); + } + + public int getPingPacketLimit() { + return this.configuration.getInt("settings.ping-packet-limit", 100); + } + + @Override + public long getConnectionThrottle() { + // Spigot Start - Automatically set connection throttle for bungee configurations + if (org.spigotmc.SpigotConfig.bungee) { + return -1; + } else { + return this.configuration.getInt("settings.connection-throttle"); + } + // Spigot End + } + + @Override + public int getTicksPerAnimalSpawns() { + return this.configuration.getInt("ticks-per.animal-spawns"); + } + + @Override + public int getTicksPerMonsterSpawns() { + return this.configuration.getInt("ticks-per.monster-spawns"); + } + + @Override + public PluginManager getPluginManager() { + return pluginManager; + } + + @Override + public CraftScheduler getScheduler() { + return scheduler; + } + + @Override + public ServicesManager getServicesManager() { + return servicesManager; + } + + @Override + public List getWorlds() { + return new ArrayList(worlds.values()); + } + + public DedicatedPlayerList getHandle() { + return playerList; + } + + // NOTE: Should only be called from DedicatedServer.ah() + public boolean dispatchServerCommand(CommandSender sender, ServerCommand serverCommand) { + if (sender instanceof Conversable) { + Conversable conversable = (Conversable)sender; + + if (conversable.isConversing()) { + conversable.acceptConversationInput(serverCommand.command); + return true; + } + } + try { + this.playerCommandState = true; + return dispatchCommand(sender, serverCommand.command); + } catch (Exception ex) { + getLogger().log(Level.WARNING, "Unexpected exception while parsing console command \"" + serverCommand.command + '"', ex); + return false; + } finally { + this.playerCommandState = false; + } + } + + @Override + public boolean dispatchCommand(CommandSender sender, String commandLine) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(commandLine, "CommandLine cannot be null"); + + if (commandMap.dispatch(sender, commandLine)) { + return true; + } + + sender.sendMessage(org.spigotmc.SpigotConfig.unknownCommandMessage); + + return false; + } + + @Override + public void reload() { + configuration = YamlConfiguration.loadConfiguration(getConfigFile()); + commandsConfiguration = YamlConfiguration.loadConfiguration(getCommandsConfigFile()); + PropertyManager config = new PropertyManager(console.options); + + ((DedicatedServer) console).propertyManager = config; + + boolean animals = config.getBoolean("spawn-animals", console.getSpawnAnimals()); + boolean monsters = config.getBoolean("spawn-monsters", console.worlds.get(0).difficulty != EnumDifficulty.PEACEFUL); + EnumDifficulty difficulty = EnumDifficulty.getById(config.getInt("difficulty", console.worlds.get(0).difficulty.ordinal())); + + online.value = config.getBoolean("online-mode", console.getOnlineMode()); + console.setSpawnAnimals(config.getBoolean("spawn-animals", console.getSpawnAnimals())); + console.setPvP(config.getBoolean("pvp", console.getPvP())); + console.setAllowFlight(config.getBoolean("allow-flight", console.getAllowFlight())); + console.setMotd(config.getString("motd", console.getMotd())); + monsterSpawn = configuration.getInt("spawn-limits.monsters"); + animalSpawn = configuration.getInt("spawn-limits.animals"); + waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals"); + ambientSpawn = configuration.getInt("spawn-limits.ambient"); + warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); + printSaveWarning = false; + console.autosavePeriod = configuration.getInt("ticks-per.autosave"); + chunkGCPeriod = configuration.getInt("chunk-gc.period-in-ticks"); + chunkGCLoadThresh = configuration.getInt("chunk-gc.load-threshold"); + loadIcon(); + + try { + playerList.getIPBans().load(); + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to load banned-ips.json, " + ex.getMessage()); + } + try { + playerList.getProfileBans().load(); + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to load banned-players.json, " + ex.getMessage()); + } + + org.spigotmc.SpigotConfig.init(); // Spigot + org.github.paperspigot.PaperSpigotConfig.init(); // PaperSpigot + for (WorldServer world : console.worlds) { + world.difficulty = difficulty; + world.setSpawnFlags(monsters, animals); + if (this.getTicksPerAnimalSpawns() < 0) { + world.ticksPerAnimalSpawns = 400; + } else { + world.ticksPerAnimalSpawns = this.getTicksPerAnimalSpawns(); + } + + if (this.getTicksPerMonsterSpawns() < 0) { + world.ticksPerMonsterSpawns = 1; + } else { + world.ticksPerMonsterSpawns = this.getTicksPerMonsterSpawns(); + } + world.spigotConfig.init(); // Spigot + world.paperSpigotConfig.init(); // PaperSpigot + } + + pluginManager.clearPlugins(); + commandMap.clearCommands(); + resetRecipes(); + org.spigotmc.SpigotConfig.registerCommands(); // Spigot + org.github.paperspigot.PaperSpigotConfig.registerCommands(); // PaperSpigot + + overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*"); + + int pollCount = 0; + + // Wait for at most 2.5 seconds for plugins to close their threads + while (pollCount < 50 && getScheduler().getActiveWorkers().size() > 0) { + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + pollCount++; + } + + List overdueWorkers = getScheduler().getActiveWorkers(); + for (BukkitWorker worker : overdueWorkers) { + Plugin plugin = worker.getOwner(); + String author = ""; + if (plugin.getDescription().getAuthors().size() > 0) { + author = plugin.getDescription().getAuthors().get(0); + } + getLogger().log(Level.SEVERE, String.format( + "Nag author: '%s' of '%s' about the following: %s", + author, + plugin.getDescription().getName(), + "This plugin is not properly shutting down its async tasks when it is being reloaded. This may cause conflicts with the newly loaded version of the plugin" + )); + } + loadPlugins(); + enablePlugins(PluginLoadOrder.STARTUP); + enablePlugins(PluginLoadOrder.POSTWORLD); + } + + private void loadIcon() { + icon = new CraftIconCache(null); + try { + final File file = new File(new File("."), "server-icon.png"); + if (file.isFile()) { + icon = loadServerIcon0(file); + } + } catch (Exception ex) { + getLogger().log(Level.WARNING, "Couldn't load server icon", ex); + } + } + + @SuppressWarnings({ "unchecked", "finally" }) + private void loadCustomPermissions() { + File file = new File(configuration.getString("settings.permissions-file")); + FileInputStream stream; + + try { + stream = new FileInputStream(file); + } catch (FileNotFoundException ex) { + try { + file.createNewFile(); + } finally { + return; + } + } + + Map> perms; + + try { + perms = (Map>) yaml.load(stream); + } catch (MarkedYAMLException ex) { + getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML: " + ex.toString()); + return; + } catch (Throwable ex) { + getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML.", ex); + return; + } finally { + try { + stream.close(); + } catch (IOException ex) {} + } + + if (perms == null) { + getLogger().log(Level.INFO, "Server permissions file " + file + " is empty, ignoring it"); + return; + } + + List permsList = Permission.loadPermissions(perms, "Permission node '%s' in " + file + " is invalid", Permission.DEFAULT_PERMISSION); + + for (Permission perm : permsList) { + try { + pluginManager.addPermission(perm); + } catch (IllegalArgumentException ex) { + getLogger().log(Level.SEVERE, "Permission in " + file + " was already defined", ex); + } + } + } + + @Override + public String toString() { + return "CraftServer{" + "serverName=" + serverName + ",serverVersion=" + serverVersion + ",minecraftVersion=" + console.getVersion() + '}'; + } + + public World createWorld(String name, World.Environment environment) { + return WorldCreator.name(name).environment(environment).createWorld(); + } + + public World createWorld(String name, World.Environment environment, long seed) { + return WorldCreator.name(name).environment(environment).seed(seed).createWorld(); + } + + public World createWorld(String name, Environment environment, ChunkGenerator generator) { + return WorldCreator.name(name).environment(environment).generator(generator).createWorld(); + } + + public World createWorld(String name, Environment environment, long seed, ChunkGenerator generator) { + return WorldCreator.name(name).environment(environment).seed(seed).generator(generator).createWorld(); + } + + @Override + public World createWorld(WorldCreator creator) { + Validate.notNull(creator, "Creator may not be null"); + + String name = creator.name(); + ChunkGenerator generator = creator.generator(); + File folder = new File(getWorldContainer(), name); + World world = getWorld(name); + WorldType type = WorldType.getType(creator.type().getName()); + boolean generateStructures = creator.generateStructures(); + + if (world != null) { + return world; + } + + if ((folder.exists()) && (!folder.isDirectory())) { + throw new IllegalArgumentException("File exists with the name '" + name + "' and isn't a folder"); + } + + if (generator == null) { + generator = getGenerator(name); + } + + Convertable converter = new WorldLoaderServer(getWorldContainer()); + if (converter.isConvertable(name)) { + getLogger().info("Converting world '" + name + "'"); + converter.convert(name, new ConvertProgressUpdater(console)); + } + + int dimension = CraftWorld.CUSTOM_DIMENSION_OFFSET + console.worlds.size(); + boolean used = false; + do { + for (WorldServer server : console.worlds) { + used = server.dimension == dimension; + if (used) { + dimension++; + break; + } + } + } while(used); + boolean hardcore = false; + + WorldServer internal = new WorldServer(console, new ServerNBTManager(getWorldContainer(), name, true), name, dimension, new WorldSettings(creator.seed(), EnumGamemode.getById(getDefaultGameMode().getValue()), generateStructures, hardcore, type), console.methodProfiler, creator.environment(), generator); + + if (!(worlds.containsKey(name.toLowerCase()))) { + return null; + } + + internal.scoreboard = getScoreboardManager().getMainScoreboard().getHandle(); + + internal.tracker = new EntityTracker(internal); + internal.addIWorldAccess(new WorldManager(console, internal)); + internal.difficulty = EnumDifficulty.EASY; + internal.setSpawnFlags(true, true); + console.worlds.add(internal); + + if (generator != null) { + internal.getWorld().getPopulators().addAll(generator.getDefaultPopulators(internal.getWorld())); + } + + pluginManager.callEvent(new WorldInitEvent(internal.getWorld())); + System.out.print("Preparing start region for level " + (console.worlds.size() - 1) + " (Seed: " + internal.getSeed() + ")"); + + if (internal.getWorld().getKeepSpawnInMemory()) { + short short1 = 196; + long i = System.currentTimeMillis(); + for (int j = -short1; j <= short1; j += 16) { + for (int k = -short1; k <= short1; k += 16) { + long l = System.currentTimeMillis(); + + if (l < i) { + i = l; + } + + if (l > i + 1000L) { + int i1 = (short1 * 2 + 1) * (short1 * 2 + 1); + int j1 = (j + short1) * (short1 * 2 + 1) + k + 1; + + System.out.println("Preparing spawn area for " + name + ", " + (j1 * 100 / i1) + "%"); + i = l; + } + + ChunkCoordinates chunkcoordinates = internal.getSpawn(); + internal.chunkProviderServer.getChunkAt(chunkcoordinates.x + j >> 4, chunkcoordinates.z + k >> 4); + } + } + } + pluginManager.callEvent(new WorldLoadEvent(internal.getWorld())); + return internal.getWorld(); + } + + @Override + public boolean unloadWorld(String name, boolean save) { + return unloadWorld(getWorld(name), save); + } + + @Override + public boolean unloadWorld(World world, boolean save) { + if (world == null) { + return false; + } + + WorldServer handle = ((CraftWorld) world).getHandle(); + + if (!(console.worlds.contains(handle))) { + return false; + } + + if (!(handle.dimension > 1)) { + return false; + } + + if (handle.players.size() > 0) { + return false; + } + + WorldUnloadEvent e = new WorldUnloadEvent(handle.getWorld()); + pluginManager.callEvent(e); + + if (e.isCancelled()) { + return false; + } + + if (save) { + try { + handle.save(true, null); + handle.saveLevel(); + WorldSaveEvent event = new WorldSaveEvent(handle.getWorld()); + getPluginManager().callEvent(event); + } catch (ExceptionWorldConflict ex) { + getLogger().log(Level.SEVERE, null, ex); + } + } + + worlds.remove(world.getName().toLowerCase()); + console.worlds.remove(console.worlds.indexOf(handle)); + + File parentFolder = world.getWorldFolder().getAbsoluteFile(); + + // Synchronized because access to RegionFileCache.a is guarded by this lock. + synchronized (RegionFileCache.class) { + // RegionFileCache.a should be RegionFileCache.cache + Iterator> i = RegionFileCache.a.entrySet().iterator(); + while(i.hasNext()) { + Map.Entry entry = i.next(); + File child = entry.getKey().getAbsoluteFile(); + while (child != null) { + if (child.equals(parentFolder)) { + i.remove(); + try { + entry.getValue().c(); // Should be RegionFile.close(); + } catch (IOException ex) { + getLogger().log(Level.SEVERE, null, ex); + } + break; + } + child = child.getParentFile(); + } + } + } + + return true; + } + + public MinecraftServer getServer() { + return console; + } + + @Override + public World getWorld(String name) { + Validate.notNull(name, "Name cannot be null"); + + return worlds.get(name.toLowerCase()); + } + + @Override + public World getWorld(UUID uid) { + for (World world : worlds.values()) { + if (world.getUID().equals(uid)) { + return world; + } + } + return null; + } + + public void addWorld(World world) { + // Check if a World already exists with the UID. + if (getWorld(world.getUID()) != null) { + System.out.println("World " + world.getName() + " is a duplicate of another world and has been prevented from loading. Please delete the uid.dat file from " + world.getName() + "'s world directory if you want to be able to load the duplicate world."); + return; + } + worlds.put(world.getName().toLowerCase(), world); + } + + @Override + public Logger getLogger() { + return logger; + } + + public ConsoleReader getReader() { + return console.reader; + } + + @Override + public PluginCommand getPluginCommand(String name) { + Command command = commandMap.getCommand(name); + + if (command instanceof PluginCommand) { + return (PluginCommand) command; + } else { + return null; + } + } + + @Override + public void savePlayers() { + checkSaveState(); + playerList.savePlayers(); + } + + @Override + public void configureDbConfig(ServerConfig config) { + Validate.notNull(config, "Config cannot be null"); + + DataSourceConfig ds = new DataSourceConfig(); + ds.setDriver(configuration.getString("database.driver")); + ds.setUrl(configuration.getString("database.url")); + ds.setUsername(configuration.getString("database.username")); + ds.setPassword(configuration.getString("database.password")); + ds.setIsolationLevel(TransactionIsolation.getLevel(configuration.getString("database.isolation"))); + + if (ds.getDriver().contains("sqlite")) { + config.setDatabasePlatform(new SQLitePlatform()); + config.getDatabasePlatform().getDbDdlSyntax().setIdentity(""); + } + + config.setDataSourceConfig(ds); + } + + @Override + public boolean addRecipe(Recipe recipe) { + CraftRecipe toAdd; + if (recipe instanceof CraftRecipe) { + toAdd = (CraftRecipe) recipe; + } else { + if (recipe instanceof ShapedRecipe) { + toAdd = CraftShapedRecipe.fromBukkitRecipe((ShapedRecipe) recipe); + } else if (recipe instanceof ShapelessRecipe) { + toAdd = CraftShapelessRecipe.fromBukkitRecipe((ShapelessRecipe) recipe); + } else if (recipe instanceof FurnaceRecipe) { + toAdd = CraftFurnaceRecipe.fromBukkitRecipe((FurnaceRecipe) recipe); + } else { + return false; + } + } + toAdd.addToCraftingManager(); + CraftingManager.getInstance().sort(); + return true; + } + + @Override + public List getRecipesFor(ItemStack result) { + Validate.notNull(result, "Result cannot be null"); + + List results = new ArrayList(); + Iterator iter = recipeIterator(); + while (iter.hasNext()) { + Recipe recipe = iter.next(); + ItemStack stack = recipe.getResult(); + if (stack.getType() != result.getType()) { + continue; + } + if (result.getDurability() == -1 || result.getDurability() == stack.getDurability()) { + results.add(recipe); + } + } + return results; + } + + @Override + public Iterator recipeIterator() { + return new RecipeIterator(); + } + + @Override + public void clearRecipes() { + CraftingManager.getInstance().recipes.clear(); + RecipesFurnace.getInstance().recipes.clear(); + RecipesFurnace.getInstance().customRecipes.clear(); + } + + @Override + public void resetRecipes() { + CraftingManager.getInstance().recipes = new CraftingManager().recipes; + RecipesFurnace.getInstance().recipes = new RecipesFurnace().recipes; + RecipesFurnace.getInstance().customRecipes.clear(); + } + + @Override + public Map getCommandAliases() { + ConfigurationSection section = commandsConfiguration.getConfigurationSection("aliases"); + Map result = new LinkedHashMap(); + + if (section != null) { + for (String key : section.getKeys(false)) { + List commands; + + if (section.isList(key)) { + commands = section.getStringList(key); + } else { + commands = ImmutableList.of(section.getString(key)); + } + + result.put(key, commands.toArray(new String[commands.size()])); + } + } + + return result; + } + + public void removeBukkitSpawnRadius() { + configuration.set("settings.spawn-radius", null); + saveConfig(); + } + + public int getBukkitSpawnRadius() { + return configuration.getInt("settings.spawn-radius", -1); + } + + @Override + public String getShutdownMessage() { + return configuration.getString("settings.shutdown-message"); + } + + @Override + public int getSpawnRadius() { + return ((DedicatedServer) console).propertyManager.getInt("spawn-protection", 16); + } + + @Override + public void setSpawnRadius(int value) { + configuration.set("settings.spawn-radius", value); + saveConfig(); + } + + @Override + public boolean getOnlineMode() { + return online.value; + } + + @Override + public boolean getAllowFlight() { + return console.getAllowFlight(); + } + + @Override + public boolean isHardcore() { + return console.isHardcore(); + } + + @Override + public boolean useExactLoginLocation() { + return configuration.getBoolean("settings.use-exact-login-location"); + } + + public ChunkGenerator getGenerator(String world) { + ConfigurationSection section = configuration.getConfigurationSection("worlds"); + ChunkGenerator result = null; + + if (section != null) { + section = section.getConfigurationSection(world); + + if (section != null) { + String name = section.getString("generator"); + + if ((name != null) && (!name.equals(""))) { + String[] split = name.split(":", 2); + String id = (split.length > 1) ? split[1] : null; + Plugin plugin = pluginManager.getPlugin(split[0]); + + if (plugin == null) { + getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + split[0] + "' does not exist"); + } else if (!plugin.isEnabled()) { + getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' is not enabled yet (is it load:STARTUP?)"); + } else { + try { + result = plugin.getDefaultWorldGenerator(world, id); + if (result == null) { + getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' lacks a default world generator"); + } + } catch (Throwable t) { + plugin.getLogger().log(Level.SEVERE, "Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName(), t); + } + } + } + } + } + + return result; + } + + @Override + @Deprecated + public CraftMapView getMap(short id) { + PersistentCollection collection = console.worlds.get(0).worldMaps; + WorldMap worldmap = (WorldMap) collection.get(WorldMap.class, "map_" + id); + if (worldmap == null) { + return null; + } + return worldmap.mapView; + } + + @Override + public CraftMapView createMap(World world) { + Validate.notNull(world, "World cannot be null"); + + net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(Items.MAP, 1, -1); + WorldMap worldmap = Items.MAP.getSavedMap(stack, ((CraftWorld) world).getHandle()); + return worldmap.mapView; + } + + @Override + public void shutdown() { + console.safeShutdown(); + } + + @Override + public int broadcast(String message, String permission) { + int count = 0; + Set permissibles = getPluginManager().getPermissionSubscriptions(permission); + + for (Permissible permissible : permissibles) { + if (permissible instanceof CommandSender && permissible.hasPermission(permission)) { + CommandSender user = (CommandSender) permissible; + user.sendMessage(message); + count++; + } + } + + return count; + } + + @Override + @Deprecated + public OfflinePlayer getOfflinePlayer(String name) { + Validate.notNull(name, "Name cannot be null"); + com.google.common.base.Preconditions.checkArgument( !org.apache.commons.lang.StringUtils.isBlank( name ), "Name cannot be blank" ); // Spigot + + OfflinePlayer result = getPlayerExact(name); + if (result == null) { + // Spigot Start + GameProfile profile = null; + // Only fetch an online UUID in online mode + if ( MinecraftServer.getServer().getOnlineMode() || org.spigotmc.SpigotConfig.bungee ) + { + profile = MinecraftServer.getServer().getUserCache().getProfile( name ); + } + // Spigot end + if (profile == null) { + // Make an OfflinePlayer using an offline mode UUID since the name has no profile + result = getOfflinePlayer(new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name)); + } else { + // Use the GameProfile even when we get a UUID so we ensure we still have a name + result = getOfflinePlayer(profile); + } + } else { + offlinePlayers.remove(result.getUniqueId()); + } + + return result; + } + + @Override + public OfflinePlayer getOfflinePlayer(UUID id) { + Validate.notNull(id, "UUID cannot be null"); + + OfflinePlayer result = getPlayer(id); + if (result == null) { + result = offlinePlayers.get(id); + if (result == null) { + result = new CraftOfflinePlayer(this, new GameProfile(id, null)); + offlinePlayers.put(id, result); + } + } else { + offlinePlayers.remove(id); + } + + return result; + } + + public OfflinePlayer getOfflinePlayer(GameProfile profile) { + OfflinePlayer player = new CraftOfflinePlayer(this, profile); + offlinePlayers.put(profile.getId(), player); + return player; + } + + @Override + @SuppressWarnings("unchecked") + public Set getIPBans() { + return new HashSet(Arrays.asList(playerList.getIPBans().getEntries())); + } + + @Override + public void banIP(String address) { + Validate.notNull(address, "Address cannot be null."); + + this.getBanList(org.bukkit.BanList.Type.IP).addBan(address, null, null, null); + } + + @Override + public void unbanIP(String address) { + Validate.notNull(address, "Address cannot be null."); + + this.getBanList(org.bukkit.BanList.Type.IP).pardon(address); + } + + @Override + public Set getBannedPlayers() { + Set result = new HashSet(); + + for (JsonListEntry entry : playerList.getProfileBans().getValues()) { + result.add(getOfflinePlayer((GameProfile) entry.getKey())); + } + + return result; + } + + @Override + public BanList getBanList(BanList.Type type) { + Validate.notNull(type, "Type cannot be null"); + + switch(type){ + case IP: + return new CraftIpBanList(playerList.getIPBans()); + case NAME: + default: + return new CraftProfileBanList(playerList.getProfileBans()); + } + } + + @Override + public void setWhitelist(boolean value) { + playerList.setHasWhitelist(value); + console.getPropertyManager().setProperty("white-list", value); + } + + @Override + public Set getWhitelistedPlayers() { + Set result = new LinkedHashSet(); + + for (JsonListEntry entry : playerList.getWhitelist().getValues()) { + result.add(getOfflinePlayer((GameProfile) entry.getKey())); + } + + return result; + } + + @Override + public Set getOperators() { + Set result = new HashSet(); + + for (JsonListEntry entry : playerList.getOPs().getValues()) { + result.add(getOfflinePlayer((GameProfile) entry.getKey())); + } + + return result; + } + + @Override + public void reloadWhitelist() { + playerList.reloadWhitelist(); + } + + @Override + public GameMode getDefaultGameMode() { + return GameMode.getByValue(console.worlds.get(0).getWorldData().getGameType().getId()); + } + + @Override + public void setDefaultGameMode(GameMode mode) { + Validate.notNull(mode, "Mode cannot be null"); + + for (World world : getWorlds()) { + ((CraftWorld) world).getHandle().worldData.setGameType(EnumGamemode.getById(mode.getValue())); + } + } + + @Override + public ConsoleCommandSender getConsoleSender() { + return console.console; + } + + public EntityMetadataStore getEntityMetadata() { + return entityMetadata; + } + + public PlayerMetadataStore getPlayerMetadata() { + return playerMetadata; + } + + public WorldMetadataStore getWorldMetadata() { + return worldMetadata; + } + + public void detectListNameConflict(EntityPlayer entityPlayer) { + // Collisions will make for invisible people + for (int i = 0; i < getHandle().players.size(); ++i) { + EntityPlayer testEntityPlayer = (EntityPlayer) getHandle().players.get(i); + + // We have a problem! + if (testEntityPlayer != entityPlayer && testEntityPlayer.listName.equals(entityPlayer.listName)) { + String oldName = entityPlayer.listName; + int spaceLeft = 16 - oldName.length(); + + if (spaceLeft <= 1) { // We also hit the list name length limit! + entityPlayer.listName = oldName.subSequence(0, oldName.length() - 2 - spaceLeft) + String.valueOf(System.currentTimeMillis() % 99); + } else { + entityPlayer.listName = oldName + String.valueOf(System.currentTimeMillis() % 99); + } + + return; + } + } + } + + @Override + public File getWorldContainer() { + if (this.getServer().universe != null) { + return this.getServer().universe; + } + + if (container == null) { + container = new File(configuration.getString("settings.world-container", ".")); + } + + return container; + } + + @Override + public OfflinePlayer[] getOfflinePlayers() { + WorldNBTStorage storage = (WorldNBTStorage) console.worlds.get(0).getDataManager(); + String[] files = storage.getPlayerDir().list(new DatFileFilter()); + Set players = new HashSet(); + + for (String file : files) { + try { + players.add(getOfflinePlayer(UUID.fromString(file.substring(0, file.length() - 4)))); + } catch (IllegalArgumentException ex) { + // Who knows what is in this directory, just ignore invalid files + } + } + + players.addAll(getOnlinePlayers()); + + return players.toArray(new OfflinePlayer[players.size()]); + } + + @Override + public Messenger getMessenger() { + return messenger; + } + + @Override + public void sendPluginMessage(Plugin source, String channel, byte[] message) { + StandardMessenger.validatePluginMessage(getMessenger(), source, channel, message); + + for (Player player : getOnlinePlayers()) { + player.sendPluginMessage(source, channel, message); + } + } + + @Override + public Set getListeningPluginChannels() { + Set result = new HashSet(); + + for (Player player : getOnlinePlayers()) { + result.addAll(player.getListeningPluginChannels()); + } + + return result; + } + + public void onPlayerJoin(Player player) { + if ((updater.isEnabled()) && (updater.getCurrent() != null) && (player.hasPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE))) { + if ((updater.getCurrent().isBroken()) && (updater.getOnBroken().contains(AutoUpdater.WARN_OPERATORS))) { + player.sendMessage(ChatColor.DARK_RED + "The version of CraftBukkit that this server is running is known to be broken. Please consider updating to the latest version at dl.bukkit.org."); + } else if ((updater.isUpdateAvailable()) && (updater.getOnUpdate().contains(AutoUpdater.WARN_OPERATORS))) { + player.sendMessage(ChatColor.DARK_PURPLE + "The version of CraftBukkit that this server is running is out of date. Please consider updating to the latest version at dl.bukkit.org."); + } + } + } + + @Override + public Inventory createInventory(InventoryHolder owner, InventoryType type) { + // TODO: Create the appropriate type, rather than Custom? + return new CraftInventoryCustom(owner, type); + } + + @Override + public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) { + return new CraftInventoryCustom(owner, type, title); + } + + @Override + public Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException { + Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!"); + return new CraftInventoryCustom(owner, size); + } + + @Override + public Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException { + Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!"); + return new CraftInventoryCustom(owner, size, title); + } + + @Override + public HelpMap getHelpMap() { + return helpMap; + } + + public SimpleCommandMap getCommandMap() { + return commandMap; + } + + @Override + public int getMonsterSpawnLimit() { + return monsterSpawn; + } + + @Override + public int getAnimalSpawnLimit() { + return animalSpawn; + } + + @Override + public int getWaterAnimalSpawnLimit() { + return waterAnimalSpawn; + } + + @Override + public int getAmbientSpawnLimit() { + return ambientSpawn; + } + + @Override + public boolean isPrimaryThread() { + return Thread.currentThread().equals(console.primaryThread); + } + + @Override + public String getMotd() { + return console.getMotd(); + } + + @Override + public WarningState getWarningState() { + return warningState; + } + + public List tabComplete(net.minecraft.server.ICommandListener sender, String message) { + if (!(sender instanceof EntityPlayer)) { + return ImmutableList.of(); + } + + Player player = ((EntityPlayer) sender).getBukkitEntity(); + if (message.startsWith("/")) { + return tabCompleteCommand(player, message); + } else { + return tabCompleteChat(player, message); + } + } + + public List tabCompleteCommand(Player player, String message) { + // Spigot Start + if ( (org.spigotmc.SpigotConfig.tabComplete < 0 || message.length() <= org.spigotmc.SpigotConfig.tabComplete) && !message.contains( " " ) ) + { + return ImmutableList.of(); + } + // Spigot End + + List completions = null; + try { + completions = getCommandMap().tabComplete(player, message.substring(1)); + } catch (CommandException ex) { + player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command"); + getLogger().log(Level.SEVERE, "Exception when " + player.getName() + " attempted to tab complete " + message, ex); + } + + return completions == null ? ImmutableList.of() : completions; + } + + public List tabCompleteChat(Player player, String message) { + List completions = new ArrayList(); + PlayerChatTabCompleteEvent event = new PlayerChatTabCompleteEvent(player, message, completions); + String token = event.getLastToken(); + for (Player p : getOnlinePlayers()) { + // MineHQ start - Disguises: Use #getDisguisedName instead of #getName + if (player.canSee(p) && StringUtil.startsWithIgnoreCase(p.getDisguisedName(), token)) { + completions.add(p.getDisguisedName()); + } + // MineHQ end + } + pluginManager.callEvent(event); + + Iterator it = completions.iterator(); + while (it.hasNext()) { + Object current = it.next(); + if (!(current instanceof String)) { + // Sanity + it.remove(); + } + } + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); + return completions; + } + + @Override + public CraftItemFactory getItemFactory() { + return CraftItemFactory.instance(); + } + + @Override + public CraftScoreboardManager getScoreboardManager() { + return scoreboardManager; + } + + public void checkSaveState() { + if (this.playerCommandState || this.printSaveWarning || this.console.autosavePeriod <= 0) { + return; + } + this.printSaveWarning = true; + getLogger().log(Level.WARNING, "A manual (plugin-induced) save has been detected while server is configured to auto-save. This may affect performance.", warningState == WarningState.ON ? new Throwable() : null); + } + + @Override + public CraftIconCache getServerIcon() { + return icon; + } + + @Override + public CraftIconCache loadServerIcon(File file) throws Exception { + Validate.notNull(file, "File cannot be null"); + if (!file.isFile()) { + throw new IllegalArgumentException(file + " is not a file"); + } + return loadServerIcon0(file); + } + + static CraftIconCache loadServerIcon0(File file) throws Exception { + return loadServerIcon0(ImageIO.read(file)); + } + + @Override + public CraftIconCache loadServerIcon(BufferedImage image) throws Exception { + Validate.notNull(image, "Image cannot be null"); + return loadServerIcon0(image); + } + + static CraftIconCache loadServerIcon0(BufferedImage image) throws Exception { + ByteBuf bytebuf = Unpooled.buffer(); + + Validate.isTrue(image.getWidth() == 64, "Must be 64 pixels wide"); + Validate.isTrue(image.getHeight() == 64, "Must be 64 pixels high"); + ImageIO.write(image, "PNG", new ByteBufOutputStream(bytebuf)); + ByteBuf bytebuf1 = Base64.encode(bytebuf); + + return new CraftIconCache("data:image/png;base64," + bytebuf1.toString(Charsets.UTF_8)); + } + + @Override + public void setIdleTimeout(int threshold) { + console.setIdleTimeout(threshold); + } + + @Override + public int getIdleTimeout() { + return console.getIdleTimeout(); + } + + // Guardian start + @Override + public boolean isGuardianEnabled() { + return SpigotConfig.guardianEnabled; + } + + @Override + public void setGuardianEnabled(boolean enabled) { + SpigotConfig.guardianEnabled = enabled; + } + + @Override + public boolean shouldGuardianAct() { + return isGuardianEnabled() && spigot().getTPS()[0] >= 19.0D; + } + // Guardian end + + // MineHQ start + @Override + public Player getPlayerByDisguise(String name) { + Validate.notNull(name, "Name cannot be null"); + + Player found = getPlayerExactByDisguise(name); + if (found != null) { + return found; + } + + String lowerName = name.toLowerCase(); + int delta = Integer.MAX_VALUE; + for (Entry entry : playerList.disguisePlayerMap.entrySet()) { + String disguisedName = entry.getKey(); + EntityPlayer player = entry.getValue(); + if (disguisedName.toLowerCase().startsWith(lowerName)) { + int curDelta = disguisedName.length() - lowerName.length(); + if (curDelta < delta) { + found = player.getBukkitEntity(); + delta = curDelta; + } + if (curDelta == 0) break; + } + } + + return found; + } + // MineHQ end + + @Deprecated + @Override + public UnsafeValues getUnsafe() { + return CraftMagicNumbers.INSTANCE; + } + + private final Spigot spigot = new Spigot() + { + + @Override + public YamlConfiguration getConfig() + { + return org.spigotmc.SpigotConfig.config; + } + + @Override + public void broadcast( BaseComponent component ) + { + for ( Player player : getOnlinePlayers() ) + { + player.spigot().sendMessage( component ); + } + } + + @Override + public void broadcast( BaseComponent... components ) + { + for ( Player player : getOnlinePlayers() ) + { + player.spigot().sendMessage( components ); + } + } + + // PaperSpigot start - Add getTPS + @Override + public double[] getTPS() { + return MinecraftServer.getServer().recentTps; + } + // PaperSpigot end + }; + + public Spigot spigot() + { + return spigot; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftSound.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftSound.java new file mode 100644 index 0000000..0cc8f9b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftSound.java @@ -0,0 +1,231 @@ +package org.bukkit.craftbukkit; + +import static org.bukkit.Sound.*; + +import org.apache.commons.lang.Validate; +import org.bukkit.Sound; + +public class CraftSound { + private static final String[] sounds = new String[Sound.values().length]; + + static { + // Ambient + set(AMBIENCE_CAVE, "ambient.cave.cave"); + set(AMBIENCE_RAIN, "ambient.weather.rain"); + set(AMBIENCE_THUNDER, "ambient.weather.thunder"); + // Damage + set(HURT_FLESH, "game.neutral.hurt"); + set(FALL_BIG, "game.neutral.hurt.fall.big"); + set(FALL_SMALL, "game.neutral.hurt.fall.small"); + // Dig Sounds + set(DIG_WOOL, "dig.cloth"); + set(DIG_GRASS, "dig.grass"); + set(DIG_GRAVEL, "dig.gravel"); + set(DIG_SAND, "dig.sand"); + set(DIG_SNOW, "dig.snow"); + set(DIG_STONE, "dig.stone"); + set(DIG_WOOD, "dig.wood"); + // Fire + set(FIRE, "fire.fire"); + set(FIRE_IGNITE, "fire.ignite"); + // Fireworks + set(FIREWORK_BLAST, "fireworks.blast"); + set(FIREWORK_BLAST2, "fireworks.blast_far"); + set(FIREWORK_LARGE_BLAST, "fireworks.largeBlast"); + set(FIREWORK_LARGE_BLAST2, "fireworks.largeBlast_far"); + set(FIREWORK_TWINKLE, "fireworks.twinkle"); + set(FIREWORK_TWINKLE2, "fireworks.twinkle_far"); + set(FIREWORK_LAUNCH, "fireworks.launch"); + // Liquid + set(SPLASH2, "game.neutral.swim.splash"); + set(SWIM, "game.neutral.swim"); + set(WATER, "liquid.water"); + set(LAVA, "liquid.lava"); + set(LAVA_POP, "liquid.lavapop"); + // Minecart + set(MINECART_BASE, "minecart.base"); + set(MINECART_INSIDE, "minecart.inside"); + // Mob + set(BAT_DEATH, "mob.bat.death"); + set(BAT_HURT, "mob.bat.hurt"); + set(BAT_IDLE, "mob.bat.idle"); + set(BAT_LOOP, "mob.bat.loop"); + set(BAT_TAKEOFF, "mob.bat.takeoff"); + set(BLAZE_BREATH, "mob.blaze.breathe"); + set(BLAZE_DEATH, "mob.blaze.death"); + set(BLAZE_HIT, "mob.blaze.hit"); + set(CAT_HISS, "mob.cat.hiss"); + set(CAT_HIT, "mob.cat.hitt"); + set(CAT_MEOW, "mob.cat.meow"); + set(CAT_PURR, "mob.cat.purr"); + set(CAT_PURREOW, "mob.cat.purreow"); + set(CHICKEN_IDLE, "mob.chicken.say"); + set(CHICKEN_HURT, "mob.chicken.hurt"); + set(CHICKEN_EGG_POP, "mob.chicken.plop"); + set(CHICKEN_WALK, "mob.chicken.step"); + set(COW_HURT, "mob.cow.hurt"); + set(COW_IDLE, "mob.cow.say"); + set(COW_WALK, "mob.cow.step"); + set(CREEPER_DEATH, "mob.creeper.death"); + set(CREEPER_HISS, "mob.creeper.say"); + set(ENDERDRAGON_DEATH, "mob.enderdragon.end"); + set(ENDERDRAGON_GROWL, "mob.enderdragon.growl"); + set(ENDERDRAGON_HIT, "mob.enderdragon.hit"); + set(ENDERDRAGON_WINGS, "mob.enderdragon.wings"); + set(ENDERMAN_DEATH, "mob.endermen.death"); + set(ENDERMAN_HIT, "mob.endermen.hit"); + set(ENDERMAN_IDLE, "mob.endermen.idle"); + set(ENDERMAN_TELEPORT, "mob.endermen.portal"); + set(ENDERMAN_SCREAM, "mob.endermen.scream"); + set(ENDERMAN_STARE, "mob.endermen.stare"); + set(GHAST_SCREAM2, "mob.ghast.affectionate_scream"); + set(GHAST_CHARGE, "mob.ghast.charge"); + set(GHAST_DEATH, "mob.ghast.death"); + set(GHAST_FIREBALL, "mob.ghast.fireball"); + set(GHAST_MOAN, "mob.ghast.moan"); + set(GHAST_SCREAM, "mob.ghast.scream"); + set(HORSE_ANGRY, "mob.horse.angry"); + set(HORSE_ARMOR, "mob.horse.armor"); + set(HORSE_BREATHE, "mob.horse.breathe"); + set(HORSE_DEATH, "mob.horse.death"); + set(HORSE_GALLOP, "mob.horse.gallop"); + set(HORSE_HIT, "mob.horse.hit"); + set(HORSE_IDLE, "mob.horse.idle"); + set(HORSE_JUMP, "mob.horse.jump"); + set(HORSE_LAND, "mob.horse.land"); + set(HORSE_SADDLE, "mob.horse.leather"); + set(HORSE_SOFT, "mob.horse.soft"); + set(HORSE_WOOD, "mob.horse.wood"); + set(DONKEY_ANGRY, "mob.horse.donkey.angry"); + set(DONKEY_DEATH, "mob.horse.donkey.death"); + set(DONKEY_HIT, "mob.horse.donkey.hit"); + set(DONKEY_IDLE, "mob.horse.donkey.idle"); + set(HORSE_SKELETON_DEATH, "mob.horse.skeleton.death"); + set(HORSE_SKELETON_HIT, "mob.horse.skeleton.hit"); + set(HORSE_SKELETON_IDLE, "mob.horse.skeleton.idle"); + set(HORSE_ZOMBIE_DEATH, "mob.horse.zombie.death"); + set(HORSE_ZOMBIE_HIT, "mob.horse.zombie.hit"); + set(HORSE_ZOMBIE_IDLE, "mob.horse.zombie.idle"); + set(IRONGOLEM_DEATH, "mob.irongolem.death"); + set(IRONGOLEM_HIT, "mob.irongolem.hit"); + set(IRONGOLEM_THROW, "mob.irongolem.throw"); + set(IRONGOLEM_WALK, "mob.irongolem.walk"); + set(MAGMACUBE_WALK, "mob.magmacube.small"); + set(MAGMACUBE_WALK2, "mob.magmacube.big"); + set(MAGMACUBE_JUMP, "mob.magmacube.jump"); + set(PIG_IDLE, "mob.pig.say"); + set(PIG_DEATH, "mob.pig.death"); + set(PIG_WALK, "mob.pig.step"); + set(SHEEP_IDLE, "mob.sheep.say"); + set(SHEEP_SHEAR, "mob.sheep.shear"); + set(SHEEP_WALK, "mob.sheep.step"); + set(SILVERFISH_HIT, "mob.silverfish.hit"); + set(SILVERFISH_KILL, "mob.silverfish.kill"); + set(SILVERFISH_IDLE, "mob.silverfish.say"); + set(SILVERFISH_WALK, "mob.silverfish.step"); + set(SKELETON_IDLE, "mob.skeleton.say"); + set(SKELETON_DEATH, "mob.skeleton.death"); + set(SKELETON_HURT, "mob.skeleton.hurt"); + set(SKELETON_WALK, "mob.skeleton.step"); + set(SLIME_ATTACK, "mob.slime.attack"); + set(SLIME_WALK, "mob.slime.small"); + set(SLIME_WALK2, "mob.slime.big"); + set(SPIDER_IDLE, "mob.spider.say"); + set(SPIDER_DEATH, "mob.spider.death"); + set(SPIDER_WALK, "mob.spider.step"); + set(VILLAGER_DEATH, "mob.villager.death"); + set(VILLAGER_HAGGLE, "mob.villager.haggle"); + set(VILLAGER_HIT, "mob.villager.hit"); + set(VILLAGER_IDLE, "mob.villager.idle"); + set(VILLAGER_NO, "mob.villager.no"); + set(VILLAGER_YES, "mob.villager.yes"); + set(WITHER_DEATH, "mob.wither.death"); + set(WITHER_HURT, "mob.wither.hurt"); + set(WITHER_IDLE, "mob.wither.idle"); + set(WITHER_SHOOT, "mob.wither.shoot"); + set(WITHER_SPAWN, "mob.wither.spawn"); + set(WOLF_BARK, "mob.wolf.bark"); + set(WOLF_DEATH, "mob.wolf.death"); + set(WOLF_GROWL, "mob.wolf.growl"); + set(WOLF_HOWL, "mob.wolf.howl"); + set(WOLF_HURT, "mob.wolf.hurt"); + set(WOLF_PANT, "mob.wolf.panting"); + set(WOLF_SHAKE, "mob.wolf.shake"); + set(WOLF_WALK, "mob.wolf.step"); + set(WOLF_WHINE, "mob.wolf.whine"); + set(ZOMBIE_METAL, "mob.zombie.metal"); + set(ZOMBIE_WOOD, "mob.zombie.wood"); + set(ZOMBIE_WOODBREAK, "mob.zombie.woodbreak"); + set(ZOMBIE_IDLE, "mob.zombie.say"); + set(ZOMBIE_DEATH, "mob.zombie.death"); + set(ZOMBIE_HURT, "mob.zombie.hurt"); + set(ZOMBIE_INFECT, "mob.zombie.infect"); + set(ZOMBIE_UNFECT, "mob.zombie.unfect"); + set(ZOMBIE_REMEDY, "mob.zombie.remedy"); + set(ZOMBIE_WALK, "mob.zombie.step"); + set(ZOMBIE_PIG_IDLE, "mob.zombiepig.zpig"); + set(ZOMBIE_PIG_ANGRY, "mob.zombiepig.zpigangry"); + set(ZOMBIE_PIG_DEATH, "mob.zombiepig.zpigdeath"); + set(ZOMBIE_PIG_HURT, "mob.zombiepig.zpighurt"); + // Note (blocks) + set(NOTE_BASS_GUITAR, "note.bassattack"); + set(NOTE_SNARE_DRUM, "note.snare"); + set(NOTE_PLING, "note.pling"); + set(NOTE_BASS, "note.bass"); + set(NOTE_PIANO, "note.harp"); + set(NOTE_BASS_DRUM, "note.bd"); + set(NOTE_STICKS, "note.hat"); + // Portal + set(PORTAL, "portal.portal"); + set(PORTAL_TRAVEL, "portal.travel"); + set(PORTAL_TRIGGER, "portal.trigger"); + // Random + set(ANVIL_BREAK, "random.anvil_break"); + set(ANVIL_LAND, "random.anvil_land"); + set(ANVIL_USE, "random.anvil_use"); + set(SHOOT_ARROW, "random.bow"); + set(ARROW_HIT, "random.bowhit"); + set(ITEM_BREAK, "random.break"); + set(BURP, "random.burp"); + set(CHEST_CLOSE, "random.chestclosed"); + set(CHEST_OPEN, "random.chestopen"); + set(CLICK, "random.click"); + set(DOOR_CLOSE, "random.door_close"); + set(DOOR_OPEN, "random.door_open"); + set(DRINK, "random.drink"); + set(EAT, "random.eat"); + set(EXPLODE, "random.explode"); + set(FIZZ, "random.fizz"); + set(FUSE, "creeper.primed"); + set(GLASS, "dig.glass"); + set(LEVEL_UP, "random.levelup"); + set(ORB_PICKUP, "random.orb"); + set(ITEM_PICKUP, "random.pop"); + set(SPLASH, "random.splash"); + set(SUCCESSFUL_HIT, "random.successful_hit"); + set(WOOD_CLICK, "random.wood_click"); + // Step + set(STEP_WOOL, "step.cloth"); + set(STEP_GRASS, "step.grass"); + set(STEP_GRAVEL, "step.gravel"); + set(STEP_LADDER, "step.ladder"); + set(STEP_SAND, "step.sand"); + set(STEP_SNOW, "step.snow"); + set(STEP_STONE, "step.stone"); + set(STEP_WOOD, "step.wood"); + // Tile + set(PISTON_EXTEND, "tile.piston.out"); + set(PISTON_RETRACT, "tile.piston.in"); + } + + private static void set(Sound sound, String key) { + sounds[sound.ordinal()] = key; + } + + public static String getSound(final Sound sound) { + Validate.notNull(sound, "Sound cannot be null"); + return sounds[sound.ordinal()]; + } + + private CraftSound() {} +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java new file mode 100644 index 0000000..24c8bf2 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java @@ -0,0 +1,142 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.EntityTypes; +import net.minecraft.server.MonsterEggInfo; +import net.minecraft.server.StatisticList; + +import org.bukkit.Achievement; +import org.bukkit.Statistic; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + +import com.google.common.base.CaseFormat; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableMap; + +public class CraftStatistic { + private static final BiMap statistics; + private static final BiMap achievements; + + static { + ImmutableMap specialCases = ImmutableMap. builder() + .put("achievement.buildWorkBench", Achievement.BUILD_WORKBENCH) + .put("achievement.diamonds", Achievement.GET_DIAMONDS) + .put("achievement.portal", Achievement.NETHER_PORTAL) + .put("achievement.ghast", Achievement.GHAST_RETURN) + .put("achievement.theEnd", Achievement.END_PORTAL) + .put("achievement.theEnd2", Achievement.THE_END) + .put("achievement.blazeRod", Achievement.GET_BLAZE_ROD) + .put("achievement.potion", Achievement.BREW_POTION) + .build(); + ImmutableBiMap.Builder statisticBuilder = ImmutableBiMap.builder(); + ImmutableBiMap.Builder achievementBuilder = ImmutableBiMap.builder(); + for (Statistic statistic : Statistic.values()) { + if (statistic == Statistic.PLAY_ONE_TICK) { + statisticBuilder.put("stat.playOneMinute", statistic); + } else { + statisticBuilder.put("stat." + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, statistic.name()), statistic); + } + } + for (Achievement achievement : Achievement.values()) { + if (specialCases.values().contains(achievement)) { + continue; + } + achievementBuilder.put("achievement." + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, achievement.name()), achievement); + } + + achievementBuilder.putAll(specialCases); + + statistics = statisticBuilder.build(); + achievements = achievementBuilder.build(); + } + + private CraftStatistic() {} + + public static org.bukkit.Achievement getBukkitAchievement(net.minecraft.server.Achievement achievement) { + return getBukkitAchievementByName(achievement.name); + } + + public static org.bukkit.Achievement getBukkitAchievementByName(String name) { + return achievements.get(name); + } + + public static org.bukkit.Statistic getBukkitStatistic(net.minecraft.server.Statistic statistic) { + return getBukkitStatisticByName(statistic.name); + } + + public static org.bukkit.Statistic getBukkitStatisticByName(String name) { + if (name.startsWith("stat.killEntity")) { + name = "stat.killEntity"; + } + if (name.startsWith("stat.entityKilledBy")) { + name = "stat.entityKilledBy"; + } + if (name.startsWith("stat.breakItem")) { + name = "stat.breakItem"; + } + if (name.startsWith("stat.useItem")) { + name = "stat.useItem"; + } + if (name.startsWith("stat.mineBlock")) { + name = "stat.mineBlock"; + } + if (name.startsWith("stat.craftItem")) { + name = "stat.craftItem"; + } + return statistics.get(name); + } + + public static net.minecraft.server.Statistic getNMSStatistic(org.bukkit.Statistic statistic) { + return StatisticList.getStatistic(statistics.inverse().get(statistic)); + } + + public static net.minecraft.server.Achievement getNMSAchievement(org.bukkit.Achievement achievement) { + return (net.minecraft.server.Achievement) StatisticList.getStatistic(achievements.inverse().get(achievement)); + } + + public static net.minecraft.server.Statistic getMaterialStatistic(org.bukkit.Statistic stat, Material material) { + try { + if (stat == Statistic.MINE_BLOCK) { + return StatisticList.MINE_BLOCK_COUNT[material.getId()]; + } + if (stat == Statistic.CRAFT_ITEM) { + return StatisticList.CRAFT_BLOCK_COUNT[material.getId()]; + } + if (stat == Statistic.USE_ITEM) { + return StatisticList.USE_ITEM_COUNT[material.getId()]; + } + if (stat == Statistic.BREAK_ITEM) { + return StatisticList.BREAK_ITEM_COUNT[material.getId()]; + } + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + return null; + } + + public static net.minecraft.server.Statistic getEntityStatistic(org.bukkit.Statistic stat, EntityType entity) { + MonsterEggInfo monsteregginfo = (MonsterEggInfo) EntityTypes.eggInfo.get(Integer.valueOf(entity.getTypeId())); + + if (monsteregginfo != null) { + return monsteregginfo.killEntityStatistic; + } + return null; + } + + public static EntityType getEntityTypeFromStatistic(net.minecraft.server.Statistic statistic) { + String statisticString = statistic.name; + return EntityType.fromName(statisticString.substring(statisticString.lastIndexOf(".") + 1)); + } + + public static Material getMaterialFromStatistic(net.minecraft.server.Statistic statistic) { + String statisticString = statistic.name; + int id; + try { + id = Integer.valueOf(statisticString.substring(statisticString.lastIndexOf(".") + 1)); + } catch (NumberFormatException e) { + return null; + } + return Material.getMaterial(id); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftTravelAgent.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftTravelAgent.java new file mode 100644 index 0000000..84af016 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftTravelAgent.java @@ -0,0 +1,86 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.ChunkCoordinates; +import net.minecraft.server.PortalTravelAgent; +import net.minecraft.server.WorldServer; + +import org.bukkit.Location; +import org.bukkit.TravelAgent; + +public class CraftTravelAgent extends PortalTravelAgent implements TravelAgent { + + public static TravelAgent DEFAULT = null; + + private int searchRadius = 128; + private int creationRadius = 16; + private boolean canCreatePortal = true; + + public CraftTravelAgent(WorldServer worldserver) { + super(worldserver); + if (DEFAULT == null && worldserver.dimension == 0) { + DEFAULT = this; + } + } + + public Location findOrCreate(Location target) { + WorldServer worldServer = ((CraftWorld) target.getWorld()).getHandle(); + boolean before = worldServer.chunkProviderServer.forceChunkLoad; + worldServer.chunkProviderServer.forceChunkLoad = true; + + Location found = this.findPortal(target); + if (found == null) { + if (this.getCanCreatePortal() && this.createPortal(target)) { + found = this.findPortal(target); + } else { + found = target; // fallback to original if unable to find or create + } + } + + worldServer.chunkProviderServer.forceChunkLoad = before; + return found; + } + + public Location findPortal(Location location) { + PortalTravelAgent pta = ((CraftWorld) location.getWorld()).getHandle().getTravelAgent(); + + // Poweruser start - check a smaller area first + ChunkCoordinates found = pta.findPortal(location.getX(), location.getY(), location.getZ(), 10); + if(found == null) { + found = pta.findPortal(location.getX(), location.getY(), location.getZ(), this.getSearchRadius()); + } + // Poweruser end + + return found != null ? new Location(location.getWorld(), found.x, found.y, found.z, location.getYaw(), location.getPitch()) : null; + } + + public boolean createPortal(Location location) { + PortalTravelAgent pta = ((CraftWorld) location.getWorld()).getHandle().getTravelAgent(); + return pta.createPortal(location.getX(), location.getY(), location.getZ(), this.getCreationRadius()); + } + + public TravelAgent setSearchRadius(int radius) { + this.searchRadius = radius; + return this; + } + + public int getSearchRadius() { + return this.searchRadius; + } + + public TravelAgent setCreationRadius(int radius) { + this.creationRadius = radius < 2 ? 0 : radius; + return this; + } + + public int getCreationRadius() { + return this.creationRadius; + } + + public boolean getCanCreatePortal() { + return this.canCreatePortal; + } + + public void setCanCreatePortal(boolean create) { + this.canCreatePortal = create; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java new file mode 100644 index 0000000..f5f48a8 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -0,0 +1,1449 @@ +package org.bukkit.craftbukkit; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.UUID; + +import net.minecraft.server.*; + +import org.apache.commons.lang.Validate; +import org.bukkit.BlockChangeDelegate; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Difficulty; +import org.bukkit.Effect; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.entity.*; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.metadata.BlockMetadataStore; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.entity.*; +import org.bukkit.entity.Entity; +import org.bukkit.entity.minecart.ExplosiveMinecart; +import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.entity.minecart.PoweredMinecart; +import org.bukkit.entity.minecart.SpawnerMinecart; +import org.bukkit.entity.minecart.StorageMinecart; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; +import org.bukkit.event.world.SpawnChangeEvent; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.util.Vector; + +public class CraftWorld implements World { + public static final int CUSTOM_DIMENSION_OFFSET = 10; + + private final WorldServer world; + private Environment environment; + private final CraftServer server = (CraftServer) Bukkit.getServer(); + private final ChunkGenerator generator; + private final List populators = new ArrayList(); + private final BlockMetadataStore blockMetadata = new BlockMetadataStore(this); + private int monsterSpawn = -1; + private int animalSpawn = -1; + private int waterAnimalSpawn = -1; + private int ambientSpawn = -1; + private int chunkLoadCount = 0; + private int chunkGCTickCount; + + private static final Random rand = new Random(); + + public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) { + this.world = world; + this.generator = gen; + + environment = env; + + if (server.chunkGCPeriod > 0) { + chunkGCTickCount = rand.nextInt(server.chunkGCPeriod); + } + } + + public Block getBlockAt(int x, int y, int z) { + return getChunkAt(x >> 4, z >> 4).getBlock(x & 0xF, y & 0xFF, z & 0xF); + } + + public int getBlockTypeIdAt(int x, int y, int z) { + return world.getTypeId(x, y, z); + } + + public int getHighestBlockYAt(int x, int z) { + if (!isChunkLoaded(x >> 4, z >> 4)) { + loadChunk(x >> 4, z >> 4); + } + + return world.getHighestBlockYAt(x, z); + } + + public Location getSpawnLocation() { + // Poweruser start + WorldData worlddata = world.getWorldData(); + return new Location(this, worlddata.c(), worlddata.d(), worlddata.e(), worlddata.getSpawnYaw(), worlddata.getSpawnPitch()); + } + + public boolean setSpawnLocation(int x, int y, int z, float yaw, float pitch) { + try { + Location previousLocation = getSpawnLocation(); + world.worldData.setSpawn(x, y, z, yaw, pitch); + + // Notify anyone who's listening. + SpawnChangeEvent event = new SpawnChangeEvent(this, previousLocation); + server.getPluginManager().callEvent(event); + + return true; + } catch (Exception e) { + return false; + } + } + // Poweruser end + + public boolean setSpawnLocation(int x, int y, int z) { + try { + Location previousLocation = getSpawnLocation(); + world.worldData.setSpawn(x, y, z); + + // Notify anyone who's listening. + SpawnChangeEvent event = new SpawnChangeEvent(this, previousLocation); + server.getPluginManager().callEvent(event); + + return true; + } catch (Exception e) { + return false; + } + } + + // PaperSpigot start - Async chunk load API + public void getChunkAtAsync(final int x, final int z, final ChunkLoadCallback callback) { + final ChunkProviderServer cps = this.world.chunkProviderServer; + cps.getChunkAt(x, z, new Runnable() { + @Override + public void run() { + callback.onLoad(cps.getChunkAt(x, z).bukkitChunk); + } + }); + } + public void getChunkAtAsync(Block block, ChunkLoadCallback callback) { + getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, callback); + } + public void getChunkAtAsync(Location location, ChunkLoadCallback callback) { + getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4, callback); + } + // PaperSpigot end + + public Chunk getChunkAt(int x, int z) { + return this.world.chunkProviderServer.getChunkAt(x, z).bukkitChunk; + } + + public Chunk getChunkAt(Block block) { + return getChunkAt(block.getX() >> 4, block.getZ() >> 4); + } + + public boolean isChunkLoaded(int x, int z) { + return world.chunkProviderServer.isChunkLoaded(x, z); + } + + public Chunk[] getLoadedChunks() { + Object[] chunks = world.chunkProviderServer.chunks.values().toArray(); + org.bukkit.Chunk[] craftChunks = new CraftChunk[chunks.length]; + + for (int i = 0; i < chunks.length; i++) { + net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) chunks[i]; + craftChunks[i] = chunk.bukkitChunk; + } + + return craftChunks; + } + + public void loadChunk(int x, int z) { + loadChunk(x, z, true); + } + + public boolean unloadChunk(Chunk chunk) { + return unloadChunk(chunk.getX(), chunk.getZ()); + } + + public boolean unloadChunk(int x, int z) { + return unloadChunk(x, z, true); + } + + public boolean unloadChunk(int x, int z, boolean save) { + return unloadChunk(x, z, save, false); + } + + public boolean unloadChunkRequest(int x, int z) { + return unloadChunkRequest(x, z, true); + } + + public boolean unloadChunkRequest(int x, int z, boolean safe) { + org.spigotmc.AsyncCatcher.catchOp( "chunk unload"); // Spigot + if (isChunkInUse(x, z)) { // MineHQ - never unload in-use chunks + return false; + } + + world.chunkProviderServer.queueUnload(x, z, true); // MineHQ + + return true; + } + + public boolean unloadChunk(int x, int z, boolean save, boolean safe) { + org.spigotmc.AsyncCatcher.catchOp( "chunk unload"); // Spigot + if (isChunkInUse(x, z)) { // MineHQ - never unload in-use chunks + return false; + } + + net.minecraft.server.Chunk chunk = world.chunkProviderServer.getChunkIfLoaded(x, z); + // PaperSpigot start - Don't create a chunk just to unload it + if (chunk == null) { + return false; + } + // PaperSpigot end + + if (chunk.mustSave) { // If chunk had previously been queued to save, must do save to avoid loss of that data + save = true; + } + + chunk.removeEntities(); // Always remove entities - even if discarding, need to get them out of world table + + if (save && !(chunk instanceof EmptyChunk)) { + world.chunkProviderServer.saveChunk(chunk); + world.chunkProviderServer.saveChunkNOP(chunk); + } + + world.chunkProviderServer.unloadQueue.remove(x, z); + world.chunkProviderServer.chunks.remove(x, z); // MineHQ + + return true; + } + + public boolean regenerateChunk(int x, int z) { + unloadChunk(x, z, false, false); + + world.chunkProviderServer.unloadQueue.remove(x, z); + + net.minecraft.server.Chunk chunk = null; + + if (world.chunkProviderServer.chunkProvider == null) { + chunk = world.chunkProviderServer.emptyChunk; + } else { + chunk = world.chunkProviderServer.chunkProvider.getOrCreateChunk(x, z); + } + + chunkLoadPostProcess(chunk, x, z); + + refreshChunk(x, z); + + return chunk != null; + } + + public boolean refreshChunk(int x, int z) { + if (!isChunkLoaded(x, z)) { + return false; + } + + int px = x << 4; + int pz = z << 4; + + // If there are more than 64 updates to a chunk at once, it will update all 'touched' sections within the chunk + // And will include biome data if all sections have been 'touched' + // This flags 65 blocks distributed across all the sections of the chunk, so that everything is sent, including biomes + int height = getMaxHeight() / 16; + for (int idx = 0; idx < 64; idx++) { + world.notify(px + (idx / height), ((idx % height) * 16), pz); + } + world.notify(px + 15, (height * 16) - 1, pz + 15); + + return true; + } + + public boolean isChunkInUse(int x, int z) { + return world.getPlayerChunkMap().isChunkInUse(x, z); + } + + public boolean loadChunk(int x, int z, boolean generate) { + org.spigotmc.AsyncCatcher.catchOp( "chunk load"); // Spigot + chunkLoadCount++; + if (generate) { + // Use the default variant of loadChunk when generate == true. + return world.chunkProviderServer.getChunkAt(x, z) != null; + } + + world.chunkProviderServer.unloadQueue.remove(x, z); + net.minecraft.server.Chunk chunk = world.chunkProviderServer.chunks.get(LongHash.toLong(x, z)); // MineHQ + + if (chunk == null) { + world.timings.syncChunkLoadTimer.startTiming(); // Spigot + chunk = world.chunkProviderServer.loadChunk(x, z); + + chunkLoadPostProcess(chunk, x, z); + world.timings.syncChunkLoadTimer.stopTiming(); // Spigot + } + return chunk != null; + } + + private void chunkLoadPostProcess(net.minecraft.server.Chunk chunk, int x, int z) { + if (chunk != null) { + world.chunkProviderServer.chunks.put(LongHash.toLong(x, z), chunk); // MineHQ + + chunk.addEntities(); + + if (!chunk.done && world.chunkProviderServer.isChunkLoaded(x + 1, z + 1) && world.chunkProviderServer.isChunkLoaded(x, z + 1) && world.chunkProviderServer.isChunkLoaded(x + 1, z)) { + world.chunkProviderServer.getChunkAt(world.chunkProviderServer, x, z); + } + + if (world.chunkProviderServer.isChunkLoaded(x - 1, z) && !world.chunkProviderServer.getOrCreateChunk(x - 1, z).done && world.chunkProviderServer.isChunkLoaded(x - 1, z + 1) && world.chunkProviderServer.isChunkLoaded(x, z + 1) && world.chunkProviderServer.isChunkLoaded(x - 1, z)) { + world.chunkProviderServer.getChunkAt(world.chunkProviderServer, x - 1, z); + } + + if (world.chunkProviderServer.isChunkLoaded(x, z - 1) && !world.chunkProviderServer.getOrCreateChunk(x, z - 1).done && world.chunkProviderServer.isChunkLoaded(x + 1, z - 1) && world.chunkProviderServer.isChunkLoaded(x, z - 1) && world.chunkProviderServer.isChunkLoaded(x + 1, z)) { + world.chunkProviderServer.getChunkAt(world.chunkProviderServer, x, z - 1); + } + + if (world.chunkProviderServer.isChunkLoaded(x - 1, z - 1) && !world.chunkProviderServer.getOrCreateChunk(x - 1, z - 1).done && world.chunkProviderServer.isChunkLoaded(x - 1, z - 1) && world.chunkProviderServer.isChunkLoaded(x, z - 1) && world.chunkProviderServer.isChunkLoaded(x - 1, z)) { + world.chunkProviderServer.getChunkAt(world.chunkProviderServer, x - 1, z - 1); + } + } + } + + public boolean isChunkLoaded(Chunk chunk) { + return isChunkLoaded(chunk.getX(), chunk.getZ()); + } + + public void loadChunk(Chunk chunk) { + loadChunk(chunk.getX(), chunk.getZ()); + ((CraftChunk) getChunkAt(chunk.getX(), chunk.getZ())).getHandle().bukkitChunk = chunk; + } + + public WorldServer getHandle() { + return world; + } + + public org.bukkit.entity.Item dropItem(Location loc, ItemStack item) { + Validate.notNull(item, "Cannot drop a Null item."); + Validate.isTrue(item.getTypeId() != 0, "Cannot drop AIR."); + EntityItem entity = new EntityItem(world, loc.getX(), loc.getY(), loc.getZ(), CraftItemStack.asNMSCopy(item)); + entity.pickupDelay = 10; + world.addEntity(entity); + // TODO this is inconsistent with how Entity.getBukkitEntity() works. + // However, this entity is not at the moment backed by a server entity class so it may be left. + return new CraftItem(world.getServer(), entity); + } + + public org.bukkit.entity.Item dropItemNaturally(Location loc, ItemStack item) { + double xs = world.random.nextFloat() * 0.7F + (1.0F - 0.7F) * 0.5D; + double ys = world.random.nextFloat() * 0.7F + (1.0F - 0.7F) * 0.5D; + double zs = world.random.nextFloat() * 0.7F + (1.0F - 0.7F) * 0.5D; + loc = loc.clone(); + loc.setX(loc.getX() + xs); + loc.setY(loc.getY() + ys); + loc.setZ(loc.getZ() + zs); + return dropItem(loc, item); + } + + public Arrow spawnArrow(Location loc, Vector velocity, float speed, float spread) { + Validate.notNull(loc, "Can not spawn arrow with a null location"); + Validate.notNull(velocity, "Can not spawn arrow with a null velocity"); + + EntityArrow arrow = new EntityArrow(world); + arrow.setPositionRotation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); + arrow.shoot(velocity.getX(), velocity.getY(), velocity.getZ(), speed, spread); + world.addEntity(arrow); + return (Arrow) arrow.getBukkitEntity(); + } + + @Deprecated + public LivingEntity spawnCreature(Location loc, CreatureType creatureType) { + return spawnCreature(loc, creatureType.toEntityType()); + } + + @Deprecated + public LivingEntity spawnCreature(Location loc, EntityType creatureType) { + Validate.isTrue(creatureType.isAlive(), "EntityType not instance of LivingEntity"); + return (LivingEntity) spawnEntity(loc, creatureType); + } + + public Entity spawnEntity(Location loc, EntityType entityType) { + return spawn(loc, entityType.getEntityClass()); + } + + public LightningStrike strikeLightning(Location loc) { + EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ()); + world.strikeLightning(lightning); + return new CraftLightningStrike(server, lightning); + } + + public LightningStrike strikeLightningEffect(Location loc) { + EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ(), true); + world.strikeLightning(lightning); + return new CraftLightningStrike(server, lightning); + } + + public boolean generateTree(Location loc, TreeType type) { + net.minecraft.server.WorldGenerator gen; + switch (type) { + case BIG_TREE: + gen = new WorldGenBigTree(true); + break; + case BIRCH: + gen = new WorldGenForest(true, false); + break; + case REDWOOD: + gen = new WorldGenTaiga2(true); + break; + case TALL_REDWOOD: + gen = new WorldGenTaiga1(); + break; + case JUNGLE: + gen = new WorldGenJungleTree(true, 10, 20, 3, 3); // Magic values as in BlockSapling + break; + case SMALL_JUNGLE: + gen = new WorldGenTrees(true, 4 + rand.nextInt(7), 3, 3, false); + break; + case COCOA_TREE: + gen = new WorldGenTrees(true, 4 + rand.nextInt(7), 3, 3, true); + break; + case JUNGLE_BUSH: + gen = new WorldGenGroundBush(3, 0); + break; + case RED_MUSHROOM: + gen = new WorldGenHugeMushroom(1); + break; + case BROWN_MUSHROOM: + gen = new WorldGenHugeMushroom(0); + break; + case SWAMP: + gen = new WorldGenSwampTree(); + break; + case ACACIA: + gen = new WorldGenAcaciaTree(true); + break; + case DARK_OAK: + gen = new WorldGenForestTree(true); + break; + case MEGA_REDWOOD: + gen = new WorldGenMegaTree(false, rand.nextBoolean()); + break; + case TALL_BIRCH: + gen = new WorldGenForest(true, true); + break; + case TREE: + default: + gen = new WorldGenTrees(true); + break; + } + + return gen.generate(world, rand, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + } + + public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) { + world.captureTreeGeneration = true; + world.captureBlockStates = true; + boolean grownTree = generateTree(loc, type); + world.captureBlockStates = false; + world.captureTreeGeneration = false; + if (grownTree) { // Copy block data to delegate + for (BlockState blockstate : world.capturedBlockStates) { + int x = blockstate.getX(); + int y = blockstate.getY(); + int z = blockstate.getZ(); + net.minecraft.server.Block oldBlock = world.getType(x, y, z); + int typeId = blockstate.getTypeId(); + int data = blockstate.getRawData(); + int flag = ((CraftBlockState)blockstate).getFlag(); + delegate.setTypeIdAndData(x, y, z, typeId, data); + net.minecraft.server.Block newBlock = world.getType(x, y, z); + world.notifyAndUpdatePhysics(x, y, z, null, oldBlock, newBlock, flag); + } + world.capturedBlockStates.clear(); + return true; + } else { + world.capturedBlockStates.clear(); + return false; + } + } + + public TileEntity getTileEntityAt(final int x, final int y, final int z) { + return world.getTileEntity(x, y, z); + } + + public String getName() { + return world.worldData.getName(); + } + + @Deprecated + public long getId() { + return world.worldData.getSeed(); + } + + public UUID getUID() { + return world.getDataManager().getUUID(); + } + + @Override + public String toString() { + return "CraftWorld{name=" + getName() + '}'; + } + + public long getTime() { + long time = getFullTime() % 24000; + if (time < 0) time += 24000; + return time; + } + + public void setTime(long time) { + long margin = (time - getFullTime()) % 24000; + if (margin < 0) margin += 24000; + setFullTime(getFullTime() + margin); + } + + public long getFullTime() { + return world.getDayTime(); + } + + public void setFullTime(long time) { + world.setDayTime(time); + + // Forces the client to update to the new time immediately + for (Player p : getPlayers()) { + CraftPlayer cp = (CraftPlayer) p; + if (cp.getHandle().playerConnection == null) continue; + + cp.getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateTime(cp.getHandle().world.getTime(), cp.getHandle().getPlayerTime(), cp.getHandle().world.getGameRules().getBoolean("doDaylightCycle"))); + } + } + + public boolean createExplosion(double x, double y, double z, float power) { + return createExplosion(x, y, z, power, false, true); + } + + public boolean createExplosion(double x, double y, double z, float power, boolean setFire) { + return createExplosion(x, y, z, power, setFire, true); + } + + public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks) { + return !world.createExplosion(null, x, y, z, power, setFire, breakBlocks).wasCanceled; + } + + public boolean createExplosion(Location loc, float power) { + return createExplosion(loc, power, false); + } + + public boolean createExplosion(Location loc, float power, boolean setFire) { + return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power, setFire); + } + + public Environment getEnvironment() { + return environment; + } + + public void setEnvironment(Environment env) { + if (environment != env) { + environment = env; + world.worldProvider = WorldProvider.byDimension(environment.getId()); + } + } + + public Block getBlockAt(Location location) { + return getBlockAt(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + public int getBlockTypeIdAt(Location location) { + return getBlockTypeIdAt(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + public int getHighestBlockYAt(Location location) { + return getHighestBlockYAt(location.getBlockX(), location.getBlockZ()); + } + + public Chunk getChunkAt(Location location) { + return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4); + } + + public ChunkGenerator getGenerator() { + return generator; + } + + public List getPopulators() { + return populators; + } + + public Block getHighestBlockAt(int x, int z) { + return getBlockAt(x, getHighestBlockYAt(x, z), z); + } + + public Block getHighestBlockAt(Location location) { + return getHighestBlockAt(location.getBlockX(), location.getBlockZ()); + } + + public Biome getBiome(int x, int z) { + return CraftBlock.biomeBaseToBiome(this.world.getBiome(x, z)); + } + + public void setBiome(int x, int z, Biome bio) { + BiomeBase bb = CraftBlock.biomeToBiomeBase(bio); + if (this.world.isLoaded(x, 0, z)) { + net.minecraft.server.Chunk chunk = this.world.getChunkAtWorldCoords(x, z); + + if (chunk != null) { + byte[] biomevals = chunk.m(); + biomevals[((z & 0xF) << 4) | (x & 0xF)] = (byte)bb.id; + } + } + } + + public double getTemperature(int x, int z) { + return this.world.getBiome(x, z).temperature; + } + + public double getHumidity(int x, int z) { + return this.world.getBiome(x, z).humidity; + } + + public List getEntities() { + List list = new ArrayList(); + + for (Object o : world.entityList) { + if (o instanceof net.minecraft.server.Entity) { + net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o; + Entity bukkitEntity = mcEnt.getBukkitEntity(); + + // Assuming that bukkitEntity isn't null + if (bukkitEntity != null) { + list.add(bukkitEntity); + } + } + } + + return list; + } + + public List getLivingEntities() { + List list = new ArrayList(); + + for (Object o : world.entityList) { + if (o instanceof net.minecraft.server.Entity) { + net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o; + Entity bukkitEntity = mcEnt.getBukkitEntity(); + + // Assuming that bukkitEntity isn't null + if (bukkitEntity != null && bukkitEntity instanceof LivingEntity) { + list.add((LivingEntity) bukkitEntity); + } + } + } + + return list; + } + + @SuppressWarnings("unchecked") + @Deprecated + public Collection getEntitiesByClass(Class... classes) { + return (Collection)getEntitiesByClasses(classes); + } + + @SuppressWarnings("unchecked") + public Collection getEntitiesByClass(Class clazz) { + Collection list = new ArrayList(); + + for (Object entity: world.entityList) { + if (entity instanceof net.minecraft.server.Entity) { + Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity(); + + if (bukkitEntity == null) { + continue; + } + + Class bukkitClass = bukkitEntity.getClass(); + + if (clazz.isAssignableFrom(bukkitClass)) { + list.add((T) bukkitEntity); + } + } + } + + return list; + } + + public Collection getEntitiesByClasses(Class... classes) { + Collection list = new ArrayList(); + + for (Object entity: world.entityList) { + if (entity instanceof net.minecraft.server.Entity) { + Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity(); + + if (bukkitEntity == null) { + continue; + } + + Class bukkitClass = bukkitEntity.getClass(); + + for (Class clazz : classes) { + if (clazz.isAssignableFrom(bukkitClass)) { + list.add(bukkitEntity); + break; + } + } + } + } + + return list; + } + + public List getPlayers() { + List list = new ArrayList(); + + for (Object o : world.players) { + if (o instanceof net.minecraft.server.Entity) { + net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o; + Entity bukkitEntity = mcEnt.getBukkitEntity(); + + if ((bukkitEntity != null) && (bukkitEntity instanceof Player)) { + list.add((Player) bukkitEntity); + } + } + } + + return list; + } + + public void save() { + // PaperSpigot start - Improved autosave + save(true); + } + + public void save(boolean forceSave) { + // PaperSpigot end + this.server.checkSaveState(); + try { + boolean oldSave = world.savingDisabled; + + world.savingDisabled = false; + world.save(forceSave, null); // PaperSpigot + + world.savingDisabled = oldSave; + } catch (ExceptionWorldConflict ex) { + ex.printStackTrace(); + } + } + + public boolean isAutoSave() { + return !world.savingDisabled; + } + + public void setAutoSave(boolean value) { + world.savingDisabled = !value; + } + + public void setDifficulty(Difficulty difficulty) { + this.getHandle().difficulty = EnumDifficulty.getById(difficulty.getValue()); + } + + public Difficulty getDifficulty() { + return Difficulty.getByValue(this.getHandle().difficulty.ordinal()); + } + + public BlockMetadataStore getBlockMetadata() { + return blockMetadata; + } + + public boolean hasStorm() { + return world.worldData.hasStorm(); + } + + public void setStorm(boolean hasStorm) { + CraftServer server = world.getServer(); + + WeatherChangeEvent weather = new WeatherChangeEvent(this, hasStorm); + server.getPluginManager().callEvent(weather); + if (!weather.isCancelled()) { + world.worldData.setStorm(hasStorm); + + // These numbers are from Minecraft + if (hasStorm) { + setWeatherDuration(rand.nextInt(12000) + 12000); + } else { + setWeatherDuration(rand.nextInt(168000) + 12000); + } + } + } + + public int getWeatherDuration() { + return world.worldData.getWeatherDuration(); + } + + public void setWeatherDuration(int duration) { + world.worldData.setWeatherDuration(duration); + } + + public boolean isThundering() { + return hasStorm() && world.worldData.isThundering(); + } + + public void setThundering(boolean thundering) { + if (thundering && !hasStorm()) setStorm(true); + CraftServer server = world.getServer(); + + ThunderChangeEvent thunder = new ThunderChangeEvent(this, thundering); + server.getPluginManager().callEvent(thunder); + if (!thunder.isCancelled()) { + world.worldData.setThundering(thundering); + + // These numbers are from Minecraft + if (thundering) { + setThunderDuration(rand.nextInt(12000) + 3600); + } else { + setThunderDuration(rand.nextInt(168000) + 12000); + } + } + } + + public int getThunderDuration() { + return world.worldData.getThunderDuration(); + } + + public void setThunderDuration(int duration) { + world.worldData.setThunderDuration(duration); + } + + public long getSeed() { + return world.worldData.getSeed(); + } + + public boolean getPVP() { + return world.pvpMode; + } + + public void setPVP(boolean pvp) { + world.pvpMode = pvp; + } + + public void playEffect(Player player, Effect effect, int data) { + playEffect(player.getLocation(), effect, data, 0); + } + + public void playEffect(Location location, Effect effect, int data) { + playEffect(location, effect, data, 64); + } + + public void playEffect(Location loc, Effect effect, T data) { + playEffect(loc, effect, data, 64); + } + + public void playEffect(Location loc, Effect effect, T data, int radius) { + if (data != null) { + Validate.isTrue(data.getClass().equals(effect.getData()), "Wrong kind of data for this effect!"); + } else { + Validate.isTrue(effect.getData() == null, "Wrong kind of data for this effect!"); + } + + if (data != null && data.getClass().equals( org.bukkit.material.MaterialData.class )) { + org.bukkit.material.MaterialData materialData = (org.bukkit.material.MaterialData) data; + Validate.isTrue( materialData.getItemType().isBlock(), "Material must be block" ); + spigot().playEffect( loc, effect, materialData.getItemType().getId(), materialData.getData(), 0, 0, 0, 1, 1, radius ); + } else { + int dataValue = data == null ? 0 : CraftEffect.getDataValue( effect, data ); + playEffect( loc, effect, dataValue, radius ); + } + } + + public void playEffect(Location location, Effect effect, int data, int radius) { + spigot().playEffect( location, effect, data, 0, 0, 0, 0, 1, 1, radius ); + } + + public T spawn(Location location, Class clazz) throws IllegalArgumentException { + return spawn(location, clazz, SpawnReason.CUSTOM); + } + + public FallingBlock spawnFallingBlock(Location location, org.bukkit.Material material, byte data) throws IllegalArgumentException { + Validate.notNull(location, "Location cannot be null"); + Validate.notNull(material, "Material cannot be null"); + Validate.isTrue(material.isBlock(), "Material must be a block"); + + double x = location.getBlockX() + 0.5; + double y = location.getBlockY() + 0.5; + double z = location.getBlockZ() + 0.5; + + // PaperSpigot start - Add FallingBlock and TNT source location API + location = location.clone(); + EntityFallingBlock entity = new EntityFallingBlock(location, world, x, y, z, net.minecraft.server.Block.getById(material.getId()), data); + // PaperSpigot end + entity.ticksLived = 1; + + world.addEntity(entity, SpawnReason.CUSTOM); + return (FallingBlock) entity.getBukkitEntity(); + } + + public FallingBlock spawnFallingBlock(Location location, int blockId, byte blockData) throws IllegalArgumentException { + return spawnFallingBlock(location, org.bukkit.Material.getMaterial(blockId), blockData); + } + + @SuppressWarnings("unchecked") + public T spawn(Location location, Class clazz, SpawnReason reason) throws IllegalArgumentException { + if (location == null || clazz == null) { + throw new IllegalArgumentException("Location or entity class cannot be null"); + } + + net.minecraft.server.Entity entity = null; + + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + float pitch = location.getPitch(); + float yaw = location.getYaw(); + + // order is important for some of these + if (Boat.class.isAssignableFrom(clazz)) { + entity = new EntityBoat(world, x, y, z); + } else if (FallingBlock.class.isAssignableFrom(clazz)) { + x = location.getBlockX(); + y = location.getBlockY(); + z = location.getBlockZ(); + int type = world.getTypeId((int) x, (int) y, (int) z); + int data = world.getData((int) x, (int) y, (int) z); + + // PaperSpigot start - Add FallingBlock and TNT source location API + location = location.clone(); + entity = new EntityFallingBlock(location, world, x + 0.5, y + 0.5, z + 0.5, net.minecraft.server.Block.getById(type), data); + // PaperSpigot end + } else if (Projectile.class.isAssignableFrom(clazz)) { + if (Snowball.class.isAssignableFrom(clazz)) { + entity = new EntitySnowball(world, x, y, z); + } else if (Egg.class.isAssignableFrom(clazz)) { + entity = new EntityEgg(world, x, y, z); + } else if (Arrow.class.isAssignableFrom(clazz)) { + entity = new EntityArrow(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (ThrownExpBottle.class.isAssignableFrom(clazz)) { + entity = new EntityThrownExpBottle(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (EnderPearl.class.isAssignableFrom(clazz)) { + entity = new EntityEnderPearl(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (ThrownPotion.class.isAssignableFrom(clazz)) { + entity = new EntityPotion(world, x, y, z, CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.POTION, 1))); + } else if (Fireball.class.isAssignableFrom(clazz)) { + if (SmallFireball.class.isAssignableFrom(clazz)) { + entity = new EntitySmallFireball(world); + } else if (WitherSkull.class.isAssignableFrom(clazz)) { + entity = new EntityWitherSkull(world); + } else { + entity = new EntityLargeFireball(world); + } + entity.setPositionRotation(x, y, z, yaw, pitch); + Vector direction = location.getDirection().multiply(10); + ((EntityFireball) entity).setDirection(direction.getX(), direction.getY(), direction.getZ()); + } + } else if (Minecart.class.isAssignableFrom(clazz)) { + if (PoweredMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartFurnace(world, x, y, z); + } else if (StorageMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartChest(world, x, y, z); + } else if (ExplosiveMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartTNT(world, x, y, z); + } else if (HopperMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartHopper(world, x, y, z); + } else if (SpawnerMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartMobSpawner(world, x, y, z); + } else { // Default to rideable minecart for pre-rideable compatibility + entity = new EntityMinecartRideable(world, x, y, z); + } + } else if (EnderSignal.class.isAssignableFrom(clazz)) { + entity = new EntityEnderSignal(world, x, y, z); + } else if (EnderCrystal.class.isAssignableFrom(clazz)) { + entity = new EntityEnderCrystal(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (LivingEntity.class.isAssignableFrom(clazz)) { + if (Chicken.class.isAssignableFrom(clazz)) { + entity = new EntityChicken(world); + } else if (Cow.class.isAssignableFrom(clazz)) { + if (MushroomCow.class.isAssignableFrom(clazz)) { + entity = new EntityMushroomCow(world); + } else { + entity = new EntityCow(world); + } + } else if (Golem.class.isAssignableFrom(clazz)) { + if (Snowman.class.isAssignableFrom(clazz)) { + entity = new EntitySnowman(world); + } else if (IronGolem.class.isAssignableFrom(clazz)) { + entity = new EntityIronGolem(world); + } + } else if (Creeper.class.isAssignableFrom(clazz)) { + entity = new EntityCreeper(world); + } else if (Ghast.class.isAssignableFrom(clazz)) { + entity = new EntityGhast(world); + } else if (Pig.class.isAssignableFrom(clazz)) { + entity = new EntityPig(world); + } else if (Player.class.isAssignableFrom(clazz)) { + // need a net server handler for this one + } else if (Sheep.class.isAssignableFrom(clazz)) { + entity = new EntitySheep(world); + } else if (Horse.class.isAssignableFrom(clazz)) { + entity = new EntityHorse(world); + } else if (Skeleton.class.isAssignableFrom(clazz)) { + entity = new EntitySkeleton(world); + } else if (Slime.class.isAssignableFrom(clazz)) { + if (MagmaCube.class.isAssignableFrom(clazz)) { + entity = new EntityMagmaCube(world); + } else { + entity = new EntitySlime(world); + } + } else if (Spider.class.isAssignableFrom(clazz)) { + if (CaveSpider.class.isAssignableFrom(clazz)) { + entity = new EntityCaveSpider(world); + } else { + entity = new EntitySpider(world); + } + } else if (Squid.class.isAssignableFrom(clazz)) { + entity = new EntitySquid(world); + } else if (Tameable.class.isAssignableFrom(clazz)) { + if (Wolf.class.isAssignableFrom(clazz)) { + entity = new EntityWolf(world); + } else if (Ocelot.class.isAssignableFrom(clazz)) { + entity = new EntityOcelot(world); + } + } else if (PigZombie.class.isAssignableFrom(clazz)) { + entity = new EntityPigZombie(world); + } else if (Zombie.class.isAssignableFrom(clazz)) { + entity = new EntityZombie(world); + } else if (Giant.class.isAssignableFrom(clazz)) { + entity = new EntityGiantZombie(world); + } else if (Silverfish.class.isAssignableFrom(clazz)) { + entity = new EntitySilverfish(world); + } else if (Enderman.class.isAssignableFrom(clazz)) { + entity = new EntityEnderman(world); + } else if (Blaze.class.isAssignableFrom(clazz)) { + entity = new EntityBlaze(world); + } else if (Villager.class.isAssignableFrom(clazz)) { + entity = new EntityVillager(world); + } else if (Witch.class.isAssignableFrom(clazz)) { + entity = new EntityWitch(world); + } else if (Wither.class.isAssignableFrom(clazz)) { + entity = new EntityWither(world); + } else if (ComplexLivingEntity.class.isAssignableFrom(clazz)) { + if (EnderDragon.class.isAssignableFrom(clazz)) { + entity = new EntityEnderDragon(world); + } + } else if (Ambient.class.isAssignableFrom(clazz)) { + if (Bat.class.isAssignableFrom(clazz)) { + entity = new EntityBat(world); + } + } + + if (entity != null) { + entity.setLocation(x, y, z, yaw, pitch); + } + } else if (Hanging.class.isAssignableFrom(clazz)) { + Block block = getBlockAt(location); + BlockFace face = BlockFace.SELF; + if (block.getRelative(BlockFace.EAST).getTypeId() == 0) { + face = BlockFace.EAST; + } else if (block.getRelative(BlockFace.NORTH).getTypeId() == 0) { + face = BlockFace.NORTH; + } else if (block.getRelative(BlockFace.WEST).getTypeId() == 0) { + face = BlockFace.WEST; + } else if (block.getRelative(BlockFace.SOUTH).getTypeId() == 0) { + face = BlockFace.SOUTH; + } + int dir; + switch (face) { + case SOUTH: + default: + dir = 0; + break; + case WEST: + dir = 1; + break; + case NORTH: + dir = 2; + break; + case EAST: + dir = 3; + break; + } + + if (Painting.class.isAssignableFrom(clazz)) { + entity = new EntityPainting(world, (int) x, (int) y, (int) z, dir); + } else if (ItemFrame.class.isAssignableFrom(clazz)) { + entity = new EntityItemFrame(world, (int) x, (int) y, (int) z, dir); + } else if (LeashHitch.class.isAssignableFrom(clazz)) { + entity = new EntityLeash(world, (int) x, (int) y, (int) z); + entity.attachedToPlayer = true; + } + + if (entity != null && !((EntityHanging) entity).survives()) { + throw new IllegalArgumentException("Cannot spawn hanging entity for " + clazz.getName() + " at " + location); + } + } else if (TNTPrimed.class.isAssignableFrom(clazz)) { + // PaperSpigot start - Add FallingBlock and TNT source location API + org.bukkit.Location loc = new org.bukkit.Location(world.getWorld(), x, y, z); + entity = new EntityTNTPrimed(loc, world, x, y, z, null); + // PaperSpigot end + } else if (ExperienceOrb.class.isAssignableFrom(clazz)) { + entity = new EntityExperienceOrb(world, x, y, z, 0); + } else if (Weather.class.isAssignableFrom(clazz)) { + // not sure what this can do + if (LightningStrike.class.isAssignableFrom(clazz)) { + entity = new EntityLightning(world, x, y, z); + // what is this, I don't even + } + } else if (Firework.class.isAssignableFrom(clazz)) { + entity = new EntityFireworks(world, x, y, z, null); + } + + if (entity != null) { + // Spigot start + if (entity instanceof EntityOcelot) + { + ( (EntityOcelot) entity ).spawnBonus = false; + } + // Spigot end + if (entity instanceof EntityInsentient) { + ((EntityInsentient) entity).prepare((GroupDataEntity) null); + } + + world.addEntity(entity, reason); + return (T) entity.getBukkitEntity(); + } + + throw new IllegalArgumentException("Cannot spawn an entity for " + clazz.getName()); + } + + public ChunkSnapshot getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain) { + return CraftChunk.getEmptyChunkSnapshot(x, z, this, includeBiome, includeBiomeTempRain); + } + + public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals) { + world.setSpawnFlags(allowMonsters, allowAnimals); + } + + public boolean getAllowAnimals() { + return world.allowAnimals; + } + + public boolean getAllowMonsters() { + return world.allowMonsters; + } + + public int getMaxHeight() { + return world.getHeight(); + } + + public int getSeaLevel() { + return 64; + } + + public boolean getKeepSpawnInMemory() { + return world.keepSpawnInMemory; + } + + public void setKeepSpawnInMemory(boolean keepLoaded) { + world.keepSpawnInMemory = keepLoaded; + // Grab the worlds spawn chunk + ChunkCoordinates chunkcoordinates = this.world.getSpawn(); + int chunkCoordX = chunkcoordinates.x >> 4; + int chunkCoordZ = chunkcoordinates.z >> 4; + // Cycle through the 25x25 Chunks around it to load/unload the chunks. + for (int x = -12; x <= 12; x++) { + for (int z = -12; z <= 12; z++) { + if (keepLoaded) { + loadChunk(chunkCoordX + x, chunkCoordZ + z); + } else { + if (isChunkLoaded(chunkCoordX + x, chunkCoordZ + z)) { + if (this.getHandle().getChunkAt(chunkCoordX + x, chunkCoordZ + z) instanceof EmptyChunk) { + unloadChunk(chunkCoordX + x, chunkCoordZ + z, false); + } else { + unloadChunk(chunkCoordX + x, chunkCoordZ + z); + } + } + } + } + } + } + + @Override + public int hashCode() { + return getUID().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + final CraftWorld other = (CraftWorld) obj; + + return this.getUID() == other.getUID(); + } + + public File getWorldFolder() { + return ((WorldNBTStorage) world.getDataManager()).getDirectory(); + } + + public void sendPluginMessage(Plugin source, String channel, byte[] message) { + StandardMessenger.validatePluginMessage(server.getMessenger(), source, channel, message); + + for (Player player : getPlayers()) { + player.sendPluginMessage(source, channel, message); + } + } + + public Set getListeningPluginChannels() { + Set result = new HashSet(); + + for (Player player : getPlayers()) { + result.addAll(player.getListeningPluginChannels()); + } + + return result; + } + + public org.bukkit.WorldType getWorldType() { + return org.bukkit.WorldType.getByName(world.getWorldData().getType().name()); + } + + public boolean canGenerateStructures() { + return world.getWorldData().shouldGenerateMapFeatures(); + } + + public long getTicksPerAnimalSpawns() { + return world.ticksPerAnimalSpawns; + } + + public void setTicksPerAnimalSpawns(int ticksPerAnimalSpawns) { + world.ticksPerAnimalSpawns = ticksPerAnimalSpawns; + } + + public long getTicksPerMonsterSpawns() { + return world.ticksPerMonsterSpawns; + } + + public void setTicksPerMonsterSpawns(int ticksPerMonsterSpawns) { + world.ticksPerMonsterSpawns = ticksPerMonsterSpawns; + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + server.getWorldMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + return server.getWorldMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return server.getWorldMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + server.getWorldMetadata().removeMetadata(this, metadataKey, owningPlugin); + } + + public int getMonsterSpawnLimit() { + if (monsterSpawn < 0) { + return server.getMonsterSpawnLimit(); + } + + return monsterSpawn; + } + + public void setMonsterSpawnLimit(int limit) { + monsterSpawn = limit; + } + + public int getAnimalSpawnLimit() { + if (animalSpawn < 0) { + return server.getAnimalSpawnLimit(); + } + + return animalSpawn; + } + + public void setAnimalSpawnLimit(int limit) { + animalSpawn = limit; + } + + public int getWaterAnimalSpawnLimit() { + if (waterAnimalSpawn < 0) { + return server.getWaterAnimalSpawnLimit(); + } + + return waterAnimalSpawn; + } + + public void setWaterAnimalSpawnLimit(int limit) { + waterAnimalSpawn = limit; + } + + public int getAmbientSpawnLimit() { + if (ambientSpawn < 0) { + return server.getAmbientSpawnLimit(); + } + + return ambientSpawn; + } + + public void setAmbientSpawnLimit(int limit) { + ambientSpawn = limit; + } + + + public void playSound(Location loc, Sound sound, float volume, float pitch) { + if (loc == null || sound == null) return; + + double x = loc.getX(); + double y = loc.getY(); + double z = loc.getZ(); + + getHandle().makeSound(x, y, z, CraftSound.getSound(sound), volume, pitch); + } + + public String getGameRuleValue(String rule) { + return getHandle().getGameRules().get(rule); + } + + public boolean setGameRuleValue(String rule, String value) { + // No null values allowed + if (rule == null || value == null) return false; + + if (!isGameRule(rule)) return false; + + getHandle().getGameRules().set(rule, value); + return true; + } + + public String[] getGameRules() { + return getHandle().getGameRules().getGameRules(); + } + + public boolean isGameRule(String rule) { + return getHandle().getGameRules().contains(rule); + } + + public void processChunkGC() { + chunkGCTickCount++; + + if (chunkLoadCount >= server.chunkGCLoadThresh && server.chunkGCLoadThresh > 0) { + chunkLoadCount = 0; + } else if (chunkGCTickCount >= server.chunkGCPeriod && server.chunkGCPeriod > 0) { + chunkGCTickCount = 0; + } else { + return; + } + + ChunkProviderServer cps = world.chunkProviderServer; + for (net.minecraft.server.Chunk chunk : cps.chunks.values()) { + // If in use, skip it + if (isChunkInUse(chunk.locX, chunk.locZ)) { + continue; + } + + // Already unloading? + if (cps.unloadQueue.contains(chunk.locX, chunk.locZ)) { + continue; + } + + // Add unload request + cps.queueUnload(chunk.locX, chunk.locZ); + } + } + // Spigot start + private final Spigot spigot = new Spigot() + { + @Override + public void playEffect( Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius ) + { + Validate.notNull( location, "Location cannot be null" ); + Validate.notNull( effect, "Effect cannot be null" ); + Validate.notNull( location.getWorld(), "World cannot be null" ); + Packet packet; + if ( effect.getType() != Effect.Type.PARTICLE ) + { + int packetData = effect.getId(); + packet = new PacketPlayOutWorldEvent( packetData, location.getBlockX(), location.getBlockY(), location.getBlockZ(), id, false ); + } else + { + StringBuilder particleFullName = new StringBuilder(); + particleFullName.append( effect.getName() ); + if ( effect.getData() != null && ( effect.getData().equals( org.bukkit.Material.class ) || effect.getData().equals( org.bukkit.material.MaterialData.class ) ) ) + { + particleFullName.append( '_' ).append( id ); + } + if ( effect.getData() != null && effect.getData().equals( org.bukkit.material.MaterialData.class ) ) + { + particleFullName.append( '_' ).append( data ); + } + packet = new PacketPlayOutWorldParticles( particleFullName.toString(), (float) location.getX(), (float) location.getY(), (float) location.getZ(), offsetX, offsetY, offsetZ, speed, particleCount ); + } + int distance; + radius *= radius; + for ( Player player : getPlayers() ) + { + if ( ( (CraftPlayer) player ).getHandle().playerConnection == null ) + { + continue; + } + if ( !location.getWorld().equals( player.getWorld() ) ) + { + continue; + } + distance = (int) player.getLocation().distanceSquared( location ); + if ( distance <= radius ) + { + ( (CraftPlayer) player ).getHandle().playerConnection.sendPacket( packet ); + } + } + } + + @Override + public void playEffect( Location location, Effect effect ) + { + CraftWorld.this.playEffect( location, effect, 0 ); + } + + @Override + public LightningStrike strikeLightning(Location loc, boolean isSilent) + { + EntityLightning lightning = new EntityLightning( world, loc.getX(), loc.getY(), loc.getZ(), false, isSilent ); + world.strikeLightning( lightning ); + return new CraftLightningStrike( server, lightning ); + } + + @Override + public LightningStrike strikeLightningEffect(Location loc, boolean isSilent) + { + EntityLightning lightning = new EntityLightning( world, loc.getX(), loc.getY(), loc.getZ(), true, isSilent ); + world.strikeLightning( lightning ); + return new CraftLightningStrike( server, lightning ); + } + }; + + public Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/LoggerOutputStream.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/LoggerOutputStream.java new file mode 100644 index 0000000..93526ab --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/LoggerOutputStream.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; + +public class LoggerOutputStream extends ByteArrayOutputStream { + private final String separator = System.getProperty("line.separator"); + private final Logger logger; + private final Level level; + + public LoggerOutputStream(Logger logger, Level level) { + super(); + this.logger = logger; + this.level = level; + } + + @Override + public void flush() throws IOException { + synchronized (this) { + super.flush(); + String record = this.toString(); + super.reset(); + + if ((record.length() > 0) && (!record.equals(separator))) { + logger.log(level, record); + } + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/Main.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/Main.java new file mode 100644 index 0000000..57fcf6b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/Main.java @@ -0,0 +1,193 @@ +package org.bukkit.craftbukkit; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import net.minecraft.server.MinecraftServer; + +public class Main { + public static boolean useJline = true; + public static boolean useConsole = true; + + public static void main(String[] args) throws Exception { + // Todo: Installation script + OptionParser parser = new OptionParser() { + { + acceptsAll(asList("?", "help"), "Show the help"); + + acceptsAll(asList("c", "config"), "Properties file to use") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("config/server", "server.properties")) // MineHQ - Dedicated config directory + .describedAs("Properties file"); + + acceptsAll(asList("P", "plugins"), "Plugin directory to use") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("plugins")) + .describedAs("Plugin directory"); + + acceptsAll(asList("h", "host", "server-ip"), "Host to listen on") + .withRequiredArg() + .ofType(String.class) + .describedAs("Hostname or IP"); + + acceptsAll(asList("W", "world-dir", "universe", "world-container"), "World container") + .withRequiredArg() + .ofType(File.class) + .describedAs("Directory containing worlds"); + + acceptsAll(asList("w", "world", "level-name"), "World name") + .withRequiredArg() + .ofType(String.class) + .describedAs("World name"); + + acceptsAll(asList("p", "port", "server-port"), "Port to listen on") + .withRequiredArg() + .ofType(Integer.class) + .describedAs("Port"); + + acceptsAll(asList("o", "online-mode"), "Whether to use online authentication") + .withRequiredArg() + .ofType(Boolean.class) + .describedAs("Authentication"); + + acceptsAll(asList("s", "size", "max-players"), "Maximum amount of players") + .withRequiredArg() + .ofType(Integer.class) + .describedAs("Server size"); + + acceptsAll(asList("d", "date-format"), "Format of the date to display in the console (for log entries)") + .withRequiredArg() + .ofType(SimpleDateFormat.class) + .describedAs("Log date format"); + + acceptsAll(asList("log-pattern"), "Specfies the log filename pattern") + .withRequiredArg() + .ofType(String.class) + .defaultsTo("server.log") + .describedAs("Log filename"); + + acceptsAll(asList("log-limit"), "Limits the maximum size of the log file (0 = unlimited)") + .withRequiredArg() + .ofType(Integer.class) + .defaultsTo(0) + .describedAs("Max log size"); + + acceptsAll(asList("log-count"), "Specified how many log files to cycle through") + .withRequiredArg() + .ofType(Integer.class) + .defaultsTo(1) + .describedAs("Log count"); + + acceptsAll(asList("log-append"), "Whether to append to the log file") + .withRequiredArg() + .ofType(Boolean.class) + .defaultsTo(true) + .describedAs("Log append"); + + acceptsAll(asList("log-strip-color"), "Strips color codes from log file"); + + acceptsAll(asList("b", "bukkit-settings"), "File for bukkit settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("config/server", "bukkit.yml")) // MineHQ - Dedicated config directory + .describedAs("Yml file"); + + acceptsAll(asList("C", "commands-settings"), "File for command settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("config/server", "commands.yml")) // MineHQ - Dedicated config directory + .describedAs("Yml file"); + + acceptsAll(asList("nojline"), "Disables jline and emulates the vanilla console"); + + acceptsAll(asList("noconsole"), "Disables the console"); + + acceptsAll(asList("v", "version"), "Show the CraftBukkit Version"); + + acceptsAll(asList("demo"), "Demo mode"); + } + }; + + OptionSet options = null; + + try { + options = parser.parse(args); + } catch (joptsimple.OptionException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage()); + } + + if ((options == null) || (options.has("?"))) { + try { + parser.printHelpOn(System.out); + } catch (IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } else if (options.has("v")) { + System.out.println(CraftServer.class.getPackage().getImplementationVersion()); + } + + // Spigot end + + try { + // This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals + String jline_UnsupportedTerminal = new String(new char[] {'j','l','i','n','e','.','U','n','s','u','p','p','o','r','t','e','d','T','e','r','m','i','n','a','l'}); + String jline_terminal = new String(new char[] {'j','l','i','n','e','.','t','e','r','m','i','n','a','l'}); + + useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal)); + + if (options.has("nojline")) { + System.setProperty("user.language", "en"); + useJline = false; + } + + if (!useJline) { + // This ensures the terminal literal will always match the jline implementation + System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName()); + } + + + if (options.has("noconsole")) { + useConsole = false; + } + + // Spigot Start + int maxPermGen = 0; // In kb + for ( String s : java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments() ) + { + if ( s.startsWith( "-XX:MaxPermSize" ) ) + { + maxPermGen = Integer.parseInt( s.replaceAll( "[^\\d]", "" ) ); + maxPermGen <<= 10 * ("kmg".indexOf( Character.toLowerCase( s.charAt( s.length() - 1 ) ) ) ); + } + } + if ( Float.parseFloat( System.getProperty( "java.class.version" ) ) < 52 && maxPermGen < ( 128 << 10 ) ) // 128mb + { + System.out.println( "Warning, your max perm gen size is not set or less than 128mb. It is recommended you restart Java with the following argument: -XX:MaxPermSize=128M" ); + System.out.println( "Please see http://www.spigotmc.org/wiki/changing-permgen-size/ for more details and more in-depth instructions." ); + } + // Spigot End + MinecraftServer.main(options); + } catch (Throwable t) { + t.printStackTrace(); + System.exit(1); // SportBukkit + } + } + + private static List asList(String... params) { + return Arrays.asList(params); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/Overridden.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/Overridden.java new file mode 100644 index 0000000..1c19c69 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/Overridden.java @@ -0,0 +1,14 @@ +package org.bukkit.craftbukkit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a method needs to be overridden in sub classes + */ +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Overridden { +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/SpigotTimings.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/SpigotTimings.java new file mode 100644 index 0000000..10e05bd --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/SpigotTimings.java @@ -0,0 +1,215 @@ +package org.bukkit.craftbukkit; + +import com.google.common.collect.Maps; +import net.minecraft.server.*; +import org.bukkit.plugin.java.JavaPluginLoader; +import org.spigotmc.CustomTimingsHandler; +import org.bukkit.scheduler.BukkitTask; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.craftbukkit.scheduler.CraftTask; + +public class SpigotTimings { + + public static final CustomTimingsHandler serverTickTimer = new CustomTimingsHandler("** Full Server Tick"); + public static final CustomTimingsHandler playerListTimer = new CustomTimingsHandler("Player List"); + public static final CustomTimingsHandler connectionTimer = new CustomTimingsHandler("Connection Handler"); + public static final CustomTimingsHandler tickablesTimer = new CustomTimingsHandler("Tickables"); + public static final CustomTimingsHandler schedulerTimer = new CustomTimingsHandler("Scheduler"); + public static final CustomTimingsHandler chunkIOTickTimer = new CustomTimingsHandler("ChunkIOTick"); + public static final CustomTimingsHandler timeUpdateTimer = new CustomTimingsHandler("Time Update"); + public static final CustomTimingsHandler serverCommandTimer = new CustomTimingsHandler("Server Command"); + public static final CustomTimingsHandler worldSaveTimer = new CustomTimingsHandler("World Save"); + + public static final CustomTimingsHandler entityMoveTimer = new CustomTimingsHandler("** entityMove"); + public static final CustomTimingsHandler tickEntityTimer = new CustomTimingsHandler("** tickEntity"); + public static final CustomTimingsHandler activatedEntityTimer = new CustomTimingsHandler("** activatedTickEntity"); + public static final CustomTimingsHandler tickTileEntityTimer = new CustomTimingsHandler("** tickTileEntity"); + + public static final CustomTimingsHandler timerEntityBaseTick = new CustomTimingsHandler("** livingEntityBaseTick"); + public static final CustomTimingsHandler timerEntityAI = new CustomTimingsHandler("** livingEntityAI"); + public static final CustomTimingsHandler timerEntityAICollision = new CustomTimingsHandler("** livingEntityAICollision"); + public static final CustomTimingsHandler timerEntityAIMove = new CustomTimingsHandler("** livingEntityAIMove"); + public static final CustomTimingsHandler timerEntityTickRest = new CustomTimingsHandler("** livingEntityTickRest"); + + public static final CustomTimingsHandler processQueueTimer = new CustomTimingsHandler("processQueue"); + public static final CustomTimingsHandler schedulerSyncTimer = new CustomTimingsHandler("** Scheduler - Sync Tasks", JavaPluginLoader.pluginParentTimer); + + public static final CustomTimingsHandler playerCommandTimer = new CustomTimingsHandler("** playerCommand"); + + public static final CustomTimingsHandler entityActivationCheckTimer = new CustomTimingsHandler("entityActivationCheck"); + public static final CustomTimingsHandler checkIfActiveTimer = new CustomTimingsHandler("** checkIfActive"); + + // Poweruser start + public static final HashMap packetHandlerTimingMap = new HashMap(); + public static final CustomTimingsHandler timerEntity_C = new CustomTimingsHandler("** livingEntityBaseTick_Entity_C()"); + public static final CustomTimingsHandler timerEntityInsentient_C = new CustomTimingsHandler("** livingEntityBaseTick_EntityInsentient_C()"); + public static final CustomTimingsHandler timerEntityLiving_C = new CustomTimingsHandler("** livingEntityBaseTick_EntityLiving_C()"); + public static final CustomTimingsHandler timerEntity_C_portal = new CustomTimingsHandler("** livingEntityBaseTick_Entity_C()_portal"); + public static final CustomTimingsHandler connectionTimer_PacketFlying_move = new CustomTimingsHandler("** Connection Handler_PacketFlying_move"); + public static final CustomTimingsHandler connectionTimer_PacketFlying_playerChunks = new CustomTimingsHandler("** Connection Handler_PacketFlying_airCheck"); + + public static CustomTimingsHandler getPacketHandlerTimings(Packet packet) { + String packetType = packet.getClass().getName(); + CustomTimingsHandler result = packetHandlerTimingMap.get(packetType); + if (result == null) { + result = new CustomTimingsHandler("** Connection Handler - " + packetType, connectionTimer); + packetHandlerTimingMap.put(packetType, result); + } + return result; + } + // Poweruser end + + public static final HashMap entityTypeTimingMap = new HashMap(); + public static final HashMap tileEntityTypeTimingMap = new HashMap(); + public static final HashMap pluginTaskTimingMap = new HashMap(); + + /** + * Gets a timer associated with a plugins tasks. + * @param task + * @param period + * @return + */ + public static CustomTimingsHandler getPluginTaskTimings(BukkitTask task, long period) { + if (!task.isSync()) { + return null; + } + String plugin; + final CraftTask ctask = (CraftTask) task; + + if (task.getOwner() != null) { + plugin = task.getOwner().getDescription().getFullName(); + } else if (ctask.timingName != null) { + plugin = "CraftScheduler"; + } else { + plugin = "Unknown"; + } + String taskname = ctask.getTaskName(); + + String name = "Task: " + plugin + " Runnable: " + taskname; + if (period > 0) { + name += "(interval:" + period +")"; + } else { + name += "(Single)"; + } + CustomTimingsHandler result = pluginTaskTimingMap.get(name); + if (result == null) { + result = new CustomTimingsHandler(name, SpigotTimings.schedulerSyncTimer); + pluginTaskTimingMap.put(name, result); + } + return result; + } + + /** + * Get a named timer for the specified entity type to track type specific timings. + * @param entity + * @return + */ + public static CustomTimingsHandler getEntityTimings(Entity entity) { + String entityType = entity.getClass().getName(); + CustomTimingsHandler result = entityTypeTimingMap.get(entityType); + if (result == null) { + result = new CustomTimingsHandler("** tickEntity - " + entityType, activatedEntityTimer); + entityTypeTimingMap.put(entityType, result); + } + return result; + } + + /** + * Get a named timer for the specified tile entity type to track type specific timings. + * @param entity + * @return + */ + public static CustomTimingsHandler getTileEntityTimings(TileEntity entity) { + String entityType = entity.getClass().getName(); + CustomTimingsHandler result = tileEntityTypeTimingMap.get(entityType); + if (result == null) { + result = new CustomTimingsHandler("** tickTileEntity - " + entityType, tickTileEntityTimer); + tileEntityTypeTimingMap.put(entityType, result); + } + return result; + } + + /** + * Set of timers per world, to track world specific timings. + */ + public static class WorldTimingsHandler { + public final CustomTimingsHandler mobSpawn; + public final CustomTimingsHandler doChunkUnload; + public final CustomTimingsHandler doChunkUnloadSave; + public final CustomTimingsHandler doPortalForcer; + public final CustomTimingsHandler doTickPending; + public final CustomTimingsHandler doTickTiles; + public final CustomTimingsHandler doTickTiles_buildList; + public final CustomTimingsHandler doTickTiles_tickingChunks; + public final CustomTimingsHandler doTickTiles_tickingChunks_getChunk; + public final CustomTimingsHandler doTickTiles_tickingChunks_tickChunk; + public final CustomTimingsHandler doTickTiles_tickingChunks_iceAndSnow; + public final CustomTimingsHandler doTickTiles_tickingChunks_tickBlocks; + public final CustomTimingsHandler doVillages; + public final CustomTimingsHandler doChunkMap; + public final CustomTimingsHandler doChunkGC; + public final CustomTimingsHandler doSounds; + public final CustomTimingsHandler entityTick; + public final CustomTimingsHandler tileEntityTick; + public final CustomTimingsHandler tileEntityPending; + public final CustomTimingsHandler tracker; + public final CustomTimingsHandler doTick; + public final CustomTimingsHandler tickEntities; + // Poweruser start + public final CustomTimingsHandler entityPlayerTickNormal; + public final CustomTimingsHandler entityPlayerTickOnMove; + // Poweruser end + + public final CustomTimingsHandler syncChunkLoadTimer; + public final CustomTimingsHandler syncChunkLoadDataTimer; + public final CustomTimingsHandler syncChunkLoadStructuresTimer; + public final CustomTimingsHandler syncChunkLoadEntitiesTimer; + public final CustomTimingsHandler syncChunkLoadTileEntitiesTimer; + public final CustomTimingsHandler syncChunkLoadTileTicksTimer; + public final CustomTimingsHandler syncChunkLoadPostTimer; + + public WorldTimingsHandler(World server) { + String name = server.worldData.getName() +" - "; + + mobSpawn = new CustomTimingsHandler("** " + name + "mobSpawn"); + doChunkUnload = new CustomTimingsHandler("** " + name + "doChunkUnload"); + doChunkUnloadSave = new CustomTimingsHandler("** " + name + "doChunkUnload_save"); + doTickPending = new CustomTimingsHandler("** " + name + "doTickPending"); + doTickTiles = new CustomTimingsHandler("** " + name + "doTickTiles"); + doTickTiles_buildList = new CustomTimingsHandler("** " + name + "doTickTiles_buildList"); + doTickTiles_tickingChunks = new CustomTimingsHandler("** " + name + "doTickTiles_tickingChunks"); + doTickTiles_tickingChunks_getChunk = new CustomTimingsHandler("** " + name + "doTickTiles_tickingChunks_getChunk"); + doTickTiles_tickingChunks_tickChunk = new CustomTimingsHandler("** " + name + "doTickTiles_tickingChunks_tickChunk"); + doTickTiles_tickingChunks_iceAndSnow = new CustomTimingsHandler("** " + name + "doTickTiles_tickingChunks_iceAndSnow"); + doTickTiles_tickingChunks_tickBlocks = new CustomTimingsHandler("** " + name + "doTickTiles_tickingChunks_tickBlocks"); + doVillages = new CustomTimingsHandler("** " + name + "doVillages"); + doChunkMap = new CustomTimingsHandler("** " + name + "doChunkMap"); + doSounds = new CustomTimingsHandler("** " + name + "doSounds"); + doChunkGC = new CustomTimingsHandler("** " + name + "doChunkGC"); + doPortalForcer = new CustomTimingsHandler("** " + name + "doPortalForcer"); + entityTick = new CustomTimingsHandler("** " + name + "entityTick"); + tileEntityTick = new CustomTimingsHandler("** " + name + "tileEntityTick"); + tileEntityPending = new CustomTimingsHandler("** " + name + "tileEntityPending"); + // Poweruser start + entityPlayerTickNormal = new CustomTimingsHandler("** " + name + "entityPlayerTick_normal"); + entityPlayerTickOnMove = new CustomTimingsHandler("** " + name + "entityPlayerTick_onMove"); + // Poweruser end + + syncChunkLoadTimer = new CustomTimingsHandler("** " + name + "syncChunkLoad"); + syncChunkLoadDataTimer = new CustomTimingsHandler("** " + name + "syncChunkLoad - Data"); + syncChunkLoadStructuresTimer = new CustomTimingsHandler("** " + name + "chunkLoad - Structures"); + syncChunkLoadEntitiesTimer = new CustomTimingsHandler("** " + name + "chunkLoad - Entities"); + syncChunkLoadTileEntitiesTimer = new CustomTimingsHandler("** " + name + "chunkLoad - TileEntities"); + syncChunkLoadTileTicksTimer = new CustomTimingsHandler("** " + name + "chunkLoad - TileTicks"); + syncChunkLoadPostTimer = new CustomTimingsHandler("** " + name + "chunkLoad - Post"); + + + tracker = new CustomTimingsHandler(name + "tracker"); + doTick = new CustomTimingsHandler(name + "doTick"); + tickEntities = new CustomTimingsHandler(name + "tickEntities"); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/TrigMath.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/TrigMath.java new file mode 100644 index 0000000..6d613c5 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/TrigMath.java @@ -0,0 +1,47 @@ +package org.bukkit.craftbukkit; + +/** + * Credits for this class goes to user aioobe on stackoverflow.com + * Source: http://stackoverflow.com/questions/4454630/j2me-calculate-the-the-distance-between-2-latitude-and-longitude + */ +public class TrigMath { + + static final double sq2p1 = 2.414213562373095048802e0; + static final double sq2m1 = .414213562373095048802e0; + static final double p4 = .161536412982230228262e2; + static final double p3 = .26842548195503973794141e3; + static final double p2 = .11530293515404850115428136e4; + static final double p1 = .178040631643319697105464587e4; + static final double p0 = .89678597403663861959987488e3; + static final double q4 = .5895697050844462222791e2; + static final double q3 = .536265374031215315104235e3; + static final double q2 = .16667838148816337184521798e4; + static final double q1 = .207933497444540981287275926e4; + static final double q0 = .89678597403663861962481162e3; + static final double PIO2 = 1.5707963267948966135E0; + + private static double mxatan(double arg) { + double argsq = arg * arg, value; + + value = ((((p4 * argsq + p3) * argsq + p2) * argsq + p1) * argsq + p0); + value = value / (((((argsq + q4) * argsq + q3) * argsq + q2) * argsq + q1) * argsq + q0); + return value * arg; + } + + private static double msatan(double arg) { + return arg < sq2m1 ? mxatan(arg) + : arg > sq2p1 ? PIO2 - mxatan(1 / arg) + : PIO2 / 2 + mxatan((arg - 1) / (arg + 1)); + } + + public static double atan(double arg) { + return arg > 0 ? msatan(arg) : -msatan(-arg); + } + + public static double atan2(double arg1, double arg2) { + if (arg1 + arg2 == arg1) + return arg1 >= 0 ? PIO2 : -PIO2; + arg1 = atan(arg1 / arg2); + return arg2 < 0 ? arg1 <= 0 ? arg1 + Math.PI : arg1 - Math.PI : arg1; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java new file mode 100644 index 0000000..fc98463 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java @@ -0,0 +1,37 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityBeacon; + +import org.bukkit.block.Block; +import org.bukkit.block.Beacon; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventoryBeacon; +import org.bukkit.inventory.Inventory; + +public class CraftBeacon extends CraftBlockState implements Beacon { + private final CraftWorld world; + private final TileEntityBeacon beacon; + + public CraftBeacon(final Block block) { + super(block); + + world = (CraftWorld) block.getWorld(); + beacon = (TileEntityBeacon) world.getTileEntityAt(getX(), getY(), getZ()); + } + + public Inventory getInventory() { + return new CraftInventoryBeacon(beacon); + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + beacon.update(); + } + + return result; + } +} + diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java new file mode 100644 index 0000000..87d79ae --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java @@ -0,0 +1,554 @@ +package org.bukkit.craftbukkit.block; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import net.minecraft.server.BiomeBase; +import net.minecraft.server.BlockCocoa; +import net.minecraft.server.BlockRedstoneWire; +import net.minecraft.server.Blocks; +import net.minecraft.server.EnumSkyBlock; +import net.minecraft.server.GameProfileSerializer; +import net.minecraft.server.Item; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.TileEntitySkull; + +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.PistonMoveReaction; +import org.bukkit.craftbukkit.CraftChunk; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.BlockVector; + +public class CraftBlock implements Block { + private final CraftChunk chunk; + private final int x; + private final int y; + private final int z; + private static final Biome BIOME_MAPPING[]; + private static final BiomeBase BIOMEBASE_MAPPING[]; + + public CraftBlock(CraftChunk chunk, int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + this.chunk = chunk; + } + + private net.minecraft.server.Block getNMSBlock() { + return CraftMagicNumbers.getBlock(this); // TODO: UPDATE THIS + } + + private static net.minecraft.server.Block getNMSBlock(int type) { + return CraftMagicNumbers.getBlock(type); + } + + public World getWorld() { + return chunk.getWorld(); + } + + public Location getLocation() { + return new Location(getWorld(), x, y, z); + } + + public Location getLocation(Location loc) { + if (loc != null) { + loc.setWorld(getWorld()); + loc.setX(x); + loc.setY(y); + loc.setZ(z); + loc.setYaw(0); + loc.setPitch(0); + } + + return loc; + } + + public BlockVector getVector() { + return new BlockVector(x, y, z); + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public Chunk getChunk() { + return chunk; + } + + public void setData(final byte data) { + chunk.getHandle().world.setData(x, y, z, data, 3); + } + + public void setData(final byte data, boolean applyPhysics) { + if (applyPhysics) { + chunk.getHandle().world.setData(x, y, z, data, 3); + } else { + chunk.getHandle().world.setData(x, y, z, data, 2); + } + } + + public byte getData() { + return (byte) chunk.getHandle().getData(this.x & 0xF, this.y & 0xFF, this.z & 0xF); + } + + public void setType(final Material type) { + setTypeId(type.getId()); + } + + public boolean setTypeId(final int type) { + return setTypeId(type, true); + } + + public boolean setTypeId(final int type, final boolean applyPhysics) { + return setTypeIdAndData(type, getData(), applyPhysics); + } + + public boolean setTypeIdAndData(final int type, final byte data, final boolean applyPhysics) { + if (applyPhysics) { + return chunk.getHandle().world.setTypeAndData(x, y, z, getNMSBlock(type), data, 3); + } else { + boolean success = chunk.getHandle().world.setTypeAndData(x, y, z, getNMSBlock(type), data, 2); + if (success) { + chunk.getHandle().world.notify(x, y, z); + } + return success; + } + } + + public Material getType() { + return Material.getMaterial(getTypeId()); + } + + @Deprecated + @Override + public int getTypeId() { + return CraftMagicNumbers.getId(chunk.getHandle().getType(this.x & 0xF, this.y & 0xFF, this.z & 0xF)); + } + + public byte getLightLevel() { + return (byte) chunk.getHandle().world.getLightLevel(this.x, this.y, this.z); + } + + public byte getLightFromSky() { + return (byte) chunk.getHandle().getBrightness(EnumSkyBlock.SKY, this.x & 0xF, this.y & 0xFF, this.z & 0xF); + } + + public byte getLightFromBlocks() { + return (byte) chunk.getHandle().getBrightness(EnumSkyBlock.BLOCK, this.x & 0xF, this.y & 0xFF, this.z & 0xF); + } + + + public Block getFace(final BlockFace face) { + return getRelative(face, 1); + } + + public Block getFace(final BlockFace face, final int distance) { + return getRelative(face, distance); + } + + public Block getRelative(final int modX, final int modY, final int modZ) { + return getWorld().getBlockAt(getX() + modX, getY() + modY, getZ() + modZ); + } + + public Block getRelative(BlockFace face) { + return getRelative(face, 1); + } + + public Block getRelative(BlockFace face, int distance) { + return getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance); + } + + public BlockFace getFace(final Block block) { + BlockFace[] values = BlockFace.values(); + + for (BlockFace face : values) { + if ((this.getX() + face.getModX() == block.getX()) && + (this.getY() + face.getModY() == block.getY()) && + (this.getZ() + face.getModZ() == block.getZ()) + ) { + return face; + } + } + + return null; + } + + @Override + public String toString() { + return "CraftBlock{" + "chunk=" + chunk + ",x=" + x + ",y=" + y + ",z=" + z + ",type=" + getType() + ",data=" + getData() + '}'; + } + + /** + * Notch uses a 0-5 to mean DOWN, UP, NORTH, SOUTH, WEST, EAST + * in that order all over. This method is convenience to convert for us. + * + * @return BlockFace the BlockFace represented by this number + */ + public static BlockFace notchToBlockFace(int notch) { + switch (notch) { + case 0: + return BlockFace.DOWN; + case 1: + return BlockFace.UP; + case 2: + return BlockFace.NORTH; + case 3: + return BlockFace.SOUTH; + case 4: + return BlockFace.WEST; + case 5: + return BlockFace.EAST; + default: + return BlockFace.SELF; + } + } + + public static int blockFaceToNotch(BlockFace face) { + switch (face) { + case DOWN: + return 0; + case UP: + return 1; + case NORTH: + return 2; + case SOUTH: + return 3; + case WEST: + return 4; + case EAST: + return 5; + default: + return 7; // Good as anything here, but technically invalid + } + } + + public BlockState getState() { + Material material = getType(); + + switch (material) { + case SIGN: + case SIGN_POST: + case WALL_SIGN: + return new CraftSign(this); + case CHEST: + case TRAPPED_CHEST: + return new CraftChest(this); + case BURNING_FURNACE: + case FURNACE: + return new CraftFurnace(this); + case DISPENSER: + return new CraftDispenser(this); + case DROPPER: + return new CraftDropper(this); + case HOPPER: + return new CraftHopper(this); + case MOB_SPAWNER: + return new CraftCreatureSpawner(this); + case NOTE_BLOCK: + return new CraftNoteBlock(this); + case JUKEBOX: + return new CraftJukebox(this); + case BREWING_STAND: + return new CraftBrewingStand(this); + case SKULL: + return new CraftSkull(this); + case COMMAND: + return new CraftCommandBlock(this); + case BEACON: + return new CraftBeacon(this); + default: + return new CraftBlockState(this); + } + } + + public Biome getBiome() { + return getWorld().getBiome(x, z); + } + + public void setBiome(Biome bio) { + getWorld().setBiome(x, z, bio); + } + + public static Biome biomeBaseToBiome(BiomeBase base) { + if (base == null) { + return null; + } + + return BIOME_MAPPING[base.id]; + } + + public static BiomeBase biomeToBiomeBase(Biome bio) { + if (bio == null) { + return null; + } + return BIOMEBASE_MAPPING[bio.ordinal()]; + } + + public double getTemperature() { + return getWorld().getTemperature(x, z); + } + + public double getHumidity() { + return getWorld().getHumidity(x, z); + } + + public boolean isBlockPowered() { + return chunk.getHandle().world.getBlockPower(x, y, z) > 0; + } + + public boolean isBlockIndirectlyPowered() { + return chunk.getHandle().world.isBlockIndirectlyPowered(x, y, z); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof CraftBlock)) return false; + CraftBlock other = (CraftBlock) o; + + return this.x == other.x && this.y == other.y && this.z == other.z && this.getWorld().equals(other.getWorld()); + } + + @Override + public int hashCode() { + return this.y << 24 ^ this.x ^ this.z ^ this.getWorld().hashCode(); + } + + public boolean isBlockFacePowered(BlockFace face) { + return chunk.getHandle().world.isBlockFacePowered(x, y, z, blockFaceToNotch(face)); + } + + public boolean isBlockFaceIndirectlyPowered(BlockFace face) { + int power = chunk.getHandle().world.getBlockFacePower(x, y, z, blockFaceToNotch(face)); + + Block relative = getRelative(face); + if (relative.getType() == Material.REDSTONE_WIRE) { + return Math.max(power, relative.getData()) > 0; + } + + return power > 0; + } + + public int getBlockPower(BlockFace face) { + int power = 0; + BlockRedstoneWire wire = Blocks.REDSTONE_WIRE; + net.minecraft.server.World world = chunk.getHandle().world; + if ((face == BlockFace.DOWN || face == BlockFace.SELF) && world.isBlockFacePowered(x, y - 1, z, 0)) power = wire.getPower(world, x, y - 1, z, power); + if ((face == BlockFace.UP || face == BlockFace.SELF) && world.isBlockFacePowered(x, y + 1, z, 1)) power = wire.getPower(world, x, y + 1, z, power); + if ((face == BlockFace.EAST || face == BlockFace.SELF) && world.isBlockFacePowered(x + 1, y, z, 2)) power = wire.getPower(world, x + 1, y, z, power); + if ((face == BlockFace.WEST || face == BlockFace.SELF) && world.isBlockFacePowered(x - 1, y, z, 3)) power = wire.getPower(world, x - 1, y, z, power); + if ((face == BlockFace.NORTH || face == BlockFace.SELF) && world.isBlockFacePowered(x, y, z - 1, 4)) power = wire.getPower(world, x, y, z - 1, power); + if ((face == BlockFace.SOUTH || face == BlockFace.SELF) && world.isBlockFacePowered(x, y, z + 1, 5)) power = wire.getPower(world, x, y, z - 1, power); + return power > 0 ? power : (face == BlockFace.SELF ? isBlockIndirectlyPowered() : isBlockFaceIndirectlyPowered(face)) ? 15 : 0; + } + + public int getBlockPower() { + return getBlockPower(BlockFace.SELF); + } + + public boolean isEmpty() { + return getType() == Material.AIR; + } + + public boolean isLiquid() { + return (getType() == Material.WATER) || (getType() == Material.STATIONARY_WATER) || (getType() == Material.LAVA) || (getType() == Material.STATIONARY_LAVA); + } + + public PistonMoveReaction getPistonMoveReaction() { + return PistonMoveReaction.getById(getNMSBlock().getMaterial().getPushReaction()); + } + + private boolean itemCausesDrops(ItemStack item) { + net.minecraft.server.Block block = this.getNMSBlock(); + net.minecraft.server.Item itemType = item != null ? net.minecraft.server.Item.getById(item.getTypeId()) : null; + return block != null && (block.getMaterial().isAlwaysDestroyable() || (itemType != null && itemType.canDestroySpecialBlock(block))); + } + + public boolean breakNaturally() { + // Order matters here, need to drop before setting to air so skulls can get their data + net.minecraft.server.Block block = this.getNMSBlock(); + byte data = getData(); + boolean result = false; + + if (block != null && block != Blocks.AIR) { + block.dropNaturally(chunk.getHandle().world, x, y, z, data, 1.0F, 0); + result = true; + } + + setTypeId(Material.AIR.getId()); + return result; + } + + public boolean breakNaturally(ItemStack item) { + if (itemCausesDrops(item)) { + return breakNaturally(); + } else { + return setTypeId(Material.AIR.getId()); + } + } + + public Collection getDrops() { + List drops = new ArrayList(); + + net.minecraft.server.Block block = this.getNMSBlock(); + if (block != Blocks.AIR) { + byte data = getData(); + // based on nms.Block.dropNaturally + int count = block.getDropCount(0, chunk.getHandle().world.random); + for (int i = 0; i < count; ++i) { + Item item = block.getDropType(data, chunk.getHandle().world.random, 0); + if (item != null) { + // Skulls are special, their data is based on the tile entity + if (Blocks.SKULL == block) { + net.minecraft.server.ItemStack nmsStack = new net.minecraft.server.ItemStack(item, 1, block.getDropData(chunk.getHandle().world, x, y, z)); + TileEntitySkull tileentityskull = (TileEntitySkull) chunk.getHandle().world.getTileEntity(x, y, z); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getGameProfile() != null) { + nmsStack.setTag(new NBTTagCompound()); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound, tileentityskull.getGameProfile()); + nmsStack.getTag().set("SkullOwner", nbttagcompound); + } + + drops.add(CraftItemStack.asBukkitCopy(nmsStack)); + // We don't want to drop cocoa blocks, we want to drop cocoa beans. + } else if (Blocks.COCOA == block) { + int dropAmount = (BlockCocoa.c(data) >= 2 ? 3 : 1); + for (int j = 0; j < dropAmount; ++j) { + drops.add(new ItemStack(Material.INK_SACK, 1, (short) 3)); + } + } else { + drops.add(new ItemStack(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(item), 1, (short) block.getDropData(data))); + } + } + } + } + return drops; + } + + public Collection getDrops(ItemStack item) { + if (itemCausesDrops(item)) { + return getDrops(); + } else { + return Collections.emptyList(); + } + } + + /* Build biome index based lookup table for BiomeBase to Biome mapping */ + static { + BIOME_MAPPING = new Biome[BiomeBase.getBiomes().length]; + BIOMEBASE_MAPPING = new BiomeBase[Biome.values().length]; + BIOME_MAPPING[BiomeBase.OCEAN.id] = Biome.OCEAN; + BIOME_MAPPING[BiomeBase.PLAINS.id] = Biome.PLAINS; + BIOME_MAPPING[BiomeBase.DESERT.id] = Biome.DESERT; + BIOME_MAPPING[BiomeBase.EXTREME_HILLS.id] = Biome.EXTREME_HILLS; + BIOME_MAPPING[BiomeBase.FOREST.id] = Biome.FOREST; + BIOME_MAPPING[BiomeBase.TAIGA.id] = Biome.TAIGA; + BIOME_MAPPING[BiomeBase.SWAMPLAND.id] = Biome.SWAMPLAND; + BIOME_MAPPING[BiomeBase.RIVER.id] = Biome.RIVER; + BIOME_MAPPING[BiomeBase.HELL.id] = Biome.HELL; + BIOME_MAPPING[BiomeBase.SKY.id] = Biome.SKY; + BIOME_MAPPING[BiomeBase.FROZEN_OCEAN.id] = Biome.FROZEN_OCEAN; + BIOME_MAPPING[BiomeBase.FROZEN_RIVER.id] = Biome.FROZEN_RIVER; + BIOME_MAPPING[BiomeBase.ICE_PLAINS.id] = Biome.ICE_PLAINS; + BIOME_MAPPING[BiomeBase.ICE_MOUNTAINS.id] = Biome.ICE_MOUNTAINS; + BIOME_MAPPING[BiomeBase.MUSHROOM_ISLAND.id] = Biome.MUSHROOM_ISLAND; + BIOME_MAPPING[BiomeBase.MUSHROOM_SHORE.id] = Biome.MUSHROOM_SHORE; + BIOME_MAPPING[BiomeBase.BEACH.id] = Biome.BEACH; + BIOME_MAPPING[BiomeBase.DESERT_HILLS.id] = Biome.DESERT_HILLS; + BIOME_MAPPING[BiomeBase.FOREST_HILLS.id] = Biome.FOREST_HILLS; + BIOME_MAPPING[BiomeBase.TAIGA_HILLS.id] = Biome.TAIGA_HILLS; + BIOME_MAPPING[BiomeBase.SMALL_MOUNTAINS.id] = Biome.SMALL_MOUNTAINS; + BIOME_MAPPING[BiomeBase.JUNGLE.id] = Biome.JUNGLE; + BIOME_MAPPING[BiomeBase.JUNGLE_HILLS.id] = Biome.JUNGLE_HILLS; + BIOME_MAPPING[BiomeBase.JUNGLE_EDGE.id] = Biome.JUNGLE_EDGE; + BIOME_MAPPING[BiomeBase.DEEP_OCEAN.id] = Biome.DEEP_OCEAN; + BIOME_MAPPING[BiomeBase.STONE_BEACH.id] = Biome.STONE_BEACH; + BIOME_MAPPING[BiomeBase.COLD_BEACH.id] = Biome.COLD_BEACH; + BIOME_MAPPING[BiomeBase.BIRCH_FOREST.id] = Biome.BIRCH_FOREST; + BIOME_MAPPING[BiomeBase.BIRCH_FOREST_HILLS.id] = Biome.BIRCH_FOREST_HILLS; + BIOME_MAPPING[BiomeBase.ROOFED_FOREST.id] = Biome.ROOFED_FOREST; + BIOME_MAPPING[BiomeBase.COLD_TAIGA.id] = Biome.COLD_TAIGA; + BIOME_MAPPING[BiomeBase.COLD_TAIGA_HILLS.id] = Biome.COLD_TAIGA_HILLS; + BIOME_MAPPING[BiomeBase.MEGA_TAIGA.id] = Biome.MEGA_TAIGA; + BIOME_MAPPING[BiomeBase.MEGA_TAIGA_HILLS.id] = Biome.MEGA_TAIGA_HILLS; + BIOME_MAPPING[BiomeBase.EXTREME_HILLS_PLUS.id] = Biome.EXTREME_HILLS_PLUS; + BIOME_MAPPING[BiomeBase.SAVANNA.id] = Biome.SAVANNA; + BIOME_MAPPING[BiomeBase.SAVANNA_PLATEAU.id] = Biome.SAVANNA_PLATEAU; + BIOME_MAPPING[BiomeBase.MESA.id] = Biome.MESA; + BIOME_MAPPING[BiomeBase.MESA_PLATEAU_F.id] = Biome.MESA_PLATEAU_FOREST; + BIOME_MAPPING[BiomeBase.MESA_PLATEAU.id] = Biome.MESA_PLATEAU; + + // Extended Biomes + BIOME_MAPPING[BiomeBase.PLAINS.id + 128] = Biome.SUNFLOWER_PLAINS; + BIOME_MAPPING[BiomeBase.DESERT.id + 128] = Biome.DESERT_MOUNTAINS; + BIOME_MAPPING[BiomeBase.FOREST.id + 128] = Biome.FLOWER_FOREST; + BIOME_MAPPING[BiomeBase.TAIGA.id + 128] = Biome.TAIGA_MOUNTAINS; + BIOME_MAPPING[BiomeBase.SWAMPLAND.id + 128] = Biome.SWAMPLAND_MOUNTAINS; + BIOME_MAPPING[BiomeBase.ICE_PLAINS.id + 128] = Biome.ICE_PLAINS_SPIKES; + BIOME_MAPPING[BiomeBase.JUNGLE.id + 128] = Biome.JUNGLE_MOUNTAINS; + BIOME_MAPPING[BiomeBase.JUNGLE_EDGE.id + 128] = Biome.JUNGLE_EDGE_MOUNTAINS; + BIOME_MAPPING[BiomeBase.COLD_TAIGA.id + 128] = Biome.COLD_TAIGA_MOUNTAINS; + BIOME_MAPPING[BiomeBase.SAVANNA.id + 128] = Biome.SAVANNA_MOUNTAINS; + BIOME_MAPPING[BiomeBase.SAVANNA_PLATEAU.id + 128] = Biome.SAVANNA_PLATEAU_MOUNTAINS; + BIOME_MAPPING[BiomeBase.MESA.id + 128] = Biome.MESA_BRYCE; + BIOME_MAPPING[BiomeBase.MESA_PLATEAU_F.id + 128] = Biome.MESA_PLATEAU_FOREST_MOUNTAINS; + BIOME_MAPPING[BiomeBase.MESA_PLATEAU.id + 128] = Biome.MESA_PLATEAU_MOUNTAINS; + BIOME_MAPPING[BiomeBase.BIRCH_FOREST.id + 128] = Biome.BIRCH_FOREST_MOUNTAINS; + BIOME_MAPPING[BiomeBase.BIRCH_FOREST_HILLS.id + 128] = Biome.BIRCH_FOREST_HILLS_MOUNTAINS; + BIOME_MAPPING[BiomeBase.ROOFED_FOREST.id + 128] = Biome.ROOFED_FOREST_MOUNTAINS; + BIOME_MAPPING[BiomeBase.MEGA_TAIGA.id + 128] = Biome.MEGA_SPRUCE_TAIGA; + BIOME_MAPPING[BiomeBase.EXTREME_HILLS.id + 128] = Biome.EXTREME_HILLS_MOUNTAINS; + BIOME_MAPPING[BiomeBase.EXTREME_HILLS_PLUS.id + 128] = Biome.EXTREME_HILLS_PLUS_MOUNTAINS; + BIOME_MAPPING[BiomeBase.MEGA_TAIGA_HILLS.id + 128] = Biome.MEGA_SPRUCE_TAIGA_HILLS; + + /* Sanity check - we should have a record for each record in the BiomeBase.a table */ + /* Helps avoid missed biomes when we upgrade bukkit to new code with new biomes */ + for (int i = 0; i < BIOME_MAPPING.length; i++) { + if ((BiomeBase.getBiome(i) != null) && (BIOME_MAPPING[i] == null)) { + throw new IllegalArgumentException("Missing Biome mapping for BiomeBase[" + i + ", " + BiomeBase.getBiome(i) + "]"); + } + if (BIOME_MAPPING[i] != null) { /* Build reverse mapping for setBiome */ + BIOMEBASE_MAPPING[BIOME_MAPPING[i].ordinal()] = BiomeBase.getBiome(i); + } + } + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + chunk.getCraftWorld().getBlockMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + return chunk.getCraftWorld().getBlockMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return chunk.getCraftWorld().getBlockMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + chunk.getCraftWorld().getBlockMetadata().removeMetadata(this, metadataKey, owningPlugin); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java new file mode 100644 index 0000000..2297cc7 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java @@ -0,0 +1,246 @@ +package org.bukkit.craftbukkit.block; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.CraftChunk; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.material.MaterialData; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; + +import java.util.List; + +public class CraftBlockState implements BlockState { + private final CraftWorld world; + private final CraftChunk chunk; + private final int x; + private final int y; + private final int z; + protected int type; + protected MaterialData data; + protected int flag; + protected final byte light; + + public CraftBlockState(final Block block) { + this.world = (CraftWorld) block.getWorld(); + this.x = block.getX(); + this.y = block.getY(); + this.z = block.getZ(); + this.type = block.getTypeId(); + this.light = block.getLightLevel(); + this.chunk = (CraftChunk) block.getChunk(); + this.flag = 3; + + createData(block.getData()); + } + + public CraftBlockState(final Block block, int flag) { + this(block); + this.flag = flag; + } + + public static CraftBlockState getBlockState(net.minecraft.server.World world, int x, int y, int z) { + return new CraftBlockState(world.getWorld().getBlockAt(x, y, z)); + } + + public static CraftBlockState getBlockState(net.minecraft.server.World world, int x, int y, int z, int flag) { + return new CraftBlockState(world.getWorld().getBlockAt(x, y, z), flag); + } + + public World getWorld() { + return world; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public Chunk getChunk() { + return chunk; + } + + public void setData(final MaterialData data) { + Material mat = getType(); + + if ((mat == null) || (mat.getData() == null)) { + this.data = data; + } else { + if ((data.getClass() == mat.getData()) || (data.getClass() == MaterialData.class)) { + this.data = data; + } else { + throw new IllegalArgumentException("Provided data is not of type " + + mat.getData().getName() + ", found " + data.getClass().getName()); + } + } + } + + public MaterialData getData() { + return data; + } + + public void setType(final Material type) { + setTypeId(type.getId()); + } + + public boolean setTypeId(final int type) { + if (this.type != type) { + this.type = type; + + createData((byte) 0); + } + return true; + } + + public Material getType() { + return Material.getMaterial(getTypeId()); + } + + public void setFlag(int flag) { + this.flag = flag; + } + + public int getFlag() { + return flag; + } + + public int getTypeId() { + return type; + } + + public byte getLightLevel() { + return light; + } + + public Block getBlock() { + return world.getBlockAt(x, y, z); + } + + public boolean update() { + return update(false); + } + + public boolean update(boolean force) { + return update(force, true); + } + + public boolean update(boolean force, boolean applyPhysics) { + Block block = getBlock(); + + if (block.getType() != getType()) { + if (force) { + block.setTypeId(getTypeId(), applyPhysics); + } else { + return false; + } + } + + block.setData(getRawData(), applyPhysics); + world.getHandle().notify(x, y, z); + + return true; + } + + private void createData(final byte data) { + Material mat = getType(); + if (mat == null || mat.getData() == null) { + this.data = new MaterialData(type, data); + } else { + this.data = mat.getNewData(data); + } + } + + public byte getRawData() { + return data.getData(); + } + + public Location getLocation() { + return new Location(world, x, y, z); + } + + public Location getLocation(Location loc) { + if (loc != null) { + loc.setWorld(world); + loc.setX(x); + loc.setY(y); + loc.setZ(z); + loc.setYaw(0); + loc.setPitch(0); + } + + return loc; + } + + public void setRawData(byte data) { + this.data.setData(data); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CraftBlockState other = (CraftBlockState) obj; + if (this.world != other.world && (this.world == null || !this.world.equals(other.world))) { + return false; + } + if (this.x != other.x) { + return false; + } + if (this.y != other.y) { + return false; + } + if (this.z != other.z) { + return false; + } + if (this.type != other.type) { + return false; + } + if (this.data != other.data && (this.data == null || !this.data.equals(other.data))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 73 * hash + (this.world != null ? this.world.hashCode() : 0); + hash = 73 * hash + this.x; + hash = 73 * hash + this.y; + hash = 73 * hash + this.z; + hash = 73 * hash + this.type; + hash = 73 * hash + (this.data != null ? this.data.hashCode() : 0); + return hash; + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + chunk.getCraftWorld().getBlockMetadata().setMetadata(getBlock(), metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + return chunk.getCraftWorld().getBlockMetadata().getMetadata(getBlock(), metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return chunk.getCraftWorld().getBlockMetadata().hasMetadata(getBlock(), metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + chunk.getCraftWorld().getBlockMetadata().removeMetadata(getBlock(), metadataKey, owningPlugin); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java new file mode 100644 index 0000000..d306c3a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityBrewingStand; +import org.bukkit.block.Block; +import org.bukkit.block.BrewingStand; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventoryBrewer; +import org.bukkit.inventory.BrewerInventory; + +public class CraftBrewingStand extends CraftBlockState implements BrewingStand { + private final TileEntityBrewingStand brewingStand; + + public CraftBrewingStand(Block block) { + super(block); + + brewingStand = (TileEntityBrewingStand) ((CraftWorld) block.getWorld()).getTileEntityAt(getX(), getY(), getZ()); + } + + public BrewerInventory getInventory() { + return new CraftInventoryBrewer(brewingStand); + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + brewingStand.update(); + } + + return result; + } + + public int getBrewingTime() { + return brewingStand.brewTime; + } + + public void setBrewingTime(int brewTime) { + brewingStand.brewTime = brewTime; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java new file mode 100644 index 0000000..21f7b73 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java @@ -0,0 +1,72 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityChest; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest; +import org.bukkit.inventory.Inventory; + +public class CraftChest extends CraftBlockState implements Chest { + private final CraftWorld world; + private final TileEntityChest chest; + + public CraftChest(final Block block) { + super(block); + + world = (CraftWorld) block.getWorld(); + chest = (TileEntityChest) world.getTileEntityAt(getX(), getY(), getZ()); + } + + public Inventory getBlockInventory() { + return new CraftInventory(chest); + } + + public Inventory getInventory() { + int x = getX(); + int y = getY(); + int z = getZ(); + // The logic here is basically identical to the logic in BlockChest.interact + CraftInventory inventory = new CraftInventory(chest); + int id; + if (world.getBlockTypeIdAt(x, y, z) == Material.CHEST.getId()) { + id = Material.CHEST.getId(); + } else if (world.getBlockTypeIdAt(x, y, z) == Material.TRAPPED_CHEST.getId()) { + id = Material.TRAPPED_CHEST.getId(); + } else { + throw new IllegalStateException("CraftChest is not a chest but is instead " + world.getBlockAt(x, y, z)); + } + + if (world.getBlockTypeIdAt(x - 1, y, z) == id) { + CraftInventory left = new CraftInventory((TileEntityChest)world.getHandle().getTileEntity(x - 1, y, z)); + inventory = new CraftInventoryDoubleChest(left, inventory); + } + if (world.getBlockTypeIdAt(x + 1, y, z) == id) { + CraftInventory right = new CraftInventory((TileEntityChest) world.getHandle().getTileEntity(x + 1, y, z)); + inventory = new CraftInventoryDoubleChest(inventory, right); + } + if (world.getBlockTypeIdAt(x, y, z - 1) == id) { + CraftInventory left = new CraftInventory((TileEntityChest) world.getHandle().getTileEntity(x, y, z - 1)); + inventory = new CraftInventoryDoubleChest(left, inventory); + } + if (world.getBlockTypeIdAt(x, y, z + 1) == id) { + CraftInventory right = new CraftInventory((TileEntityChest) world.getHandle().getTileEntity(x, y, z + 1)); + inventory = new CraftInventoryDoubleChest(inventory, right); + } + return inventory; + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + chest.update(); + } + + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java new file mode 100644 index 0000000..57af2bc --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityCommand; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.craftbukkit.CraftWorld; + +public class CraftCommandBlock extends CraftBlockState implements CommandBlock { + private final TileEntityCommand commandBlock; + private String command; + private String name; + + public CraftCommandBlock(Block block) { + super(block); + + CraftWorld world = (CraftWorld) block.getWorld(); + commandBlock = (TileEntityCommand) world.getTileEntityAt(getX(), getY(), getZ()); + command = commandBlock.getCommandBlock().getCommand(); + name = commandBlock.getCommandBlock().getName(); + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command != null ? command : ""; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name != null ? name : "@"; + } + + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + commandBlock.getCommandBlock().setCommand(command); + commandBlock.getCommandBlock().setName(name); + } + + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java new file mode 100644 index 0000000..fa4c0a9 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java @@ -0,0 +1,73 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityMobSpawner; + +import org.bukkit.block.Block; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.entity.CreatureType; +import org.bukkit.entity.EntityType; + +public class CraftCreatureSpawner extends CraftBlockState implements CreatureSpawner { + private final TileEntityMobSpawner spawner; + + public CraftCreatureSpawner(final Block block) { + super(block); + + spawner = (TileEntityMobSpawner) ((CraftWorld) block.getWorld()).getTileEntityAt(getX(), getY(), getZ()); + } + + @Deprecated + public CreatureType getCreatureType() { + return CreatureType.fromName(spawner.getSpawner().getMobName()); + } + + public EntityType getSpawnedType() { + return EntityType.fromName(spawner.getSpawner().getMobName()); + } + + @Deprecated + public void setCreatureType(CreatureType creatureType) { + spawner.getSpawner().setMobName(creatureType.getName()); + } + + public void setSpawnedType(EntityType entityType) { + if (entityType == null || entityType.getName() == null) { + throw new IllegalArgumentException("Can't spawn EntityType " + entityType + " from mobspawners!"); + } + + spawner.getSpawner().setMobName(entityType.getName()); + } + + @Deprecated + public String getCreatureTypeId() { + return spawner.getSpawner().getMobName(); + } + + @Deprecated + public void setCreatureTypeId(String creatureName) { + setCreatureTypeByName(creatureName); + } + + public String getCreatureTypeName() { + return spawner.getSpawner().getMobName(); + } + + public void setCreatureTypeByName(String creatureType) { + // Verify input + EntityType type = EntityType.fromName(creatureType); + if (type == null) { + return; + } + setSpawnedType(type); + } + + public int getDelay() { + return spawner.getSpawner().spawnDelay; + } + + public void setDelay(int delay) { + spawner.getSpawner().spawnDelay = delay; + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java new file mode 100644 index 0000000..762a8e6 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java @@ -0,0 +1,64 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockDispenser; +import net.minecraft.server.Blocks; +import net.minecraft.server.TileEntityDispenser; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Dispenser; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource; +import org.bukkit.inventory.Inventory; +import org.bukkit.projectiles.BlockProjectileSource; + +public class CraftDispenser extends CraftBlockState implements Dispenser { + private final CraftWorld world; + private final TileEntityDispenser dispenser; + + public CraftDispenser(final Block block) { + super(block); + + world = (CraftWorld) block.getWorld(); + dispenser = (TileEntityDispenser) world.getTileEntityAt(getX(), getY(), getZ()); + } + + public Inventory getInventory() { + return new CraftInventory(dispenser); + } + + public BlockProjectileSource getBlockProjectileSource() { + Block block = getBlock(); + + if (block.getType() != Material.DISPENSER) { + return null; + } + + return new CraftBlockProjectileSource(dispenser); + } + + public boolean dispense() { + Block block = getBlock(); + + if (block.getType() == Material.DISPENSER) { + BlockDispenser dispense = (BlockDispenser) Blocks.DISPENSER; + + dispense.dispense(world.getHandle(), getX(), getY(), getZ()); + return true; + } else { + return false; + } + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + dispenser.update(); + } + + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java new file mode 100644 index 0000000..6b4ef8a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java @@ -0,0 +1,49 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockDropper; +import net.minecraft.server.Blocks; +import net.minecraft.server.TileEntityDropper; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Dropper; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.inventory.Inventory; + +public class CraftDropper extends CraftBlockState implements Dropper { + private final CraftWorld world; + private final TileEntityDropper dropper; + + public CraftDropper(final Block block) { + super(block); + + world = (CraftWorld) block.getWorld(); + dropper = (TileEntityDropper) world.getTileEntityAt(getX(), getY(), getZ()); + } + + public Inventory getInventory() { + return new CraftInventory(dropper); + } + + public void drop() { + Block block = getBlock(); + + if (block.getType() == Material.DROPPER) { + BlockDropper drop = (BlockDropper) Blocks.DROPPER; + + drop.dispense(world.getHandle(), getX(), getY(), getZ()); + } + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + dropper.update(); + } + + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java new file mode 100644 index 0000000..8c548f1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java @@ -0,0 +1,49 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityFurnace; +import org.bukkit.block.Block; +import org.bukkit.block.Furnace; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventoryFurnace; +import org.bukkit.inventory.FurnaceInventory; + +public class CraftFurnace extends CraftBlockState implements Furnace { + private final TileEntityFurnace furnace; + + public CraftFurnace(final Block block) { + super(block); + + furnace = (TileEntityFurnace) ((CraftWorld) block.getWorld()).getTileEntityAt(getX(), getY(), getZ()); + } + + public FurnaceInventory getInventory() { + return new CraftInventoryFurnace(furnace); + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + furnace.update(); + } + + return result; + } + + public short getBurnTime() { + return (short) furnace.burnTime; + } + + public void setBurnTime(short burnTime) { + furnace.burnTime = burnTime; + } + + public short getCookTime() { + return (short) furnace.cookTime; + } + + public void setCookTime(short cookTime) { + furnace.cookTime = cookTime; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java new file mode 100644 index 0000000..a46c472 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java @@ -0,0 +1,33 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityHopper; +import org.bukkit.block.Block; +import org.bukkit.block.Hopper; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.inventory.Inventory; + +public class CraftHopper extends CraftBlockState implements Hopper { + private final TileEntityHopper hopper; + + public CraftHopper(final Block block) { + super(block); + + hopper = (TileEntityHopper) ((CraftWorld) block.getWorld()).getTileEntityAt(getX(), getY(), getZ()); + } + + public Inventory getInventory() { + return new CraftInventory(hopper); + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + hopper.update(); + } + + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java new file mode 100644 index 0000000..761b76a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java @@ -0,0 +1,60 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockJukeBox; +import net.minecraft.server.Blocks; +import net.minecraft.server.ItemStack; +import net.minecraft.server.TileEntityRecordPlayer; +import org.bukkit.Effect; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Jukebox; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class CraftJukebox extends CraftBlockState implements Jukebox { + private final CraftWorld world; + private final TileEntityRecordPlayer jukebox; + + public CraftJukebox(final Block block) { + super(block); + + world = (CraftWorld) block.getWorld(); + jukebox = (TileEntityRecordPlayer) world.getTileEntityAt(getX(), getY(), getZ()); + } + + @Override + public Material getPlaying() { + ItemStack record = jukebox.getRecord(); + if (record == null) { + return Material.AIR; + } + return CraftMagicNumbers.getMaterial(record.getItem()); + } + + @Override + public void setPlaying(Material record) { + if (record == null || CraftMagicNumbers.getItem(record) == null) { + record = Material.AIR; + jukebox.setRecord(null); + } else { + jukebox.setRecord(new ItemStack(CraftMagicNumbers.getItem(record), 1)); + } + jukebox.update(); + if (record == Material.AIR) { + world.getHandle().setData(getX(), getY(), getZ(), 0, 3); + } else { + world.getHandle().setData(getX(), getY(), getZ(), 1, 3); + } + world.playEffect(getLocation(), Effect.RECORD_PLAY, record.getId()); + } + + public boolean isPlaying() { + return getRawData() == 1; + } + + public boolean eject() { + boolean result = isPlaying(); + ((BlockJukeBox) Blocks.JUKEBOX).dropRecord(world.getHandle(), getX(), getY(), getZ()); + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftNoteBlock.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftNoteBlock.java new file mode 100644 index 0000000..b83e335 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftNoteBlock.java @@ -0,0 +1,74 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityNote; + +import org.bukkit.Instrument; +import org.bukkit.Material; +import org.bukkit.Note; +import org.bukkit.block.Block; +import org.bukkit.block.NoteBlock; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class CraftNoteBlock extends CraftBlockState implements NoteBlock { + private final CraftWorld world; + private final TileEntityNote note; + + public CraftNoteBlock(final Block block) { + super(block); + + world = (CraftWorld) block.getWorld(); + note = (TileEntityNote) world.getTileEntityAt(getX(), getY(), getZ()); + } + + public Note getNote() { + return new Note(note.note); + } + + public byte getRawNote() { + return note.note; + } + + public void setNote(Note n) { + note.note = n.getId(); + } + + public void setRawNote(byte n) { + note.note = n; + } + + public boolean play() { + Block block = getBlock(); + + if (block.getType() == Material.NOTE_BLOCK) { + note.play(world.getHandle(), getX(), getY(), getZ()); + return true; + } else { + return false; + } + } + + @Override + public boolean play(byte instrument, byte note) { + Block block = getBlock(); + + if (block.getType() == Material.NOTE_BLOCK) { + world.getHandle().playBlockAction(getX(), getY(), getZ(), CraftMagicNumbers.getBlock(block), instrument, note); + return true; + } else { + return false; + } + } + + @Override + public boolean play(Instrument instrument, Note note) { + Block block = getBlock(); + + if (block.getType() == Material.NOTE_BLOCK) { + world.getHandle().playBlockAction(getX(), getY(), getZ(), CraftMagicNumbers.getBlock(block), instrument.getType(), note.getId()); + return true; + } else { + return false; + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftSign.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftSign.java new file mode 100644 index 0000000..1533dd4 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftSign.java @@ -0,0 +1,64 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntitySign; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.craftbukkit.CraftWorld; + +public class CraftSign extends CraftBlockState implements Sign { + private final TileEntitySign sign; + private final String[] lines; + + public CraftSign(final Block block) { + super(block); + + CraftWorld world = (CraftWorld) block.getWorld(); + sign = (TileEntitySign) world.getTileEntityAt(getX(), getY(), getZ()); + // Spigot start + if (sign == null) { + lines = new String[]{"", "", "", ""}; + return; + } + // Spigot end + lines = new String[sign.lines.length]; + System.arraycopy(sign.lines, 0, lines, 0, lines.length); + } + + public String[] getLines() { + return lines; + } + + public String getLine(int index) throws IndexOutOfBoundsException { + return lines[index]; + } + + public void setLine(int index, String line) throws IndexOutOfBoundsException { + lines[index] = line; + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result && sign != null) { // Spigot, add null check + sign.lines = sanitizeLines(lines); + sign.update(); + } + + return result; + } + + public static String[] sanitizeLines(String[] lines) { + String[] astring = new String[4]; + + for (int i = 0; i < 4; i++) { + if (i < lines.length && lines[i] != null) { + astring[i] = lines[i]; + } else { + astring[i] = ""; + } + } + + return TileEntitySign.sanitizeLines(astring); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java new file mode 100644 index 0000000..dc9a587 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java @@ -0,0 +1,205 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.TileEntitySkull; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +import org.bukkit.SkullType; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Skull; +import org.bukkit.craftbukkit.CraftWorld; + +public class CraftSkull extends CraftBlockState implements Skull { + private static final int MAX_OWNER_LENGTH = 16; + private final TileEntitySkull skull; + private GameProfile profile; + private SkullType skullType; + private byte rotation; + + public CraftSkull(final Block block) { + super(block); + + CraftWorld world = (CraftWorld) block.getWorld(); + skull = (TileEntitySkull) world.getTileEntityAt(getX(), getY(), getZ()); + profile = skull.getGameProfile(); + skullType = getSkullType(skull.getSkullType()); + rotation = (byte) skull.getRotation(); + } + + static SkullType getSkullType(int id) { + switch (id) { + case 0: + return SkullType.SKELETON; + case 1: + return SkullType.WITHER; + case 2: + return SkullType.ZOMBIE; + case 3: + return SkullType.PLAYER; + case 4: + return SkullType.CREEPER; + default: + throw new AssertionError(id); + } + } + + static int getSkullType(SkullType type) { + switch(type) { + case SKELETON: + return 0; + case WITHER: + return 1; + case ZOMBIE: + return 2; + case PLAYER: + return 3; + case CREEPER: + return 4; + default: + throw new AssertionError(type); + } + } + + static byte getBlockFace(BlockFace rotation) { + switch (rotation) { + case NORTH: + return 0; + case NORTH_NORTH_EAST: + return 1; + case NORTH_EAST: + return 2; + case EAST_NORTH_EAST: + return 3; + case EAST: + return 4; + case EAST_SOUTH_EAST: + return 5; + case SOUTH_EAST: + return 6; + case SOUTH_SOUTH_EAST: + return 7; + case SOUTH: + return 8; + case SOUTH_SOUTH_WEST: + return 9; + case SOUTH_WEST: + return 10; + case WEST_SOUTH_WEST: + return 11; + case WEST: + return 12; + case WEST_NORTH_WEST: + return 13; + case NORTH_WEST: + return 14; + case NORTH_NORTH_WEST: + return 15; + default: + throw new IllegalArgumentException("Invalid BlockFace rotation: " + rotation); + } + } + + static BlockFace getBlockFace(byte rotation) { + switch (rotation) { + case 0: + return BlockFace.NORTH; + case 1: + return BlockFace.NORTH_NORTH_EAST; + case 2: + return BlockFace.NORTH_EAST; + case 3: + return BlockFace.EAST_NORTH_EAST; + case 4: + return BlockFace.EAST; + case 5: + return BlockFace.EAST_SOUTH_EAST; + case 6: + return BlockFace.SOUTH_EAST; + case 7: + return BlockFace.SOUTH_SOUTH_EAST; + case 8: + return BlockFace.SOUTH; + case 9: + return BlockFace.SOUTH_SOUTH_WEST; + case 10: + return BlockFace.SOUTH_WEST; + case 11: + return BlockFace.WEST_SOUTH_WEST; + case 12: + return BlockFace.WEST; + case 13: + return BlockFace.WEST_NORTH_WEST; + case 14: + return BlockFace.NORTH_WEST; + case 15: + return BlockFace.NORTH_NORTH_WEST; + default: + throw new AssertionError(rotation); + } + } + + public boolean hasOwner() { + return profile != null; + } + + public String getOwner() { + return hasOwner() ? profile.getName() : null; + } + + public boolean setOwner(String name) { + if (name == null || name.length() > MAX_OWNER_LENGTH) { + return false; + } + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(name); + if (profile == null) { + return false; + } + + if (skullType != SkullType.PLAYER) { + skullType = SkullType.PLAYER; + } + + this.profile = profile; + return true; + } + + public BlockFace getRotation() { + return getBlockFace(rotation); + } + + public void setRotation(BlockFace rotation) { + this.rotation = getBlockFace(rotation); + } + + public SkullType getSkullType() { + return skullType; + } + + public void setSkullType(SkullType skullType) { + this.skullType = skullType; + + if (skullType != SkullType.PLAYER) { + profile = null; + } + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result) { + if (skullType == SkullType.PLAYER) { + skull.setGameProfile(profile); + } else { + skull.setSkullType(getSkullType(skullType)); + } + + skull.setRotation(rotation); + skull.update(); + } + + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java new file mode 100644 index 0000000..94dcd5e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.chunkio; + +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkProviderServer; +import net.minecraft.server.ChunkRegionLoader; +import net.minecraft.server.World; +import org.bukkit.craftbukkit.util.AsynchronousExecutor; +import org.spigotmc.SpigotConfig; // Poweruser + +public class ChunkIOExecutor { + static final int BASE_THREADS = 1; + + private static final AsynchronousExecutor instance = new AsynchronousExecutor(new ChunkIOProvider(), BASE_THREADS); + + public static Chunk syncChunkLoad(World world, ChunkRegionLoader loader, ChunkProviderServer provider, int x, int z) { + return instance.getSkipQueue(new QueuedChunk(x, z, loader, world, provider)); + } + + public static void queueChunkLoad(World world, ChunkRegionLoader loader, ChunkProviderServer provider, int x, int z, Runnable runnable) { + instance.add(new QueuedChunk(x, z, loader, world, provider), runnable); + } + + // Abuses the fact that hashCode and equals for QueuedChunk only use world and coords + public static void dropQueuedChunkLoad(World world, int x, int z, Runnable runnable) { + instance.drop(new QueuedChunk(x, z, null, world, null), runnable); + } + + public static void adjustPoolSize(int players) { + int size = Math.max(BASE_THREADS, (int) Math.ceil(players / SpigotConfig.playersPerChunkIOThread)); // Poweruser + instance.setActiveThreads(size); + } + + public static void tick() { + instance.finishActive(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java new file mode 100644 index 0000000..5de79d4 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java @@ -0,0 +1,80 @@ +package org.bukkit.craftbukkit.chunkio; + +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkRegionLoader; +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Server; +import org.bukkit.craftbukkit.util.AsynchronousExecutor; +import org.bukkit.craftbukkit.util.LongHash; + +import java.util.concurrent.atomic.AtomicInteger; + +class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider { + private final AtomicInteger threadNumber = new AtomicInteger(1); + + // async stuff + public Chunk callStage1(QueuedChunk queuedChunk) throws RuntimeException { + ChunkRegionLoader loader = queuedChunk.loader; + Object[] data = loader.loadChunk(queuedChunk.world, queuedChunk.x, queuedChunk.z); + + if (data != null) { + queuedChunk.compound = (NBTTagCompound) data[1]; + return (Chunk) data[0]; + } + + return null; + } + + // sync stuff + public void callStage2(QueuedChunk queuedChunk, Chunk chunk) throws RuntimeException { + if (chunk == null) { + // If the chunk loading failed just do it synchronously (may generate) + queuedChunk.provider.originalGetChunkAt(queuedChunk.x, queuedChunk.z); + return; + } + + queuedChunk.loader.loadEntities(chunk, queuedChunk.compound.getCompound("Level"), queuedChunk.world); + chunk.lastSaved = queuedChunk.provider.world.getTime(); + queuedChunk.provider.chunks.put(LongHash.toLong(queuedChunk.x, queuedChunk.z), chunk); // MineHQ + chunk.addEntities(); + + if (queuedChunk.provider.chunkProvider != null) { + queuedChunk.provider.world.timings.syncChunkLoadStructuresTimer.startTiming(); // Spigot + queuedChunk.provider.chunkProvider.recreateStructures(queuedChunk.x, queuedChunk.z); + queuedChunk.provider.world.timings.syncChunkLoadStructuresTimer.stopTiming(); // Spigot + } + + Server server = queuedChunk.provider.world.getServer(); + if (server != null) { + server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, false)); + } + + // Update neighbor counts + for (int x = -2; x < 3; x++) { + for (int z = -2; z < 3; z++) { + if (x == 0 && z == 0) { + continue; + } + + Chunk neighbor = queuedChunk.provider.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z); + if (neighbor != null) { + neighbor.setNeighborLoaded(-x, -z); + chunk.setNeighborLoaded(x, z); + } + } + } + + chunk.loadNearby(queuedChunk.provider, queuedChunk.provider, queuedChunk.x, queuedChunk.z); + } + + public void callStage3(QueuedChunk queuedChunk, Chunk chunk, Runnable runnable) throws RuntimeException { + runnable.run(); + } + + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, "Chunk I/O Executor Thread-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/QueuedChunk.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/QueuedChunk.java new file mode 100644 index 0000000..842d424 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/chunkio/QueuedChunk.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.chunkio; + +import net.minecraft.server.ChunkProviderServer; +import net.minecraft.server.ChunkRegionLoader; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.World; + +class QueuedChunk { + final int x; + final int z; + final ChunkRegionLoader loader; + final World world; + final ChunkProviderServer provider; + NBTTagCompound compound; + + public QueuedChunk(int x, int z, ChunkRegionLoader loader, World world, ChunkProviderServer provider) { + this.x = x; + this.z = z; + this.loader = loader; + this.world = world; + this.provider = provider; + } + + @Override + public int hashCode() { + return (x * 31 + z * 29) ^ world.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof QueuedChunk) { + QueuedChunk other = (QueuedChunk) object; + return x == other.x && z == other.z && world == other.world; + } + + return false; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java new file mode 100644 index 0000000..26a2fb8 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java @@ -0,0 +1,74 @@ +package org.bukkit.craftbukkit.command; + +import java.util.EnumMap; +import java.util.Map; + +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Attribute; +import jline.Terminal; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.craftbukkit.CraftServer; + +public class ColouredConsoleSender extends CraftConsoleCommandSender { + private final Terminal terminal; + private final Map replacements = new EnumMap(ChatColor.class); + private final ChatColor[] colors = ChatColor.values(); + + protected ColouredConsoleSender() { + super(); + this.terminal = ((CraftServer) getServer()).getReader().getTerminal(); + + replacements.put(ChatColor.BLACK, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString()); + replacements.put(ChatColor.DARK_BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString()); + replacements.put(ChatColor.DARK_GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).boldOff().toString()); + replacements.put(ChatColor.DARK_AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).boldOff().toString()); + replacements.put(ChatColor.DARK_RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).boldOff().toString()); + replacements.put(ChatColor.DARK_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).boldOff().toString()); + replacements.put(ChatColor.GOLD, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).boldOff().toString()); + replacements.put(ChatColor.GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).boldOff().toString()); + replacements.put(ChatColor.DARK_GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).bold().toString()); + replacements.put(ChatColor.BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).bold().toString()); + replacements.put(ChatColor.GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).bold().toString()); + replacements.put(ChatColor.AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).bold().toString()); + replacements.put(ChatColor.RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).bold().toString()); + replacements.put(ChatColor.LIGHT_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).bold().toString()); + replacements.put(ChatColor.YELLOW, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).bold().toString()); + replacements.put(ChatColor.WHITE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).bold().toString()); + replacements.put(ChatColor.MAGIC, Ansi.ansi().a(Attribute.BLINK_SLOW).toString()); + replacements.put(ChatColor.BOLD, Ansi.ansi().a(Attribute.UNDERLINE_DOUBLE).toString()); + replacements.put(ChatColor.STRIKETHROUGH, Ansi.ansi().a(Attribute.STRIKETHROUGH_ON).toString()); + replacements.put(ChatColor.UNDERLINE, Ansi.ansi().a(Attribute.UNDERLINE).toString()); + replacements.put(ChatColor.ITALIC, Ansi.ansi().a(Attribute.ITALIC).toString()); + replacements.put(ChatColor.RESET, Ansi.ansi().a(Attribute.RESET).toString()); + } + + @Override + public void sendMessage(String message) { + if (terminal.isAnsiSupported()) { + if (!conversationTracker.isConversingModaly()) { + String result = message; + for (ChatColor color : colors) { + if (replacements.containsKey(color)) { + result = result.replaceAll("(?i)" + color.toString(), replacements.get(color)); + } else { + result = result.replaceAll("(?i)" + color.toString(), ""); + } + } + System.out.println(result + Ansi.ansi().reset().toString()); + } + } else { + super.sendMessage(message); + } + } + + public static ConsoleCommandSender getInstance() { + if (Bukkit.getConsoleSender() != null) { + return Bukkit.getConsoleSender(); + } else { + return new ColouredConsoleSender(); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java new file mode 100644 index 0000000..7ef5772 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java @@ -0,0 +1,47 @@ +package org.bukkit.craftbukkit.command; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.util.Waitable; + +import jline.console.completer.Completer; + +public class ConsoleCommandCompleter implements Completer { + private final CraftServer server; + + public ConsoleCommandCompleter(CraftServer server) { + this.server = server; + } + + public int complete(final String buffer, final int cursor, final List candidates) { + Waitable> waitable = new Waitable>() { + @Override + protected List evaluate() { + return server.getCommandMap().tabComplete(server.getConsoleSender(), buffer); + } + }; + this.server.getServer().processQueue.add(waitable); + try { + List offers = waitable.get(); + if (offers == null) { + return cursor; + } + candidates.addAll(offers); + + final int lastSpace = buffer.lastIndexOf(' '); + if (lastSpace == -1) { + return cursor - buffer.length(); + } else { + return cursor - (buffer.length() - lastSpace - 1); + } + } catch (ExecutionException e) { + this.server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return cursor; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java new file mode 100644 index 0000000..bdee7d7 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java @@ -0,0 +1,45 @@ +package org.bukkit.craftbukkit.command; + +import net.minecraft.server.ICommandListener; +import net.minecraft.server.TileEntityCommandListener; + +import org.bukkit.block.Block; +import org.bukkit.command.BlockCommandSender; + +/** + * Represents input from a command block + */ +public class CraftBlockCommandSender extends ServerCommandSender implements BlockCommandSender { + private final TileEntityCommandListener commandBlock; + + public CraftBlockCommandSender(TileEntityCommandListener commandBlockListenerAbstract) { + super(); + this.commandBlock = commandBlockListenerAbstract; + } + + public Block getBlock() { + return commandBlock.getWorld().getWorld().getBlockAt(commandBlock.getChunkCoordinates().x, commandBlock.getChunkCoordinates().y, commandBlock.getChunkCoordinates().z); + } + + public void sendMessage(String message) { + } + + public void sendMessage(String[] messages) { + } + + public String getName() { + return commandBlock.getName(); + } + + public boolean isOp() { + return true; + } + + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of a block"); + } + + public ICommandListener getTileEntity() { + return commandBlock; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java new file mode 100644 index 0000000..9abcf92 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java @@ -0,0 +1,66 @@ +package org.bukkit.craftbukkit.command; + +import org.bukkit.ChatColor; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.conversations.Conversation; +import org.bukkit.conversations.ConversationAbandonedEvent; +import org.bukkit.conversations.ManuallyAbandonedConversationCanceller; +import org.bukkit.craftbukkit.conversations.ConversationTracker; + +/** + * Represents CLI input from a console + */ +public class CraftConsoleCommandSender extends ServerCommandSender implements ConsoleCommandSender { + + protected final ConversationTracker conversationTracker = new ConversationTracker(); + + protected CraftConsoleCommandSender() { + super(); + } + + public void sendMessage(String message) { + sendRawMessage(message); + } + + public void sendRawMessage(String message) { + System.out.println(ChatColor.stripColor(message)); + } + + public void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + public String getName() { + return "CONSOLE"; + } + + public boolean isOp() { + return true; + } + + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of server console"); + } + + public boolean beginConversation(Conversation conversation) { + return conversationTracker.beginConversation(conversation); + } + + public void abandonConversation(Conversation conversation) { + conversationTracker.abandonConversation(conversation, new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller())); + } + + public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) { + conversationTracker.abandonConversation(conversation, details); + } + + public void acceptConversationInput(String input) { + conversationTracker.acceptConversationInput(input); + } + + public boolean isConversing() { + return conversationTracker.isConversing(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java new file mode 100644 index 0000000..25d1255 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java @@ -0,0 +1,37 @@ +package org.bukkit.craftbukkit.command; + +import net.minecraft.server.RemoteControlCommandListener; +import org.bukkit.command.RemoteConsoleCommandSender; + +public class CraftRemoteConsoleCommandSender extends ServerCommandSender implements RemoteConsoleCommandSender { + public CraftRemoteConsoleCommandSender() { + super(); + } + + @Override + public void sendMessage(String message) { + RemoteControlCommandListener.instance.sendMessage(message + "\n"); // Send a newline after each message, to preserve formatting. + } + + @Override + public void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + @Override + public String getName() { + return "Rcon"; + } + + @Override + public boolean isOp() { + return true; + } + + @Override + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of remote controller."); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java new file mode 100644 index 0000000..b339cf3 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java @@ -0,0 +1,80 @@ +package org.bukkit.craftbukkit.command; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +import java.util.Set; + +public abstract class ServerCommandSender implements CommandSender { + private static PermissibleBase blockPermInst; + private final PermissibleBase perm; + + public ServerCommandSender() { + if (this instanceof CraftBlockCommandSender) { + if (blockPermInst == null) { + blockPermInst = new PermissibleBase(this); + } + this.perm = blockPermInst; + } else { + this.perm = new PermissibleBase(this); + } + } + + public boolean isPermissionSet(String name) { + return perm.isPermissionSet(name); + } + + public boolean isPermissionSet(Permission perm) { + return this.perm.isPermissionSet(perm); + } + + public boolean hasPermission(String name) { + return perm.hasPermission(name); + } + + public boolean hasPermission(Permission perm) { + return this.perm.hasPermission(perm); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return perm.addAttachment(plugin, name, value); + } + + public PermissionAttachment addAttachment(Plugin plugin) { + return perm.addAttachment(plugin); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return perm.addAttachment(plugin, name, value, ticks); + } + + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return perm.addAttachment(plugin, ticks); + } + + public void removeAttachment(PermissionAttachment attachment) { + perm.removeAttachment(attachment); + } + + public void recalculatePermissions() { + perm.recalculatePermissions(); + } + + public Set getEffectivePermissions() { + return perm.getEffectivePermissions(); + } + + public boolean isPlayer() { + return false; + } + + public Server getServer() { + return Bukkit.getServer(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java new file mode 100644 index 0000000..5909613 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java @@ -0,0 +1,191 @@ +package org.bukkit.craftbukkit.command; + +import java.util.List; + +import net.minecraft.server.ChatMessage; +import net.minecraft.server.CommandAbstract; +import net.minecraft.server.CommandBlockListenerAbstract; +import net.minecraft.server.CommandException; +import net.minecraft.server.EntityMinecartCommandBlock; +import net.minecraft.server.EntityMinecartCommandBlockListener; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.EnumChatFormat; +import net.minecraft.server.ExceptionUsage; +import net.minecraft.server.ICommandListener; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PlayerSelector; +import net.minecraft.server.RemoteControlCommandListener; +import net.minecraft.server.TileEntityCommandListener; +import net.minecraft.server.WorldServer; + +import org.apache.commons.lang.Validate; +import org.apache.logging.log4j.Level; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.RemoteConsoleCommandSender; +import org.bukkit.command.defaults.*; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.entity.CraftMinecartCommand; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; + +public final class VanillaCommandWrapper extends VanillaCommand { + protected final CommandAbstract vanillaCommand; + + public VanillaCommandWrapper(CommandAbstract vanillaCommand) { + super(vanillaCommand.getCommand()); + this.vanillaCommand = vanillaCommand; + } + + public VanillaCommandWrapper(CommandAbstract vanillaCommand, String usage) { + super(vanillaCommand.getCommand()); + this.vanillaCommand = vanillaCommand; + this.description = "A Mojang provided command."; + this.usageMessage = usage; + this.setPermission("minecraft.command." + vanillaCommand.getCommand()); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + + ICommandListener icommandlistener = getListener(sender); + // Some commands use the worldserver variable but we leave it full of null values, + // so we must temporarily populate it with the world of the commandsender + WorldServer[] prev = MinecraftServer.getServer().worldServer; + MinecraftServer.getServer().worldServer = new WorldServer[]{(WorldServer) icommandlistener.getWorld()}; + try { + vanillaCommand.execute(icommandlistener, args); + } catch (ExceptionUsage exceptionusage) { + ChatMessage chatmessage = new ChatMessage("commands.generic.usage", new Object[] {new ChatMessage(exceptionusage.getMessage(), exceptionusage.getArgs())}); + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage); + } catch (CommandException commandexception) { + ChatMessage chatmessage = new ChatMessage(commandexception.getMessage(), commandexception.getArgs()); + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage); + } finally { + MinecraftServer.getServer().worldServer = prev; + } + return true; + } + + @Override + public List 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"); + return (List) vanillaCommand.tabComplete(getListener(sender), args); + } + + public final int dispatchVanillaCommandBlock(CommandBlockListenerAbstract icommandlistener, String s) { + // Copied from net.minecraft.server.CommandHandler + s = s.trim(); + if (s.startsWith("/")) { + s = s.substring(1); + } + String as[] = s.split(" "); + as = dropFirstArgument(as); + int i = getPlayerListSize(as); + int j = 0; + // Some commands use the worldserver variable but we leave it full of null values, + // so we must temporarily populate it with the world of the commandsender + WorldServer[] prev = MinecraftServer.getServer().worldServer; + MinecraftServer.getServer().worldServer = new WorldServer[]{(WorldServer) icommandlistener.getWorld()}; + try { + if (vanillaCommand.canUse(icommandlistener)) { + if (i > -1) { + EntityPlayer aentityplayer[] = PlayerSelector.getPlayers(icommandlistener, as[i]); + String s2 = as[i]; + EntityPlayer aentityplayer1[] = aentityplayer; + int k = aentityplayer1.length; + for (int l = 0; l < k;l++) { + EntityPlayer entityplayer = aentityplayer1[l]; + as[i] = entityplayer.getName(); + try { + vanillaCommand.execute(icommandlistener, as); + j++; + continue; + } catch (CommandException commandexception1) { + ChatMessage chatmessage4 = new ChatMessage(commandexception1.getMessage(), commandexception1.getArgs()); + chatmessage4.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage4); + } + } + + as[i] = s2; + } else { + vanillaCommand.execute(icommandlistener, as); + j++; + } + } else { + ChatMessage chatmessage = new ChatMessage("commands.generic.permission", new Object[0]); + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage); + } + } catch (ExceptionUsage exceptionusage) { + ChatMessage chatmessage1 = new ChatMessage("commands.generic.usage", new Object[] { new ChatMessage(exceptionusage.getMessage(), exceptionusage.getArgs()) }); + chatmessage1.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage1); + } catch (CommandException commandexception) { + ChatMessage chatmessage2 = new ChatMessage(commandexception.getMessage(), commandexception.getArgs()); + chatmessage2.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage2); + } catch (Throwable throwable) { + ChatMessage chatmessage3 = new ChatMessage("commands.generic.exception", new Object[0]); + chatmessage3.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage3); + if(icommandlistener instanceof TileEntityCommandListener) { + TileEntityCommandListener listener = (TileEntityCommandListener) icommandlistener; + MinecraftServer.getLogger().log(Level.WARN, String.format("CommandBlock at (%d,%d,%d) failed to handle command", listener.getChunkCoordinates().x, listener.getChunkCoordinates().y, listener.getChunkCoordinates().z), throwable); + } else if (icommandlistener instanceof EntityMinecartCommandBlockListener) { + EntityMinecartCommandBlockListener listener = (EntityMinecartCommandBlockListener) icommandlistener; + MinecraftServer.getLogger().log(Level.WARN, String.format("MinecartCommandBlock at (%d,%d,%d) failed to handle command", listener.getChunkCoordinates().x, listener.getChunkCoordinates().y, listener.getChunkCoordinates().z), throwable); + } else { + MinecraftServer.getLogger().log(Level.WARN, String.format("Unknown CommandBlock failed to handle command"), throwable); + } + } finally { + MinecraftServer.getServer().worldServer = prev; + } + return j; + } + + private ICommandListener getListener(CommandSender sender) { + if (sender instanceof Player) { + return ((CraftPlayer) sender).getHandle(); + } + if (sender instanceof BlockCommandSender) { + return ((CraftBlockCommandSender) sender).getTileEntity(); + } + if (sender instanceof CommandMinecart) { + return ((EntityMinecartCommandBlock) ((CraftMinecartCommand) sender).getHandle()).getCommandBlock(); + } + if (sender instanceof RemoteConsoleCommandSender) { + return RemoteControlCommandListener.instance; + } + if (sender instanceof ConsoleCommandSender) { + return ((CraftServer) sender.getServer()).getServer(); + } + return null; + } + + private int getPlayerListSize(String as[]) { + for (int i = 0; i < as.length; i++) { + if (vanillaCommand.isListStart(as, i) && PlayerSelector.isList(as[i])) { + return i; + } + } + return -1; + } + + private String[] dropFirstArgument(String as[]) { + String as1[] = new String[as.length - 1]; + for (int i = 1; i < as.length; i++) { + as1[i - 1] = as[i]; + } + + return as1; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java new file mode 100644 index 0000000..30ef7d9 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java @@ -0,0 +1,69 @@ +package org.bukkit.craftbukkit.conversations; + +import java.util.LinkedList; +import java.util.logging.Level; + +import org.bukkit.Bukkit; +import org.bukkit.conversations.Conversation; +import org.bukkit.conversations.ConversationAbandonedEvent; +import org.bukkit.conversations.ManuallyAbandonedConversationCanceller; + +/** + */ +public class ConversationTracker { + + private LinkedList conversationQueue = new LinkedList(); + + public synchronized boolean beginConversation(Conversation conversation) { + if (!conversationQueue.contains(conversation)) { + conversationQueue.addLast(conversation); + if (conversationQueue.getFirst() == conversation) { + conversation.begin(); + conversation.outputNextPrompt(); + return true; + } + } + return true; + } + + public synchronized void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) { + if (!conversationQueue.isEmpty()) { + if (conversationQueue.getFirst() == conversation) { + conversation.abandon(details); + } + if (conversationQueue.contains(conversation)) { + conversationQueue.remove(conversation); + } + if (!conversationQueue.isEmpty()) { + conversationQueue.getFirst().outputNextPrompt(); + } + } + } + + public synchronized void abandonAllConversations() { + + LinkedList oldQueue = conversationQueue; + conversationQueue = new LinkedList(); + for (Conversation conversation : oldQueue) { + try { + conversation.abandon(new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller())); + } catch (Throwable t) { + Bukkit.getLogger().log(Level.SEVERE, "Unexpected exception while abandoning a conversation", t); + } + } + } + + public synchronized void acceptConversationInput(String input) { + if (isConversing()) { + conversationQueue.getFirst().acceptInput(input); + } + } + + public synchronized boolean isConversing() { + return !conversationQueue.isEmpty(); + } + + public synchronized boolean isConversingModaly() { + return isConversing() && conversationQueue.getFirst().isModal(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java new file mode 100644 index 0000000..18a6e38 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java @@ -0,0 +1,139 @@ +package org.bukkit.craftbukkit.enchantments; + +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.enchantments.EnchantmentWrapper; +import org.bukkit.inventory.ItemStack; + +public class CraftEnchantment extends Enchantment { + private final net.minecraft.server.Enchantment target; + + public CraftEnchantment(net.minecraft.server.Enchantment target) { + super(target.id); + this.target = target; + } + + @Override + public int getMaxLevel() { + return target.getMaxLevel(); + } + + @Override + public int getStartLevel() { + return target.getStartLevel(); + } + + @Override + public EnchantmentTarget getItemTarget() { + switch (target.slot) { + case ALL: + return EnchantmentTarget.ALL; + case ARMOR: + return EnchantmentTarget.ARMOR; + case ARMOR_FEET: + return EnchantmentTarget.ARMOR_FEET; + case ARMOR_HEAD: + return EnchantmentTarget.ARMOR_HEAD; + case ARMOR_LEGS: + return EnchantmentTarget.ARMOR_LEGS; + case ARMOR_TORSO: + return EnchantmentTarget.ARMOR_TORSO; + case DIGGER: + return EnchantmentTarget.TOOL; + case WEAPON: + return EnchantmentTarget.WEAPON; + case BOW: + return EnchantmentTarget.BOW; + case FISHING_ROD: + return EnchantmentTarget.FISHING_ROD; + default: + return null; + } + } + + @Override + public boolean canEnchantItem(ItemStack item) { + return target.canEnchant(CraftItemStack.asNMSCopy(item)); + } + + @Override + public String getName() { + switch (target.id) { + case 0: + return "PROTECTION_ENVIRONMENTAL"; + case 1: + return "PROTECTION_FIRE"; + case 2: + return "PROTECTION_FALL"; + case 3: + return "PROTECTION_EXPLOSIONS"; + case 4: + return "PROTECTION_PROJECTILE"; + case 5: + return "OXYGEN"; + case 6: + return "WATER_WORKER"; + case 7: + return "THORNS"; + case 16: + return "DAMAGE_ALL"; + case 17: + return "DAMAGE_UNDEAD"; + case 18: + return "DAMAGE_ARTHROPODS"; + case 19: + return "KNOCKBACK"; + case 20: + return "FIRE_ASPECT"; + case 21: + return "LOOT_BONUS_MOBS"; + case 32: + return "DIG_SPEED"; + case 33: + return "SILK_TOUCH"; + case 34: + return "DURABILITY"; + case 35: + return "LOOT_BONUS_BLOCKS"; + case 48: + return "ARROW_DAMAGE"; + case 49: + return "ARROW_KNOCKBACK"; + case 50: + return "ARROW_FIRE"; + case 51: + return "ARROW_INFINITE"; + case 61: + return "LUCK"; + case 62: + return "LURE"; + default: + return "UNKNOWN_ENCHANT_" + target.id; + } + } + + public static net.minecraft.server.Enchantment getRaw(Enchantment enchantment) { + if (enchantment instanceof EnchantmentWrapper) { + enchantment = ((EnchantmentWrapper) enchantment).getEnchantment(); + } + + if (enchantment instanceof CraftEnchantment) { + return ((CraftEnchantment) enchantment).target; + } + + return null; + } + + @Override + public boolean conflictsWith(Enchantment other) { + if (other instanceof EnchantmentWrapper) { + other = ((EnchantmentWrapper) other).getEnchantment(); + } + if (!(other instanceof CraftEnchantment)) { + return false; + } + CraftEnchantment ench = (CraftEnchantment) other; + return !target.a(ench.target); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java new file mode 100644 index 0000000..7c16255 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Projectile; + +public abstract class AbstractProjectile extends CraftEntity implements Projectile { + + private boolean doesBounce; + + public AbstractProjectile(CraftServer server, net.minecraft.server.Entity entity) { + super(server, entity); + doesBounce = false; + } + + public boolean doesBounce() { + return doesBounce; + } + + public void setBounce(boolean doesBounce) { + this.doesBounce = doesBounce; + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java new file mode 100644 index 0000000..ce49700 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java @@ -0,0 +1,67 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityAgeable; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Ageable; + +public class CraftAgeable extends CraftCreature implements Ageable { + public CraftAgeable(CraftServer server, EntityAgeable entity) { + super(server, entity); + } + + public int getAge() { + return getHandle().getAge(); + } + + public void setAge(int age) { + getHandle().setAge(age); + } + + public void setAgeLock(boolean lock) { + getHandle().ageLocked = lock; + } + + public boolean getAgeLock() { + return getHandle().ageLocked; + } + + public void setBaby() { + if (isAdult()) { + setAge(-24000); + } + } + + public void setAdult() { + if (!isAdult()) { + setAge(0); + } + } + + public boolean isAdult() { + return getAge() >= 0; + } + + + public boolean canBreed() { + return getAge() == 0; + } + + public void setBreed(boolean breed) { + if (breed) { + setAge(0); + } else if (isAdult()) { + setAge(6000); + } + } + + @Override + public EntityAgeable getHandle() { + return (EntityAgeable) entity; + } + + @Override + public String toString() { + return "CraftAgeable"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAmbient.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAmbient.java new file mode 100644 index 0000000..086980e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAmbient.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityAmbient; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Ambient; +import org.bukkit.entity.EntityType; + +public class CraftAmbient extends CraftLivingEntity implements Ambient { + public CraftAmbient(CraftServer server, EntityAmbient entity) { + super(server, entity); + } + + @Override + public EntityAmbient getHandle() { + return (EntityAmbient) entity; + } + + @Override + public String toString() { + return "CraftAmbient"; + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAnimals.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAnimals.java new file mode 100644 index 0000000..4b9b078 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAnimals.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityAnimal; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Animals; + +public class CraftAnimals extends CraftAgeable implements Animals { + + public CraftAnimals(CraftServer server, EntityAnimal entity) { + super(server, entity); + } + + @Override + public EntityAnimal getHandle() { + return (EntityAnimal) entity; + } + + @Override + public String toString() { + return "CraftAnimals"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java new file mode 100644 index 0000000..8c8a173 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java @@ -0,0 +1,96 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityArrow; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.projectiles.ProjectileSource; + +public class CraftArrow extends AbstractProjectile implements Arrow { + + public CraftArrow(CraftServer server, EntityArrow entity) { + super(server, entity); + } + + public void setKnockbackStrength(int knockbackStrength) { + Validate.isTrue(knockbackStrength >= 0, "Knockback cannot be negative"); + getHandle().setKnockbackStrength(knockbackStrength); + } + + public int getKnockbackStrength() { + return getHandle().knockbackStrength; + } + + public boolean isCritical() { + return getHandle().isCritical(); + } + + public void setCritical(boolean critical) { + getHandle().setCritical(critical); + } + + public ProjectileSource getShooter() { + return getHandle().projectileSource; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof LivingEntity) { + getHandle().shooter = ((CraftLivingEntity) shooter).getHandle(); + } else { + getHandle().shooter = null; + } + getHandle().projectileSource = shooter; + } + + @Override + public EntityArrow getHandle() { + return (EntityArrow) entity; + } + + @Override + public String toString() { + return "CraftArrow"; + } + + public EntityType getType() { + return EntityType.ARROW; + } + + @Deprecated + public LivingEntity _INVALID_getShooter() { + if (getHandle().shooter == null) { + return null; + } + return (LivingEntity) getHandle().shooter.getBukkitEntity(); + } + + @Deprecated + public void _INVALID_setShooter(LivingEntity shooter) { + getHandle().shooter = ((CraftLivingEntity) shooter).getHandle(); + } + + // Spigot start + private final Arrow.Spigot spigot = new Arrow.Spigot() + { + @Override + public double getDamage() + { + return getHandle().e(); + } + + @Override + public void setDamage(double damage) + { + getHandle().b( damage ); + } + }; + + public Arrow.Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java new file mode 100644 index 0000000..76ada1c --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityBat; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Bat; +import org.bukkit.entity.EntityType; + +public class CraftBat extends CraftAmbient implements Bat { + public CraftBat(CraftServer server, EntityBat entity) { + super(server, entity); + } + + @Override + public EntityBat getHandle() { + return (EntityBat) entity; + } + + @Override + public String toString() { + return "CraftBat"; + } + + public EntityType getType() { + return EntityType.BAT; + } + + @Override + public boolean isAwake() { + return !getHandle().isAsleep(); + } + + @Override + public void setAwake(boolean state) { + getHandle().setAsleep(!state); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBlaze.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBlaze.java new file mode 100644 index 0000000..830d7a8 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBlaze.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityBlaze; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Blaze; +import org.bukkit.entity.EntityType; + +public class CraftBlaze extends CraftMonster implements Blaze { + public CraftBlaze(CraftServer server, EntityBlaze entity) { + super(server, entity); + } + + @Override + public EntityBlaze getHandle() { + return (EntityBlaze) entity; + } + + @Override + public String toString() { + return "CraftBlaze"; + } + + public EntityType getType() { + return EntityType.BLAZE; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java new file mode 100644 index 0000000..103b9d4 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java @@ -0,0 +1,63 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityBoat; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Boat; +import org.bukkit.entity.EntityType; + +public class CraftBoat extends CraftVehicle implements Boat { + + public CraftBoat(CraftServer server, EntityBoat entity) { + super(server, entity); + } + + public double getMaxSpeed() { + return getHandle().maxSpeed; + } + + public void setMaxSpeed(double speed) { + if (speed >= 0D) { + getHandle().maxSpeed = speed; + } + } + + public double getOccupiedDeceleration() { + return getHandle().occupiedDeceleration; + } + + public void setOccupiedDeceleration(double speed) { + if (speed >= 0D) { + getHandle().occupiedDeceleration = speed; + } + } + + public double getUnoccupiedDeceleration() { + return getHandle().unoccupiedDeceleration; + } + + public void setUnoccupiedDeceleration(double speed) { + getHandle().unoccupiedDeceleration = speed; + } + + public boolean getWorkOnLand() { + return getHandle().landBoats; + } + + public void setWorkOnLand(boolean workOnLand) { + getHandle().landBoats = workOnLand; + } + + @Override + public EntityBoat getHandle() { + return (EntityBoat) entity; + } + + @Override + public String toString() { + return "CraftBoat"; + } + + public EntityType getType() { + return EntityType.BOAT; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCaveSpider.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCaveSpider.java new file mode 100644 index 0000000..0648a85 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCaveSpider.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCaveSpider; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.CaveSpider; +import org.bukkit.entity.EntityType; + +public class CraftCaveSpider extends CraftSpider implements CaveSpider { + public CraftCaveSpider(CraftServer server, EntityCaveSpider entity) { + super(server, entity); + } + + @Override + public EntityCaveSpider getHandle() { + return (EntityCaveSpider) entity; + } + + @Override + public String toString() { + return "CraftCaveSpider"; + } + + public EntityType getType() { + return EntityType.CAVE_SPIDER; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java new file mode 100644 index 0000000..d20c219 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityChicken; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Chicken; +import org.bukkit.entity.EntityType; + +public class CraftChicken extends CraftAnimals implements Chicken { + + public CraftChicken(CraftServer server, EntityChicken entity) { + super(server, entity); + } + + @Override + public EntityChicken getHandle() { + return (EntityChicken) entity; + } + + @Override + public String toString() { + return "CraftChicken"; + } + + public EntityType getType() { + return EntityType.CHICKEN; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexLivingEntity.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexLivingEntity.java new file mode 100644 index 0000000..cc115cc --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexLivingEntity.java @@ -0,0 +1,21 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLiving; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ComplexLivingEntity; + +public abstract class CraftComplexLivingEntity extends CraftLivingEntity implements ComplexLivingEntity { + public CraftComplexLivingEntity(CraftServer server, EntityLiving entity) { + super(server, entity); + } + + @Override + public EntityLiving getHandle() { + return (EntityLiving) entity; + } + + @Override + public String toString() { + return "CraftComplexLivingEntity"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexPart.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexPart.java new file mode 100644 index 0000000..eb1ce79 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexPart.java @@ -0,0 +1,43 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityComplexPart; +import net.minecraft.server.EntityEnderDragon; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ComplexEntityPart; +import org.bukkit.entity.ComplexLivingEntity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.EntityDamageEvent; + +public class CraftComplexPart extends CraftEntity implements ComplexEntityPart { + public CraftComplexPart(CraftServer server, EntityComplexPart entity) { + super(server, entity); + } + + public ComplexLivingEntity getParent() { + return (ComplexLivingEntity) ((EntityEnderDragon) getHandle().owner).getBukkitEntity(); + } + + @Override + public void setLastDamageCause(EntityDamageEvent cause) { + getParent().setLastDamageCause(cause); + } + + @Override + public EntityDamageEvent getLastDamageCause() { + return getParent().getLastDamageCause(); + } + + @Override + public EntityComplexPart getHandle() { + return (EntityComplexPart) entity; + } + + @Override + public String toString() { + return "CraftComplexPart"; + } + + public EntityType getType() { + return EntityType.COMPLEX_PART; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java new file mode 100644 index 0000000..fc48ebd --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCow; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Cow; +import org.bukkit.entity.EntityType; + +public class CraftCow extends CraftAnimals implements Cow { + + public CraftCow(CraftServer server, EntityCow entity) { + super(server, entity); + } + + @Override + public EntityCow getHandle() { + return (EntityCow) entity; + } + + @Override + public String toString() { + return "CraftCow"; + } + + public EntityType getType() { + return EntityType.COW; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCreature.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCreature.java new file mode 100644 index 0000000..a6c0b94 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCreature.java @@ -0,0 +1,42 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCreature; +import net.minecraft.server.EntityLiving; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Creature; +import org.bukkit.entity.LivingEntity; + +public class CraftCreature extends CraftLivingEntity implements Creature { + public CraftCreature(CraftServer server, EntityCreature entity) { + super(server, entity); + } + + public void setTarget(LivingEntity target) { + EntityCreature entity = getHandle(); + if (target == null) { + entity.target = null; + entity.setGoalTarget(null); + } else if (target instanceof CraftLivingEntity) { + entity.target = ((CraftLivingEntity) target).getHandle(); + entity.pathEntity = entity.world.findPath(entity, entity.target, 16.0F, true, false, false, true); + entity.setGoalTarget(((CraftLivingEntity) target).getHandle()); + } + } + + public CraftLivingEntity getTarget() { + if (getHandle().target == null) return null; + if (!(getHandle().target instanceof EntityLiving)) return null; + + return (CraftLivingEntity) getHandle().target.getBukkitEntity(); + } + + @Override + public EntityCreature getHandle() { + return (EntityCreature) entity; + } + + @Override + public String toString() { + return "CraftCreature"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCreeper.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCreeper.java new file mode 100644 index 0000000..ed771a5 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCreeper.java @@ -0,0 +1,54 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCreeper; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.CreeperPowerEvent; + +public class CraftCreeper extends CraftMonster implements Creeper { + + public CraftCreeper(CraftServer server, EntityCreeper entity) { + super(server, entity); + } + + public boolean isPowered() { + return getHandle().isPowered(); + } + + public void setPowered(boolean powered) { + CraftServer server = this.server; + Creeper entity = (Creeper) this.getHandle().getBukkitEntity(); + + if (powered) { + CreeperPowerEvent event = new CreeperPowerEvent(entity, CreeperPowerEvent.PowerCause.SET_ON); + server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + getHandle().setPowered(true); + } + } else { + CreeperPowerEvent event = new CreeperPowerEvent(entity, CreeperPowerEvent.PowerCause.SET_OFF); + server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + getHandle().setPowered(false); + } + } + } + + @Override + public EntityCreeper getHandle() { + return (EntityCreeper) entity; + } + + @Override + public String toString() { + return "CraftCreeper"; + } + + public EntityType getType() { + return EntityType.CREEPER; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEgg.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEgg.java new file mode 100644 index 0000000..60c5188 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEgg.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEgg; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Egg; +import org.bukkit.entity.EntityType; + +public class CraftEgg extends CraftProjectile implements Egg { + public CraftEgg(CraftServer server, EntityEgg entity) { + super(server, entity); + } + + @Override + public EntityEgg getHandle() { + return (EntityEgg) entity; + } + + @Override + public String toString() { + return "CraftEgg"; + } + + public EntityType getType() { + return EntityType.EGG; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java new file mode 100644 index 0000000..2bcf3a1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEnderCrystal; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderCrystal; +import org.bukkit.entity.EntityType; + +public class CraftEnderCrystal extends CraftEntity implements EnderCrystal { + public CraftEnderCrystal(CraftServer server, EntityEnderCrystal entity) { + super(server, entity); + } + + @Override + public EntityEnderCrystal getHandle() { + return (EntityEnderCrystal) entity; + } + + @Override + public String toString() { + return "CraftEnderCrystal"; + } + + public EntityType getType() { + return EntityType.ENDER_CRYSTAL; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java new file mode 100644 index 0000000..fa0d63a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java @@ -0,0 +1,44 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; + +import java.util.Set; + +import net.minecraft.server.EntityComplexPart; +import net.minecraft.server.EntityEnderDragon; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ComplexEntityPart; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.EntityType; + +public class CraftEnderDragon extends CraftComplexLivingEntity implements EnderDragon { + public CraftEnderDragon(CraftServer server, EntityEnderDragon entity) { + super(server, entity); + } + + public Set getParts() { + Builder builder = ImmutableSet.builder(); + + for (EntityComplexPart part : getHandle().children) { + builder.add((ComplexEntityPart) part.getBukkitEntity()); + } + + return builder.build(); + } + + @Override + public EntityEnderDragon getHandle() { + return (EntityEnderDragon) entity; + } + + @Override + public String toString() { + return "CraftEnderDragon"; + } + + public EntityType getType() { + return EntityType.ENDER_DRAGON; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java new file mode 100644 index 0000000..736a460 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java @@ -0,0 +1,87 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityComplexPart; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.EnderDragonPart; +import org.bukkit.entity.Entity; +import org.bukkit.util.NumberConversions; + +public class CraftEnderDragonPart extends CraftComplexPart implements EnderDragonPart { + public CraftEnderDragonPart(CraftServer server, EntityComplexPart entity) { + super(server, entity); + } + + @Override + public EnderDragon getParent() { + return (EnderDragon) super.getParent(); + } + + @Override + public EntityComplexPart getHandle() { + return (EntityComplexPart) entity; + } + + @Override + public String toString() { + return "CraftEnderDragonPart"; + } + + public void damage(double amount) { + getParent().damage(amount); + } + + public void damage(double amount, Entity source) { + getParent().damage(amount, source); + } + + public double getHealth() { + return getParent().getHealth(); + } + + public void setHealth(double health) { + getParent().setHealth(health); + } + + public double getMaxHealth() { + return getParent().getMaxHealth(); + } + + public void setMaxHealth(double health) { + getParent().setMaxHealth(health); + } + + public void resetMaxHealth() { + getParent().resetMaxHealth(); + } + + @Deprecated + public void _INVALID_damage(int amount) { + damage(amount); + } + + @Deprecated + public void _INVALID_damage(int amount, Entity source) { + damage(amount, source); + } + + @Deprecated + public int _INVALID_getHealth() { + return NumberConversions.ceil(getHealth()); + } + + @Deprecated + public void _INVALID_setHealth(int health) { + setHealth(health); + } + + @Deprecated + public int _INVALID_getMaxHealth() { + return NumberConversions.ceil(getMaxHealth()); + } + + @Deprecated + public void _INVALID_setMaxHealth(int health) { + setMaxHealth(health); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderPearl.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderPearl.java new file mode 100644 index 0000000..f42f9ab --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderPearl.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEnderPearl; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.EntityType; + +public class CraftEnderPearl extends CraftProjectile implements EnderPearl { + public CraftEnderPearl(CraftServer server, EntityEnderPearl entity) { + super(server, entity); + } + + @Override + public EntityEnderPearl getHandle() { + return (EntityEnderPearl) entity; + } + + @Override + public String toString() { + return "CraftEnderPearl"; + } + + public EntityType getType() { + return EntityType.ENDER_PEARL; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java new file mode 100644 index 0000000..e3a5081 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEnderSignal; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderSignal; +import org.bukkit.entity.EntityType; + +public class CraftEnderSignal extends CraftEntity implements EnderSignal { + public CraftEnderSignal(CraftServer server, EntityEnderSignal entity) { + super(server, entity); + } + + @Override + public EntityEnderSignal getHandle() { + return (EntityEnderSignal) entity; + } + + @Override + public String toString() { + return "CraftEnderSignal"; + } + + public EntityType getType() { + return EntityType.ENDER_SIGNAL; + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java new file mode 100644 index 0000000..09a03c0 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEnderman; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.EntityType; +import org.bukkit.material.MaterialData; + +public class CraftEnderman extends CraftMonster implements Enderman { + public CraftEnderman(CraftServer server, EntityEnderman entity) { + super(server, entity); + } + + public MaterialData getCarriedMaterial() { + return CraftMagicNumbers.getMaterial(getHandle().getCarried()).getNewData((byte) getHandle().getCarriedData()); + } + + public void setCarriedMaterial(MaterialData data) { + getHandle().setCarried(CraftMagicNumbers.getBlock(data.getItemTypeId())); + getHandle().setCarriedData(data.getData()); + } + + @Override + public EntityEnderman getHandle() { + return (EntityEnderman) entity; + } + + @Override + public String toString() { + return "CraftEnderman"; + } + + public EntityType getType() { + return EntityType.ENDERMAN; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java new file mode 100644 index 0000000..75bfc69 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -0,0 +1,428 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.List; +import java.util.UUID; + +import net.minecraft.server.*; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.Vector; + +public abstract class CraftEntity implements org.bukkit.entity.Entity { + protected final CraftServer server; + protected Entity entity; + private EntityDamageEvent lastDamageEvent; + + public CraftEntity(final CraftServer server, final Entity entity) { + this.server = server; + this.entity = entity; + } + + public static CraftEntity getEntity(CraftServer server, Entity entity) { + /** + * Order is *EXTREMELY* important -- keep it right! =D + */ + if (entity instanceof EntityLiving) { + // Players + if (entity instanceof EntityHuman) { + if (entity instanceof EntityPlayer) { return new CraftPlayer(server, (EntityPlayer) entity); } + else { return new CraftHumanEntity(server, (EntityHuman) entity); } + } + else if (entity instanceof EntityCreature) { + // Animals + if (entity instanceof EntityAnimal) { + if (entity instanceof EntityChicken) { return new CraftChicken(server, (EntityChicken) entity); } + else if (entity instanceof EntityCow) { + if (entity instanceof EntityMushroomCow) { return new CraftMushroomCow(server, (EntityMushroomCow) entity); } + else { return new CraftCow(server, (EntityCow) entity); } + } + else if (entity instanceof EntityPig) { return new CraftPig(server, (EntityPig) entity); } + else if (entity instanceof EntityTameableAnimal) { + if (entity instanceof EntityWolf) { return new CraftWolf(server, (EntityWolf) entity); } + else if (entity instanceof EntityOcelot) { return new CraftOcelot(server, (EntityOcelot) entity); } + } + else if (entity instanceof EntitySheep) { return new CraftSheep(server, (EntitySheep) entity); } + else if (entity instanceof EntityHorse) { return new CraftHorse(server, (EntityHorse) entity); } + else { return new CraftAnimals(server, (EntityAnimal) entity); } + } + // Monsters + else if (entity instanceof EntityMonster) { + if (entity instanceof EntityZombie) { + if (entity instanceof EntityPigZombie) { return new CraftPigZombie(server, (EntityPigZombie) entity); } + else { return new CraftZombie(server, (EntityZombie) entity); } + } + else if (entity instanceof EntityCreeper) { return new CraftCreeper(server, (EntityCreeper) entity); } + else if (entity instanceof EntityEnderman) { return new CraftEnderman(server, (EntityEnderman) entity); } + else if (entity instanceof EntitySilverfish) { return new CraftSilverfish(server, (EntitySilverfish) entity); } + else if (entity instanceof EntityGiantZombie) { return new CraftGiant(server, (EntityGiantZombie) entity); } + else if (entity instanceof EntitySkeleton) { return new CraftSkeleton(server, (EntitySkeleton) entity); } + else if (entity instanceof EntityBlaze) { return new CraftBlaze(server, (EntityBlaze) entity); } + else if (entity instanceof EntityWitch) { return new CraftWitch(server, (EntityWitch) entity); } + else if (entity instanceof EntityWither) { return new CraftWither(server, (EntityWither) entity); } + else if (entity instanceof EntitySpider) { + if (entity instanceof EntityCaveSpider) { return new CraftCaveSpider(server, (EntityCaveSpider) entity); } + else { return new CraftSpider(server, (EntitySpider) entity); } + } + + else { return new CraftMonster(server, (EntityMonster) entity); } + } + // Water Animals + else if (entity instanceof EntityWaterAnimal) { + if (entity instanceof EntitySquid) { return new CraftSquid(server, (EntitySquid) entity); } + else { return new CraftWaterMob(server, (EntityWaterAnimal) entity); } + } + else if (entity instanceof EntityGolem) { + if (entity instanceof EntitySnowman) { return new CraftSnowman(server, (EntitySnowman) entity); } + else if (entity instanceof EntityIronGolem) { return new CraftIronGolem(server, (EntityIronGolem) entity); } + } + else if (entity instanceof EntityVillager) { return new CraftVillager(server, (EntityVillager) entity); } + else { return new CraftCreature(server, (EntityCreature) entity); } + } + // Slimes are a special (and broken) case + else if (entity instanceof EntitySlime) { + if (entity instanceof EntityMagmaCube) { return new CraftMagmaCube(server, (EntityMagmaCube) entity); } + else { return new CraftSlime(server, (EntitySlime) entity); } + } + // Flying + else if (entity instanceof EntityFlying) { + if (entity instanceof EntityGhast) { return new CraftGhast(server, (EntityGhast) entity); } + else { return new CraftFlying(server, (EntityFlying) entity); } + } + else if (entity instanceof EntityEnderDragon) { + return new CraftEnderDragon(server, (EntityEnderDragon) entity); + } + // Ambient + else if (entity instanceof EntityAmbient) { + if (entity instanceof EntityBat) { return new CraftBat(server, (EntityBat) entity); } + else { return new CraftAmbient(server, (EntityAmbient) entity); } + } + else { return new CraftLivingEntity(server, (EntityLiving) entity); } + } + else if (entity instanceof EntityComplexPart) { + EntityComplexPart part = (EntityComplexPart) entity; + if (part.owner instanceof EntityEnderDragon) { return new CraftEnderDragonPart(server, (EntityComplexPart) entity); } + else { return new CraftComplexPart(server, (EntityComplexPart) entity); } + } + else if (entity instanceof EntityExperienceOrb) { return new CraftExperienceOrb(server, (EntityExperienceOrb) entity); } + else if (entity instanceof EntityArrow) { return new CraftArrow(server, (EntityArrow) entity); } + else if (entity instanceof EntityBoat) { return new CraftBoat(server, (EntityBoat) entity); } + else if (entity instanceof EntityProjectile) { + if (entity instanceof EntityEgg) { return new CraftEgg(server, (EntityEgg) entity); } + else if (entity instanceof EntitySnowball) { return new CraftSnowball(server, (EntitySnowball) entity); } + else if (entity instanceof EntityPotion) { return new CraftThrownPotion(server, (EntityPotion) entity); } + else if (entity instanceof EntityEnderPearl) { return new CraftEnderPearl(server, (EntityEnderPearl) entity); } + else if (entity instanceof EntityThrownExpBottle) { return new CraftThrownExpBottle(server, (EntityThrownExpBottle) entity); } + } + else if (entity instanceof EntityFallingBlock) { return new CraftFallingSand(server, (EntityFallingBlock) entity); } + else if (entity instanceof EntityFireball) { + if (entity instanceof EntitySmallFireball) { return new CraftSmallFireball(server, (EntitySmallFireball) entity); } + else if (entity instanceof EntityLargeFireball) { return new CraftLargeFireball(server, (EntityLargeFireball) entity); } + else if (entity instanceof EntityWitherSkull) { return new CraftWitherSkull(server, (EntityWitherSkull) entity); } + else { return new CraftFireball(server, (EntityFireball) entity); } + } + else if (entity instanceof EntityEnderSignal) { return new CraftEnderSignal(server, (EntityEnderSignal) entity); } + else if (entity instanceof EntityEnderCrystal) { return new CraftEnderCrystal(server, (EntityEnderCrystal) entity); } + else if (entity instanceof EntityFishingHook) { return new CraftFish(server, (EntityFishingHook) entity); } + else if (entity instanceof EntityItem) { return new CraftItem(server, (EntityItem) entity); } + else if (entity instanceof EntityWeather) { + if (entity instanceof EntityLightning) { return new CraftLightningStrike(server, (EntityLightning) entity); } + else { return new CraftWeather(server, (EntityWeather) entity); } + } + else if (entity instanceof EntityMinecartAbstract) { + if (entity instanceof EntityMinecartFurnace) { return new CraftMinecartFurnace(server, (EntityMinecartFurnace) entity); } + else if (entity instanceof EntityMinecartChest) { return new CraftMinecartChest(server, (EntityMinecartChest) entity); } + else if (entity instanceof EntityMinecartTNT) { return new CraftMinecartTNT(server, (EntityMinecartTNT) entity); } + else if (entity instanceof EntityMinecartHopper) { return new CraftMinecartHopper(server, (EntityMinecartHopper) entity); } + else if (entity instanceof EntityMinecartMobSpawner) { return new CraftMinecartMobSpawner(server, (EntityMinecartMobSpawner) entity); } + else if (entity instanceof EntityMinecartRideable) { return new CraftMinecartRideable(server, (EntityMinecartRideable) entity); } + else if (entity instanceof EntityMinecartCommandBlock) { return new CraftMinecartCommand(server, (EntityMinecartCommandBlock) entity); } + } else if (entity instanceof EntityHanging) { + if (entity instanceof EntityPainting) { return new CraftPainting(server, (EntityPainting) entity); } + else if (entity instanceof EntityItemFrame) { return new CraftItemFrame(server, (EntityItemFrame) entity); } + else if (entity instanceof EntityLeash) { return new CraftLeash(server, (EntityLeash) entity); } + else { return new CraftHanging(server, (EntityHanging) entity); } + } + else if (entity instanceof EntityTNTPrimed) { return new CraftTNTPrimed(server, (EntityTNTPrimed) entity); } + else if (entity instanceof EntityFireworks) { return new CraftFirework(server, (EntityFireworks) entity); } + + throw new AssertionError("Unknown entity " + entity == null ? null : entity.getClass()); + } + + public Location getLocation() { + return new Location(getWorld(), entity.locX, entity.locY, entity.locZ, entity.yaw, entity.pitch); + } + + public Location getLocation(Location loc) { + if (loc != null) { + loc.setWorld(getWorld()); + loc.setX(entity.locX); + loc.setY(entity.locY); + loc.setZ(entity.locZ); + loc.setYaw(entity.yaw); + loc.setPitch(entity.pitch); + } + + return loc; + } + + public Vector getVelocity() { + return new Vector(entity.motX, entity.motY, entity.motZ); + } + + public void setVelocity(Vector vel) { + entity.motX = vel.getX(); + entity.motY = vel.getY(); + entity.motZ = vel.getZ(); + entity.velocityChanged = true; + } + + public boolean isOnGround() { + if (entity instanceof EntityArrow) { + return ((EntityArrow) entity).isInGround(); + } + return entity.onGround; + } + + public World getWorld() { + return entity.world.getWorld(); + } + + public boolean teleport(Location location) { + return teleport(location, TeleportCause.PLUGIN); + } + + public boolean teleport(Location location, TeleportCause cause) { + if (entity.passenger != null || entity.dead) { + return false; + } + + // If this entity is riding another entity, we must dismount before teleporting. + entity.mount(null); + + // Spigot start + if (!location.getWorld().equals(getWorld())) { + entity.teleportTo(location, cause.equals(TeleportCause.NETHER_PORTAL)); + return true; + } + + // entity.world = ((CraftWorld) location.getWorld()).getHandle(); + // Spigot end + entity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + // entity.setLocation() throws no event, and so cannot be cancelled + return true; + } + + public boolean teleport(org.bukkit.entity.Entity destination) { + return teleport(destination.getLocation()); + } + + public boolean teleport(org.bukkit.entity.Entity destination, TeleportCause cause) { + return teleport(destination.getLocation(), cause); + } + + public List getNearbyEntities(double x, double y, double z) { + @SuppressWarnings("unchecked") + List notchEntityList = entity.world.getEntities(entity, entity.boundingBox.grow(x, y, z)); + List bukkitEntityList = new java.util.ArrayList(notchEntityList.size()); + + for (Entity e : notchEntityList) { + bukkitEntityList.add(e.getBukkitEntity()); + } + return bukkitEntityList; + } + + public int getEntityId() { + return entity.getId(); + } + + public int getFireTicks() { + return entity.fireTicks; + } + + public int getMaxFireTicks() { + return entity.maxFireTicks; + } + + public void setFireTicks(int ticks) { + entity.fireTicks = ticks; + } + + public void remove() { + entity.dead = true; + } + + public boolean isDead() { + return !entity.isAlive(); + } + + public boolean isValid() { + return entity.isAlive() && entity.valid; + } + + public Server getServer() { + return server; + } + + public Vector getMomentum() { + return getVelocity(); + } + + public void setMomentum(Vector value) { + setVelocity(value); + } + + public org.bukkit.entity.Entity getPassenger() { + return isEmpty() ? null : getHandle().passenger.getBukkitEntity(); + } + + public boolean setPassenger(org.bukkit.entity.Entity passenger) { + if (passenger instanceof CraftEntity) { + ((CraftEntity) passenger).getHandle().setPassengerOf(getHandle()); + return true; + } else { + return false; + } + } + + public boolean isEmpty() { + return getHandle().passenger == null; + } + + public boolean eject() { + if (getHandle().passenger == null) { + return false; + } + + getHandle().passenger.setPassengerOf(null); + return true; + } + + public float getFallDistance() { + return getHandle().fallDistance; + } + + public void setFallDistance(float distance) { + getHandle().fallDistance = distance; + } + + public void setLastDamageCause(EntityDamageEvent event) { + lastDamageEvent = event; + } + + public EntityDamageEvent getLastDamageCause() { + return lastDamageEvent; + } + + public UUID getUniqueId() { + return getHandle().uniqueID; + } + + public int getTicksLived() { + return getHandle().ticksLived; + } + + public void setTicksLived(int value) { + if (value <= 0) { + throw new IllegalArgumentException("Age must be at least 1 tick"); + } + getHandle().ticksLived = value; + } + + public Entity getHandle() { + return entity; + } + + public void playEffect(EntityEffect type) { + this.getHandle().world.broadcastEntityEffect(getHandle(), type.getData()); + } + + public void setHandle(final Entity entity) { + this.entity = entity; + } + + @Override + public String toString() { + return "CraftEntity{" + "id=" + getEntityId() + '}'; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CraftEntity other = (CraftEntity) obj; + return (this.getEntityId() == other.getEntityId()); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 29 * hash + this.getEntityId(); + return hash; + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + server.getEntityMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + return server.getEntityMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return server.getEntityMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + server.getEntityMetadata().removeMetadata(this, metadataKey, owningPlugin); + } + + public boolean isInsideVehicle() { + return getHandle().vehicle != null; + } + + public boolean leaveVehicle() { + if (getHandle().vehicle == null) { + return false; + } + + getHandle().setPassengerOf(null); + return true; + } + + public org.bukkit.entity.Entity getVehicle() { + if (getHandle().vehicle == null) { + return null; + } + + return getHandle().vehicle.getBukkitEntity(); + } + + // Spigot start + private final Spigot spigot = new Spigot() + { + @Override + public boolean isInvulnerable() + { + return getHandle().isInvulnerable(); + } + }; + + public Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java new file mode 100644 index 0000000..3a09cab --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java @@ -0,0 +1,34 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityExperienceOrb; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ExperienceOrb; + +public class CraftExperienceOrb extends CraftEntity implements ExperienceOrb { + public CraftExperienceOrb(CraftServer server, EntityExperienceOrb entity) { + super(server, entity); + } + + public int getExperience() { + return getHandle().value; + } + + public void setExperience(int value) { + getHandle().value = value; + } + + @Override + public EntityExperienceOrb getHandle() { + return (EntityExperienceOrb) entity; + } + + @Override + public String toString() { + return "CraftExperienceOrb"; + } + + public EntityType getType() { + return EntityType.EXPERIENCE_ORB; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingSand.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingSand.java new file mode 100644 index 0000000..830f5bb --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingSand.java @@ -0,0 +1,57 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFallingBlock; + +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.FallingSand; + +public class CraftFallingSand extends CraftEntity implements FallingSand { + + public CraftFallingSand(CraftServer server, EntityFallingBlock entity) { + super(server, entity); + } + + @Override + public EntityFallingBlock getHandle() { + return (EntityFallingBlock) entity; + } + + @Override + public String toString() { + return "CraftFallingSand"; + } + + public EntityType getType() { + return EntityType.FALLING_BLOCK; + } + + public Material getMaterial() { + return Material.getMaterial(getBlockId()); + } + + public int getBlockId() { + return CraftMagicNumbers.getId(getHandle().id); + } + + public byte getBlockData() { + return (byte) getHandle().data; + } + + public boolean getDropItem() { + return getHandle().dropItem; + } + + public void setDropItem(boolean drop) { + getHandle().dropItem = drop; + } + + // PaperSpigot start - Add FallingBlock and TNT source location API + @Override + public org.bukkit.Location getSourceLoc() { + return getHandle().sourceLoc; + } + // PaperSpigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java new file mode 100644 index 0000000..6f0b942 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java @@ -0,0 +1,89 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFireball; +import net.minecraft.server.MathHelper; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.LivingEntity; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.util.Vector; + +public class CraftFireball extends AbstractProjectile implements Fireball { + public CraftFireball(CraftServer server, EntityFireball entity) { + super(server, entity); + } + + public float getYield() { + return getHandle().bukkitYield; + } + + public boolean isIncendiary() { + return getHandle().isIncendiary; + } + + public void setIsIncendiary(boolean isIncendiary) { + getHandle().isIncendiary = isIncendiary; + } + + public void setYield(float yield) { + getHandle().bukkitYield = yield; + } + + public ProjectileSource getShooter() { + return getHandle().projectileSource; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof CraftLivingEntity) { + getHandle().shooter = ((CraftLivingEntity) shooter).getHandle(); + } else { + getHandle().shooter = null; + } + getHandle().projectileSource = shooter; + } + + public Vector getDirection() { + return new Vector(getHandle().dirX, getHandle().dirY, getHandle().dirZ); + } + + public void setDirection(Vector direction) { + Validate.notNull(direction, "Direction can not be null"); + double x = direction.getX(); + double y = direction.getY(); + double z = direction.getZ(); + double magnitude = (double) MathHelper.sqrt(x * x + y * y + z * z); + getHandle().dirX = x / magnitude; + getHandle().dirY = y / magnitude; + getHandle().dirZ = z / magnitude; + } + + @Override + public EntityFireball getHandle() { + return (EntityFireball) entity; + } + + @Override + public String toString() { + return "CraftFireball"; + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } + + @Deprecated + public void _INVALID_setShooter(LivingEntity shooter) { + setShooter(shooter); + } + + @Deprecated + public LivingEntity _INVALID_getShooter() { + if (getHandle().shooter != null) { + return (LivingEntity) getHandle().shooter.getBukkitEntity(); + } + return null; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java new file mode 100644 index 0000000..76a9bdb --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java @@ -0,0 +1,74 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFireworks; +import net.minecraft.server.ItemStack; +import net.minecraft.server.Items; + +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Firework; +import org.bukkit.inventory.meta.FireworkMeta; + +import java.util.Random; + +public class CraftFirework extends CraftEntity implements Firework { + private static final int FIREWORK_ITEM_INDEX = 8; + + private final Random random = new Random(); + private final CraftItemStack item; + + public CraftFirework(CraftServer server, EntityFireworks entity) { + super(server, entity); + + ItemStack item = getHandle().getDataWatcher().getItemStack(FIREWORK_ITEM_INDEX); + + if (item == null) { + item = new ItemStack(Items.FIREWORKS); + getHandle().getDataWatcher().watch(FIREWORK_ITEM_INDEX, item); + } + + this.item = CraftItemStack.asCraftMirror(item); + + // Ensure the item is a firework... + if (this.item.getType() != Material.FIREWORK) { + this.item.setType(Material.FIREWORK); + } + } + + @Override + public EntityFireworks getHandle() { + return (EntityFireworks) entity; + } + + @Override + public String toString() { + return "CraftFirework"; + } + + @Override + public EntityType getType() { + return EntityType.FIREWORK; + } + + @Override + public FireworkMeta getFireworkMeta() { + return (FireworkMeta) item.getItemMeta(); + } + + @Override + public void setFireworkMeta(FireworkMeta meta) { + item.setItemMeta(meta); + + // Copied from EntityFireworks constructor, update firework lifetime/power + getHandle().expectedLifespan = 10 * (1 + meta.getPower()) + random.nextInt(6) + random.nextInt(7); + + getHandle().getDataWatcher().update(FIREWORK_ITEM_INDEX); + } + + @Override + public void detonate() { + getHandle().expectedLifespan = 0; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java new file mode 100644 index 0000000..edb30e7 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java @@ -0,0 +1,75 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFishingHook; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.MathHelper; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Fish; +import org.bukkit.entity.LivingEntity; +import org.bukkit.projectiles.ProjectileSource; + +public class CraftFish extends AbstractProjectile implements Fish { + private double biteChance = -1; + + public CraftFish(CraftServer server, EntityFishingHook entity) { + super(server, entity); + } + + public ProjectileSource getShooter() { + if (getHandle().owner != null) { + return getHandle().owner.getBukkitEntity(); + } + + return null; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof CraftHumanEntity) { + getHandle().owner = (EntityHuman) ((CraftHumanEntity) shooter).entity; + } + } + + @Override + public EntityFishingHook getHandle() { + return (EntityFishingHook) entity; + } + + @Override + public String toString() { + return "CraftFish"; + } + + public EntityType getType() { + return EntityType.FISHING_HOOK; + } + + public double getBiteChance() { + EntityFishingHook hook = getHandle(); + + if (this.biteChance == -1) { + if (hook.world.isRainingAt(MathHelper.floor(hook.locX), MathHelper.floor(hook.locY) + 1, MathHelper.floor(hook.locZ))) { + return 1/300.0; + } + return 1/500.0; + } + return this.biteChance; + } + + public void setBiteChance(double chance) { + Validate.isTrue(chance >= 0 && chance <= 1, "The bite chance must be between 0 and 1."); + this.biteChance = chance; + } + + @Deprecated + public LivingEntity _INVALID_getShooter() { + return (LivingEntity) getShooter(); + } + + @Deprecated + public void _INVALID_setShooter(LivingEntity shooter) { + setShooter(shooter); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFlying.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFlying.java new file mode 100644 index 0000000..f374c7b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFlying.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFlying; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Flying; + +public class CraftFlying extends CraftLivingEntity implements Flying { + + public CraftFlying(CraftServer server, EntityFlying entity) { + super(server, entity); + } + + @Override + public EntityFlying getHandle() { + return (EntityFlying) entity; + } + + @Override + public String toString() { + return "CraftFlying"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java new file mode 100644 index 0000000..ee9516f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGhast; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Ghast; + +public class CraftGhast extends CraftFlying implements Ghast { + + public CraftGhast(CraftServer server, EntityGhast entity) { + super(server, entity); + } + + @Override + public EntityGhast getHandle() { + return (EntityGhast) entity; + } + + @Override + public String toString() { + return "CraftGhast"; + } + + public EntityType getType() { + return EntityType.GHAST; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGiant.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGiant.java new file mode 100644 index 0000000..e560913 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGiant.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGiantZombie; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Giant; + +public class CraftGiant extends CraftMonster implements Giant { + + public CraftGiant(CraftServer server, EntityGiantZombie entity) { + super(server, entity); + } + + @Override + public EntityGiantZombie getHandle() { + return (EntityGiantZombie) entity; + } + + @Override + public String toString() { + return "CraftGiant"; + } + + public EntityType getType() { + return EntityType.GIANT; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGolem.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGolem.java new file mode 100644 index 0000000..1fef5e0 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGolem.java @@ -0,0 +1,21 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGolem; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Golem; + +public class CraftGolem extends CraftCreature implements Golem { + public CraftGolem(CraftServer server, EntityGolem entity) { + super(server, entity); + } + + @Override + public EntityGolem getHandle() { + return (EntityGolem) entity; + } + + @Override + public String toString() { + return "CraftGolem"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java new file mode 100644 index 0000000..e51dddb --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java @@ -0,0 +1,83 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHanging; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Hanging; + +public class CraftHanging extends CraftEntity implements Hanging { + public CraftHanging(CraftServer server, EntityHanging entity) { + super(server, entity); + } + + public BlockFace getAttachedFace() { + return getFacing().getOppositeFace(); + } + + public void setFacingDirection(BlockFace face) { + setFacingDirection(face, false); + } + + public boolean setFacingDirection(BlockFace face, boolean force) { + Block block = getLocation().getBlock().getRelative(getAttachedFace()).getRelative(face.getOppositeFace()).getRelative(getFacing()); + EntityHanging hanging = getHandle(); + int x = hanging.x, y = hanging.y, z = hanging.z, dir = hanging.direction; + hanging.x = block.getX(); + hanging.y = block.getY(); + hanging.z = block.getZ(); + switch (face) { + case SOUTH: + default: + getHandle().setDirection(0); + break; + case WEST: + getHandle().setDirection(1); + break; + case NORTH: + getHandle().setDirection(2); + break; + case EAST: + getHandle().setDirection(3); + break; + } + if (!force && !hanging.survives()) { + // Revert since it doesn't fit + hanging.x = x; + hanging.y = y; + hanging.z = z; + hanging.setDirection(dir); + return false; + } + return true; + } + + public BlockFace getFacing() { + switch (this.getHandle().direction) { + case 0: + default: + return BlockFace.SOUTH; + case 1: + return BlockFace.WEST; + case 2: + return BlockFace.NORTH; + case 3: + return BlockFace.EAST; + } + } + + @Override + public EntityHanging getHandle() { + return (EntityHanging) entity; + } + + @Override + public String toString() { + return "CraftHanging"; + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHorse.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHorse.java new file mode 100644 index 0000000..8522cad --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHorse.java @@ -0,0 +1,146 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHorse; +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventoryHorse; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse; +import org.bukkit.inventory.HorseInventory; + +import java.util.UUID; + +public class CraftHorse extends CraftAnimals implements Horse { + + public CraftHorse(CraftServer server, EntityHorse entity) { + super(server, entity); + } + + @Override + public EntityHorse getHandle() { + return (EntityHorse) entity; + } + + public Variant getVariant() { + return Variant.values()[getHandle().getType()]; + } + + public void setVariant(Variant variant) { + Validate.notNull(variant, "Variant cannot be null"); + getHandle().setType(variant.ordinal()); + } + + public Color getColor() { + return Color.values()[getHandle().getVariant() & 0xFF]; + } + + public void setColor(Color color) { + Validate.notNull(color, "Color cannot be null"); + getHandle().setVariant(color.ordinal() & 0xFF | getStyle().ordinal() << 8); + } + + public Style getStyle() { + return Style.values()[getHandle().getVariant() >>> 8]; + } + + public void setStyle(Style style) { + Validate.notNull(style, "Style cannot be null"); + getHandle().setVariant(getColor().ordinal() & 0xFF | style.ordinal() << 8); + } + + public boolean isCarryingChest() { + return getHandle().hasChest(); + } + + public void setCarryingChest(boolean chest) { + if (chest == isCarryingChest()) return; + getHandle().setHasChest(chest); + getHandle().loadChest(); + } + + public int getDomestication() { + return getHandle().getTemper(); + } + + public void setDomestication(int value) { + Validate.isTrue(value >= 0, "Domestication cannot be less than zero"); + Validate.isTrue(value <= getMaxDomestication(), "Domestication cannot be greater than the max domestication"); + getHandle().setTemper(value); + } + + public int getMaxDomestication() { + return getHandle().getMaxDomestication(); + } + + public void setMaxDomestication(int value) { + Validate.isTrue(value > 0, "Max domestication cannot be zero or less"); + getHandle().maxDomestication = value; + } + + public double getJumpStrength() { + return getHandle().getJumpStrength(); + } + + public void setJumpStrength(double strength) { + Validate.isTrue(strength >= 0, "Jump strength cannot be less than zero"); + getHandle().getAttributeInstance(EntityHorse.attributeJumpStrength).setValue(strength); + } + + @Override + public boolean isTamed() { + return getHandle().isTame(); + } + + @Override + public void setTamed(boolean tamed) { + getHandle().setTame(tamed); + } + + @Override + public AnimalTamer getOwner() { + if (getOwnerUUID() == null) return null; + return getServer().getOfflinePlayer(getOwnerUUID()); + } + + @Override + public void setOwner(AnimalTamer owner) { + if (owner != null) { + setTamed(true); + getHandle().setPathEntity(null); + setOwnerUUID(owner.getUniqueId()); + } else { + setTamed(false); + setOwnerUUID(null); + } + } + + public UUID getOwnerUUID() { + try { + return UUID.fromString(getHandle().getOwnerUUID()); + } catch (IllegalArgumentException ex) { + return null; + } + } + + public void setOwnerUUID(UUID uuid) { + if (uuid == null) { + getHandle().setOwnerUUID(""); + } else { + getHandle().setOwnerUUID(uuid.toString()); + } + } + + public HorseInventory getInventory() { + return new CraftInventoryHorse(getHandle().inventoryChest); + } + + @Override + public String toString() { + return "CraftHorse{variant=" + getVariant() + ", owner=" + getOwner() + '}'; + } + + public EntityType getType() { + return EntityType.HORSE; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java new file mode 100644 index 0000000..3d1ca3d --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -0,0 +1,337 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.Set; + +import net.minecraft.server.Container; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityMinecartHopper; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.PacketPlayInCloseWindow; +import net.minecraft.server.PacketPlayOutOpenWindow; +import net.minecraft.server.TileEntityBrewingStand; +import net.minecraft.server.TileEntityDispenser; +import net.minecraft.server.TileEntityFurnace; +import net.minecraft.server.TileEntityHopper; + +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftContainer; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryPlayer; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { + private CraftInventoryPlayer inventory; + private final CraftInventory enderChest; + protected final PermissibleBase perm = new PermissibleBase(this); + private boolean op; + private GameMode mode; + + public CraftHumanEntity(final CraftServer server, final EntityHuman entity) { + super(server, entity); + mode = server.getDefaultGameMode(); + this.inventory = new CraftInventoryPlayer(entity.inventory); + enderChest = new CraftInventory(entity.getEnderChest()); + } + + public String getName() { + return getHandle().getName(); + } + + public PlayerInventory getInventory() { + return inventory; + } + + public EntityEquipment getEquipment() { + return inventory; + } + + public Inventory getEnderChest() { + return enderChest; + } + + public ItemStack getItemInHand() { + return getInventory().getItemInHand(); + } + + public void setItemInHand(ItemStack item) { + getInventory().setItemInHand(item); + } + + public ItemStack getItemOnCursor() { + return CraftItemStack.asCraftMirror(getHandle().inventory.getCarried()); + } + + public void setItemOnCursor(ItemStack item) { + net.minecraft.server.ItemStack stack = CraftItemStack.asNMSCopy(item); + getHandle().inventory.setCarried(stack); + if (this instanceof CraftPlayer) { + ((EntityPlayer) getHandle()).broadcastCarriedItem(); // Send set slot for cursor + } + } + + public boolean isSleeping() { + return getHandle().sleeping; + } + + public int getSleepTicks() { + return getHandle().sleepTicks; + } + + public boolean isOp() { + return op; + } + + public boolean isPermissionSet(String name) { + return perm.isPermissionSet(name); + } + + public boolean isPermissionSet(Permission perm) { + return this.perm.isPermissionSet(perm); + } + + public boolean hasPermission(String name) { + return perm.hasPermission(name); + } + + public boolean hasPermission(Permission perm) { + return this.perm.hasPermission(perm); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return perm.addAttachment(plugin, name, value); + } + + public PermissionAttachment addAttachment(Plugin plugin) { + return perm.addAttachment(plugin); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return perm.addAttachment(plugin, name, value, ticks); + } + + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return perm.addAttachment(plugin, ticks); + } + + public void removeAttachment(PermissionAttachment attachment) { + perm.removeAttachment(attachment); + } + + public void recalculatePermissions() { + perm.recalculatePermissions(); + } + + public void setOp(boolean value) { + this.op = value; + perm.recalculatePermissions(); + } + + public Set getEffectivePermissions() { + return perm.getEffectivePermissions(); + } + + public GameMode getGameMode() { + return mode; + } + + public void setGameMode(GameMode mode) { + if (mode == null) { + throw new IllegalArgumentException("Mode cannot be null"); + } + + this.mode = mode; + } + + @Override + public EntityHuman getHandle() { + return (EntityHuman) entity; + } + + public void setHandle(final EntityHuman entity) { + super.setHandle(entity); + this.inventory = new CraftInventoryPlayer(entity.inventory); + } + + @Override + public String toString() { + return "CraftHumanEntity{" + "id=" + getEntityId() + "name=" + getName() + '}'; + } + + public InventoryView getOpenInventory() { + return getHandle().activeContainer.getBukkitView(); + } + + public InventoryView openInventory(Inventory inventory) { + if(!(getHandle() instanceof EntityPlayer)) return null; + EntityPlayer player = (EntityPlayer) getHandle(); + InventoryType type = inventory.getType(); + Container formerContainer = getHandle().activeContainer; + // TODO: Should we check that it really IS a CraftInventory first? + CraftInventory craftinv = (CraftInventory) inventory; + switch(type) { + case PLAYER: + case CHEST: + case ENDER_CHEST: + getHandle().openContainer(craftinv.getInventory()); + break; + case DISPENSER: + if (craftinv.getInventory() instanceof TileEntityDispenser) { + getHandle().openDispenser((TileEntityDispenser) craftinv.getInventory()); + } else { + openCustomInventory(inventory, player, 3); + } + break; + case FURNACE: + if (craftinv.getInventory() instanceof TileEntityFurnace) { + getHandle().openFurnace((TileEntityFurnace) craftinv.getInventory()); + } else { + openCustomInventory(inventory, player, 2); + } + break; + case WORKBENCH: + openCustomInventory(inventory, player, 1); + break; + case BREWING: + if (craftinv.getInventory() instanceof TileEntityBrewingStand) { + getHandle().openBrewingStand((TileEntityBrewingStand) craftinv.getInventory()); + } else { + openCustomInventory(inventory, player, 5); + } + break; + case ENCHANTING: + openCustomInventory(inventory, player, 4); + break; + case HOPPER: + if (craftinv.getInventory() instanceof TileEntityHopper) { + getHandle().openHopper((TileEntityHopper) craftinv.getInventory()); + } else if (craftinv.getInventory() instanceof EntityMinecartHopper) { + getHandle().openMinecartHopper((EntityMinecartHopper) craftinv.getInventory()); + } else { + openCustomInventory(inventory, player, 9); + } + break; + case CREATIVE: + case CRAFTING: + throw new IllegalArgumentException("Can't open a " + type + " inventory!"); + } + if (getHandle().activeContainer == formerContainer) { + return null; + } + getHandle().activeContainer.checkReachable = false; + return getHandle().activeContainer.getBukkitView(); + } + + private void openCustomInventory(Inventory inventory, EntityPlayer player, int windowType) { + if (player.playerConnection == null) return; + Container container = new CraftContainer(inventory, this, player.nextContainerCounter()); + + container = CraftEventFactory.callInventoryOpenEvent(player, container); + if(container == null) return; + + String title = container.getBukkitView().getTitle(); + int size = container.getBukkitView().getTopInventory().getSize(); + + player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, title, size, true)); + getHandle().activeContainer = container; + getHandle().activeContainer.addSlotListener(player); + } + + public InventoryView openWorkbench(Location location, boolean force) { + if (!force) { + Block block = location.getBlock(); + if (block.getType() != Material.WORKBENCH) { + return null; + } + } + if (location == null) { + location = getLocation(); + } + getHandle().startCrafting(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + if (force) { + getHandle().activeContainer.checkReachable = false; + } + return getHandle().activeContainer.getBukkitView(); + } + + public InventoryView openEnchanting(Location location, boolean force) { + if (!force) { + Block block = location.getBlock(); + if (block.getType() != Material.ENCHANTMENT_TABLE) { + return null; + } + } + if (location == null) { + location = getLocation(); + } + getHandle().startEnchanting(location.getBlockX(), location.getBlockY(), location.getBlockZ(), null); + if (force) { + getHandle().activeContainer.checkReachable = false; + } + return getHandle().activeContainer.getBukkitView(); + } + + public void openInventory(InventoryView inventory) { + if (!(getHandle() instanceof EntityPlayer)) return; // TODO: NPC support? + if (((EntityPlayer) getHandle()).playerConnection == null) return; + if (getHandle().activeContainer != getHandle().defaultContainer) { + // fire INVENTORY_CLOSE if one already open + ((EntityPlayer)getHandle()).playerConnection.a(new PacketPlayInCloseWindow(getHandle().activeContainer.windowId)); + } + EntityPlayer player = (EntityPlayer) getHandle(); + Container container; + if (inventory instanceof CraftInventoryView) { + container = ((CraftInventoryView) inventory).getHandle(); + } else { + container = new CraftContainer(inventory, player.nextContainerCounter()); + } + + // Trigger an INVENTORY_OPEN event + container = CraftEventFactory.callInventoryOpenEvent(player, container); + if (container == null) { + return; + } + + // Now open the window + InventoryType type = inventory.getType(); + int windowType = CraftContainer.getNotchInventoryType(type); + String title = inventory.getTitle(); + int size = inventory.getTopInventory().getSize(); + player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, title, size, false)); + player.activeContainer = container; + player.activeContainer.addSlotListener(player); + } + + public void closeInventory() { + getHandle().closeInventory(); + } + + public boolean isBlocking() { + return getHandle().isBlocking(); + } + + public boolean setWindowProperty(InventoryView.Property prop, int value) { + return false; + } + + public int getExpToLevel() { + return getHandle().getExpToLevel(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java new file mode 100644 index 0000000..ddee207 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java @@ -0,0 +1,35 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityIronGolem; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.IronGolem; + +public class CraftIronGolem extends CraftGolem implements IronGolem { + public CraftIronGolem(CraftServer server, EntityIronGolem entity) { + super(server, entity); + } + + @Override + public EntityIronGolem getHandle() { + return (EntityIronGolem) entity; + } + + @Override + public String toString() { + return "CraftIronGolem"; + } + + public boolean isPlayerCreated() { + return getHandle().isPlayerCreated(); + } + + public void setPlayerCreated(boolean playerCreated) { + getHandle().setPlayerCreated(playerCreated); + } + + @Override + public EntityType getType() { + return EntityType.IRON_GOLEM; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java new file mode 100644 index 0000000..02b32cf --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityItem; + +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Item; +import org.bukkit.inventory.ItemStack; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.CraftServer; + +public class CraftItem extends CraftEntity implements Item { + private final EntityItem item; + + public CraftItem(CraftServer server, Entity entity, EntityItem item) { + super(server, entity); + this.item = item; + } + + public CraftItem(CraftServer server, EntityItem entity) { + this(server, entity, entity); + } + + public ItemStack getItemStack() { + return CraftItemStack.asCraftMirror(item.getItemStack()); + } + + public void setItemStack(ItemStack stack) { + item.setItemStack(CraftItemStack.asNMSCopy(stack)); + } + + public int getPickupDelay() { + return item.pickupDelay; + } + + public void setPickupDelay(int delay) { + item.pickupDelay = delay; + } + + @Override + public String toString() { + return "CraftItem"; + } + + public EntityType getType() { + return EntityType.DROPPED_ITEM; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java new file mode 100644 index 0000000..3f4e1ae --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java @@ -0,0 +1,99 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityItemFrame; +import net.minecraft.server.WorldServer; + +import org.apache.commons.lang.Validate; + +import org.bukkit.Rotation; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ItemFrame; + +public class CraftItemFrame extends CraftHanging implements ItemFrame { + public CraftItemFrame(CraftServer server, EntityItemFrame entity) { + super(server, entity); + } + + public boolean setFacingDirection(BlockFace face, boolean force) { + if (!super.setFacingDirection(face, force)) { + return false; + } + + WorldServer world = ((CraftWorld) this.getWorld()).getHandle(); + world.getTracker().untrackEntity(this.getHandle()); + world.getTracker().track(this.getHandle()); + return true; + } + + public void setItem(org.bukkit.inventory.ItemStack item) { + if (item == null || item.getTypeId() == 0) { + getHandle().getDataWatcher().add(2, 5); + getHandle().getDataWatcher().update(2); + } else { + getHandle().setItem(CraftItemStack.asNMSCopy(item)); + } + } + + public org.bukkit.inventory.ItemStack getItem() { + return CraftItemStack.asBukkitCopy(getHandle().getItem()); + } + + public Rotation getRotation() { + return toBukkitRotation(getHandle().getRotation()); + } + + Rotation toBukkitRotation(int value) { + // Translate NMS rotation integer to Bukkit API + switch (value) { + case 0: + return Rotation.NONE; + case 1: + return Rotation.CLOCKWISE; + case 2: + return Rotation.FLIPPED; + case 3: + return Rotation.COUNTER_CLOCKWISE; + default: + throw new AssertionError("Unknown rotation " + value + " for " + getHandle()); + } + } + + public void setRotation(Rotation rotation) { + Validate.notNull(rotation, "Rotation cannot be null"); + getHandle().setRotation(toInteger(rotation)); + } + + static int toInteger(Rotation rotation) { + // Translate Bukkit API rotation to NMS integer + switch (rotation) { + case NONE: + return 0; + case CLOCKWISE: + return 1; + case FLIPPED: + return 2; + case COUNTER_CLOCKWISE: + return 3; + default: + throw new IllegalArgumentException(rotation + " is not applicable to an ItemFrame"); + } + } + + @Override + public EntityItemFrame getHandle() { + return (EntityItemFrame) entity; + } + + @Override + public String toString() { + return "CraftItemFrame{item=" + getItem() + ", rotation=" + getRotation() + "}"; + } + + public EntityType getType() { + return EntityType.ITEM_FRAME; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLargeFireball.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLargeFireball.java new file mode 100644 index 0000000..ca03794 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLargeFireball.java @@ -0,0 +1,32 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLargeFireball; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LargeFireball; + +public class CraftLargeFireball extends CraftFireball implements LargeFireball { + public CraftLargeFireball(CraftServer server, EntityLargeFireball entity) { + super(server, entity); + } + + @Override + public void setYield(float yield) { + super.setYield(yield); + getHandle().yield = (int) yield; + } + + @Override + public EntityLargeFireball getHandle() { + return (EntityLargeFireball) entity; + } + + @Override + public String toString() { + return "CraftLargeFireball"; + } + + public EntityType getType() { + return EntityType.FIREBALL; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java new file mode 100644 index 0000000..710ed7a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLeash; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LeashHitch; + +public class CraftLeash extends CraftHanging implements LeashHitch { + public CraftLeash(CraftServer server, EntityLeash entity) { + super(server, entity); + } + + @Override + public EntityLeash getHandle() { + return (EntityLeash) entity; + } + + @Override + public String toString() { + return "CraftLeash"; + } + + public EntityType getType() { + return EntityType.LEASH_HITCH; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLightningStrike.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLightningStrike.java new file mode 100644 index 0000000..be4f10f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLightningStrike.java @@ -0,0 +1,50 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLightning; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LightningStrike; + +public class CraftLightningStrike extends CraftEntity implements LightningStrike { + public CraftLightningStrike(final CraftServer server, final EntityLightning entity) { + super(server, entity); + } + + public boolean isEffect() { + return ((EntityLightning) super.getHandle()).isEffect; + } + + @Override + public EntityLightning getHandle() { + return (EntityLightning) entity; + } + + @Override + public String toString() { + return "CraftLightningStrike"; + } + + public EntityType getType() { + return EntityType.LIGHTNING; + } + + // Spigot start + private final LightningStrike.Spigot spigot = new LightningStrike.Spigot() + { + + @Override + public boolean isSilent() + { + return getHandle().isSilent; + } + + }; + + public LightningStrike.Spigot spigot() + { + return this.spigot; + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java new file mode 100644 index 0000000..82f6a82 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -0,0 +1,483 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.*; +import net.valorhcf.knockback.Knockback; +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftEntityEquipment; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Entity; +import org.bukkit.entity.*; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.BlockIterator; +import org.bukkit.util.NumberConversions; +import org.bukkit.util.Vector; + +import java.util.*; + +public class CraftLivingEntity extends CraftEntity implements LivingEntity { + + // joeleoli start + @Override + public Knockback getKbProfile() { + return getHandle().getKnockback(); + } + + @Override + public void setKbProfile(Knockback profile) { + getHandle().setKbProfile(profile); + } + // joeleoli end + + private CraftEntityEquipment equipment; + + public CraftLivingEntity(final CraftServer server, final EntityLiving entity) { + super(server, entity); + + if (entity instanceof EntityInsentient) { + equipment = new CraftEntityEquipment(this); + } + } + + public double getHealth() { + return Math.min(Math.max(0, getHandle().getHealth()), getMaxHealth()); + } + + public void setHealth(double health) { + if ((health < 0) || (health > getMaxHealth())) { + throw new IllegalArgumentException("Health must be between 0 and " + getMaxHealth()); + } + + getHandle().setHealth((float) health); + + if (entity instanceof EntityPlayer && health == 0) { + ((EntityPlayer) entity).die(DamageSource.GENERIC); + } + } + + public double getMaxHealth() { + return getHandle().getMaxHealth(); + } + + public void setMaxHealth(double amount) { + Validate.isTrue(amount > 0, "Max health must be greater than 0"); + + getHandle().getAttributeInstance(GenericAttributes.maxHealth).setValue(amount); + + if (getHealth() > amount) { + setHealth(amount); + } + } + + public void resetMaxHealth() { + setMaxHealth(getHandle().getMaxHealth()); + } + + @Deprecated + public Egg throwEgg() { + return launchProjectile(Egg.class); + } + + @Deprecated + public Snowball throwSnowball() { + return launchProjectile(Snowball.class); + } + + public double getEyeHeight() { + return getHandle().getHeadHeight(); + } + + public double getEyeHeight(boolean ignoreSneaking) { + return getEyeHeight(); + } + + private List getLineOfSight(HashSet transparent, int maxDistance, int maxLength) { + if (maxDistance > 120) { + maxDistance = 120; + } + ArrayList blocks = new ArrayList(); + Iterator itr = new BlockIterator(this, maxDistance); + while (itr.hasNext()) { + Block block = itr.next(); + blocks.add(block); + if (maxLength != 0 && blocks.size() > maxLength) { + blocks.remove(0); + } + int id = block.getTypeId(); + if (transparent == null) { + if (id != 0) { + break; + } + } else { + if (!transparent.contains((byte) id)) { + break; + } + } + } + return blocks; + } + + public List getLineOfSight(HashSet transparent, int maxDistance) { + return getLineOfSight(transparent, maxDistance, 0); + } + + public Block getTargetBlock(HashSet transparent, int maxDistance) { + List blocks = getLineOfSight(transparent, maxDistance, 1); + return blocks.get(0); + } + + public List getLastTwoTargetBlocks(HashSet transparent, int maxDistance) { + return getLineOfSight(transparent, maxDistance, 2); + } + + @Deprecated + public Arrow shootArrow() { + return launchProjectile(Arrow.class); + } + + public int getRemainingAir() { + return getHandle().getAirTicks(); + } + + public void setRemainingAir(int ticks) { + getHandle().setAirTicks(ticks); + } + + public int getMaximumAir() { + return getHandle().maxAirTicks; + } + + public void setMaximumAir(int ticks) { + getHandle().maxAirTicks = ticks; + } + + public void damage(double amount) { + damage(amount, null); + } + + public void damage(double amount, org.bukkit.entity.Entity source) { + DamageSource reason = DamageSource.GENERIC; + + if (source instanceof HumanEntity) { + reason = DamageSource.playerAttack(((CraftHumanEntity) source).getHandle()); + } else if (source instanceof LivingEntity) { + reason = DamageSource.mobAttack(((CraftLivingEntity) source).getHandle()); + } + + if (entity instanceof EntityEnderDragon) { + ((EntityEnderDragon) entity).dealDamage(reason, (float) amount); + } else { + entity.damageEntity(reason, (float) amount); + } + } + + public Location getEyeLocation() { + Location loc = getLocation(); + loc.setY(loc.getY() + getEyeHeight()); + return loc; + } + + public int getMaximumNoDamageTicks() { + return getHandle().maxNoDamageTicks; + } + + public void setMaximumNoDamageTicks(int ticks) { + getHandle().maxNoDamageTicks = ticks; + } + + public double getLastDamage() { + return getHandle().lastDamage; + } + + public void setLastDamage(double damage) { + getHandle().lastDamage = (float) damage; + } + + public int getNoDamageTicks() { + return getHandle().noDamageTicks; + } + + public void setNoDamageTicks(int ticks) { + getHandle().noDamageTicks = ticks; + } + + @Override + public EntityLiving getHandle() { + return (EntityLiving) entity; + } + + public void setHandle(final EntityLiving entity) { + super.setHandle(entity); + } + + @Override + public String toString() { + return "CraftLivingEntity{" + "id=" + getEntityId() + '}'; + } + + public Player getKiller() { + return getHandle().killer == null ? null : (Player) getHandle().killer.getBukkitEntity(); + } + + public boolean addPotionEffect(PotionEffect effect) { + return addPotionEffect(effect, false); + } + + public boolean addPotionEffect(PotionEffect effect, boolean force) { + if (hasPotionEffect(effect.getType())) { + if (!force) { + return false; + } + removePotionEffect(effect.getType()); + } + + getHandle().addEffect(new MobEffect(effect.getType().getId(), effect.getDuration(), effect.getAmplifier(), effect.isAmbient())); + return true; + } + + public boolean addPotionEffects(Collection effects) { + boolean success = true; + for (PotionEffect effect : effects) { + success &= addPotionEffect(effect); + } + return success; + } + + public boolean hasPotionEffect(PotionEffectType type) { + return getHandle().hasEffect(MobEffectList.byId[type.getId()]); + } + + public void removePotionEffect(PotionEffectType type) { + getHandle().removeEffect(type.getId()); + } + + public Collection getActivePotionEffects() { + List effects = new ArrayList(); + for (Object raw : getHandle().getEffects()) { + if (!(raw instanceof MobEffect)) + continue; + MobEffect handle = (MobEffect) raw; + effects.add(new PotionEffect(PotionEffectType.getById(handle.getEffectId()), handle.getDuration(), handle.getAmplifier(), handle.isAmbient())); + } + return effects; + } + + public T launchProjectile(Class projectile) { + return launchProjectile(projectile, null); + } + + @SuppressWarnings("unchecked") + public T launchProjectile(Class projectile, Vector velocity) { + net.minecraft.server.World world = ((CraftWorld) getWorld()).getHandle(); + net.minecraft.server.Entity launch = null; + + if (Snowball.class.isAssignableFrom(projectile)) { + launch = new EntitySnowball(world, getHandle()); + } else if (Egg.class.isAssignableFrom(projectile)) { + launch = new EntityEgg(world, getHandle()); + } else if (EnderPearl.class.isAssignableFrom(projectile)) { + launch = new EntityEnderPearl(world, getHandle()); + } else if (Arrow.class.isAssignableFrom(projectile)) { + launch = new EntityArrow(world, getHandle(), 1); + } else if (ThrownPotion.class.isAssignableFrom(projectile)) { + launch = new EntityPotion(world, getHandle(), CraftItemStack.asNMSCopy(new ItemStack(Material.POTION, 1))); + } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { + launch = new EntityThrownExpBottle(world, getHandle()); + } else if (Fish.class.isAssignableFrom(projectile) && getHandle() instanceof EntityHuman) { + launch = new EntityFishingHook(world, (EntityHuman) getHandle()); + } else if (Fireball.class.isAssignableFrom(projectile)) { + Location location = getEyeLocation(); + Vector direction = location.getDirection().multiply(10); + + if (SmallFireball.class.isAssignableFrom(projectile)) { + launch = new EntitySmallFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ()); + } else if (WitherSkull.class.isAssignableFrom(projectile)) { + launch = new EntityWitherSkull(world, getHandle(), direction.getX(), direction.getY(), direction.getZ()); + } else { + launch = new EntityLargeFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ()); + } + + ((EntityFireball) launch).projectileSource = this; + launch.setPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + } + + Validate.notNull(launch, "Projectile not supported"); + + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } + + world.addEntity(launch); + return (T) launch.getBukkitEntity(); + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } + + public boolean hasLineOfSight(Entity other) { + return getHandle().hasLineOfSight(((CraftEntity) other).getHandle()); + } + + public boolean getRemoveWhenFarAway() { + return getHandle() instanceof EntityInsentient && !((EntityInsentient) getHandle()).persistent; + } + + public void setRemoveWhenFarAway(boolean remove) { + if (getHandle() instanceof EntityInsentient) { + ((EntityInsentient) getHandle()).persistent = !remove; + } + } + + public EntityEquipment getEquipment() { + return equipment; + } + + public void setCanPickupItems(boolean pickup) { + if (getHandle() instanceof EntityInsentient) { + ((EntityInsentient) getHandle()).canPickUpLoot = pickup; + } + } + + public boolean getCanPickupItems() { + return getHandle() instanceof EntityInsentient && ((EntityInsentient) getHandle()).canPickUpLoot; + } + + @Override + public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) { + if (getHealth() == 0) { + return false; + } + + return super.teleport(location, cause); + } + + public void setCustomName(String name) { + if (!(getHandle() instanceof EntityInsentient)) { + return; + } + + if (name == null) { + name = ""; + } + + // Names cannot be more than 64 characters due to DataWatcher limitations + if (name.length() > 64) { + name = name.substring(0, 64); + } + + ((EntityInsentient) getHandle()).setCustomName(name); + } + + public String getCustomName() { + if (!(getHandle() instanceof EntityInsentient)) { + return null; + } + + String name = ((EntityInsentient) getHandle()).getCustomName(); + + if (name == null || name.length() == 0) { + return null; + } + + return name; + } + + public void setCustomNameVisible(boolean flag) { + if (getHandle() instanceof EntityInsentient) { + ((EntityInsentient) getHandle()).setCustomNameVisible(flag); + } + } + + public boolean isCustomNameVisible() { + return getHandle() instanceof EntityInsentient && ((EntityInsentient) getHandle()).getCustomNameVisible(); + } + + public boolean isLeashed() { + if (!(getHandle() instanceof EntityInsentient)) { + return false; + } + return ((EntityInsentient) getHandle()).getLeashHolder() != null; + } + + public Entity getLeashHolder() throws IllegalStateException { + if (!isLeashed()) { + throw new IllegalStateException("Entity not leashed"); + } + return ((EntityInsentient) getHandle()).getLeashHolder().getBukkitEntity(); + } + + private boolean unleash() { + if (!isLeashed()) { + return false; + } + ((EntityInsentient) getHandle()).unleash(true, false); + return true; + } + + public boolean setLeashHolder(Entity holder) { + if ((getHandle() instanceof EntityWither) || !(getHandle() instanceof EntityInsentient)) { + return false; + } + + if (holder == null) { + return unleash(); + } + + if (holder.isDead()) { + return false; + } + + unleash(); + ((EntityInsentient) getHandle()).setLeashHolder(((CraftEntity) holder).getHandle(), true); + return true; + } + + @Deprecated + public int _INVALID_getLastDamage() { + return NumberConversions.ceil(getLastDamage()); + } + + @Deprecated + public void _INVALID_setLastDamage(int damage) { + setLastDamage(damage); + } + + @Deprecated + public void _INVALID_damage(int amount) { + damage(amount); + } + + @Deprecated + public void _INVALID_damage(int amount, Entity source) { + damage(amount, source); + } + + @Deprecated + public int _INVALID_getHealth() { + return NumberConversions.ceil(getHealth()); + } + + @Deprecated + public void _INVALID_setHealth(int health) { + setHealth(health); + } + + @Deprecated + public int _INVALID_getMaxHealth() { + return NumberConversions.ceil(getMaxHealth()); + } + + @Deprecated + public void _INVALID_setMaxHealth(int health) { + setMaxHealth(health); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMagmaCube.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMagmaCube.java new file mode 100644 index 0000000..dace70b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMagmaCube.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMagmaCube; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.MagmaCube; + +public class CraftMagmaCube extends CraftSlime implements MagmaCube { + + public CraftMagmaCube(CraftServer server, EntityMagmaCube entity) { + super(server, entity); + } + + public EntityMagmaCube getHandle() { + return (EntityMagmaCube) entity; + } + + @Override + public String toString() { + return "CraftMagmaCube"; + } + + public EntityType getType() { + return EntityType.MAGMA_CUBE; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java new file mode 100644 index 0000000..daf6bb4 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java @@ -0,0 +1,71 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartAbstract; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Minecart; +import org.bukkit.util.NumberConversions; +import org.bukkit.util.Vector; + +public abstract class CraftMinecart extends CraftVehicle implements Minecart { + public CraftMinecart(CraftServer server, EntityMinecartAbstract entity) { + super(server, entity); + } + + public void setDamage(double damage) { + getHandle().setDamage((float) damage); + } + + public double getDamage() { + return getHandle().getDamage(); + } + + public double getMaxSpeed() { + return getHandle().maxSpeed; + } + + public void setMaxSpeed(double speed) { + if (speed >= 0D) { + getHandle().maxSpeed = speed; + } + } + + public boolean isSlowWhenEmpty() { + return getHandle().slowWhenEmpty; + } + + public void setSlowWhenEmpty(boolean slow) { + getHandle().slowWhenEmpty = slow; + } + + public Vector getFlyingVelocityMod() { + return getHandle().getFlyingVelocityMod(); + } + + public void setFlyingVelocityMod(Vector flying) { + getHandle().setFlyingVelocityMod(flying); + } + + public Vector getDerailedVelocityMod() { + return getHandle().getDerailedVelocityMod(); + } + + public void setDerailedVelocityMod(Vector derailed) { + getHandle().setDerailedVelocityMod(derailed); + } + + @Override + public EntityMinecartAbstract getHandle() { + return (EntityMinecartAbstract) entity; + } + + @Deprecated + public void _INVALID_setDamage(int damage) { + setDamage(damage); + } + + @Deprecated + public int _INVALID_getDamage() { + return NumberConversions.ceil(getDamage()); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java new file mode 100644 index 0000000..f5a1875 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java @@ -0,0 +1,32 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartChest; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.StorageMinecart; +import org.bukkit.inventory.Inventory; + +@SuppressWarnings("deprecation") +public class CraftMinecartChest extends CraftMinecart implements StorageMinecart { + private final CraftInventory inventory; + + public CraftMinecartChest(CraftServer server, EntityMinecartChest entity) { + super(server, entity); + inventory = new CraftInventory(entity); + } + + public Inventory getInventory() { + return inventory; + } + + @Override + public String toString() { + return "CraftMinecartChest{" + "inventory=" + inventory + '}'; + } + + public EntityType getType() { + return EntityType.MINECART_CHEST; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java new file mode 100644 index 0000000..813b080 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java @@ -0,0 +1,127 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.Set; + +import net.minecraft.server.EntityMinecartCommandBlock; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +public class CraftMinecartCommand extends CraftMinecart implements CommandMinecart { + private final PermissibleBase perm = new PermissibleBase(this); + + public CraftMinecartCommand(CraftServer server, EntityMinecartCommandBlock entity) { + super(server, entity); + } + + @Override + public String getCommand() { + return ((EntityMinecartCommandBlock) getHandle()).getCommandBlock().getCommand(); + } + + @Override + public void setCommand(String command) { + ((EntityMinecartCommandBlock) getHandle()).getCommandBlock().setCommand(command != null ? command : ""); + } + + @Override + public void setName(String name) { + ((EntityMinecartCommandBlock) getHandle()).getCommandBlock().setName(name != null ? name : "@"); + } + + @Override + public EntityType getType() { + return EntityType.MINECART_COMMAND; + } + + @Override + public void sendMessage(String message) { + } + + @Override + public void sendMessage(String[] messages) { + } + + @Override + public String getName() { + return ((EntityMinecartCommandBlock) getHandle()).getCommandBlock().getName(); + } + + @Override + public boolean isOp() { + return true; + } + + @Override + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of a minecart"); + } + + @Override + public boolean isPermissionSet(String name) { + return perm.isPermissionSet(name); + } + + @Override + public boolean isPermissionSet(Permission perm) { + return this.perm.isPermissionSet(perm); + } + + @Override + public boolean hasPermission(String name) { + return perm.hasPermission(name); + } + + @Override + public boolean hasPermission(Permission perm) { + return this.perm.hasPermission(perm); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return perm.addAttachment(plugin, name, value); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin) { + return perm.addAttachment(plugin); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return perm.addAttachment(plugin, name, value, ticks); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return perm.addAttachment(plugin, ticks); + } + + @Override + public void removeAttachment(PermissionAttachment attachment) { + perm.removeAttachment(attachment); + } + + @Override + public void recalculatePermissions() { + perm.recalculatePermissions(); + } + + @Override + public Set getEffectivePermissions() { + return perm.getEffectivePermissions(); + } + + @Override + public Server getServer() { + return Bukkit.getServer(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java new file mode 100644 index 0000000..463b4ce --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartFurnace; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.PoweredMinecart; + +@SuppressWarnings("deprecation") +public class CraftMinecartFurnace extends CraftMinecart implements PoweredMinecart { + public CraftMinecartFurnace(CraftServer server, EntityMinecartFurnace entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartFurnace"; + } + + public EntityType getType() { + return EntityType.MINECART_FURNACE; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java new file mode 100644 index 0000000..9bd358d --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartHopper; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.inventory.Inventory; + +final class CraftMinecartHopper extends CraftMinecart implements HopperMinecart { + private final CraftInventory inventory; + + CraftMinecartHopper(CraftServer server, EntityMinecartHopper entity) { + super(server, entity); + inventory = new CraftInventory(entity); + } + + @Override + public String toString() { + return "CraftMinecartHopper{" + "inventory=" + inventory + '}'; + } + + public EntityType getType() { + return EntityType.MINECART_HOPPER; + } + + public Inventory getInventory() { + return inventory; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java new file mode 100644 index 0000000..a78d816 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartMobSpawner; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.SpawnerMinecart; + +final class CraftMinecartMobSpawner extends CraftMinecart implements SpawnerMinecart { + CraftMinecartMobSpawner(CraftServer server, EntityMinecartMobSpawner entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartMobSpawner"; + } + + public EntityType getType() { + return EntityType.MINECART_MOB_SPAWNER; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartRideable.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartRideable.java new file mode 100644 index 0000000..9305d88 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartRideable.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartAbstract; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.RideableMinecart; + +public class CraftMinecartRideable extends CraftMinecart implements RideableMinecart { + public CraftMinecartRideable(CraftServer server, EntityMinecartAbstract entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartRideable"; + } + + public EntityType getType() { + return EntityType.MINECART; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartTNT.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartTNT.java new file mode 100644 index 0000000..0c8109b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartTNT.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartTNT; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.ExplosiveMinecart; + +final class CraftMinecartTNT extends CraftMinecart implements ExplosiveMinecart { + CraftMinecartTNT(CraftServer server, EntityMinecartTNT entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartTNT"; + } + + public EntityType getType() { + return EntityType.MINECART_TNT; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMonster.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMonster.java new file mode 100644 index 0000000..72dedbc --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMonster.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMonster; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Monster; + +public class CraftMonster extends CraftCreature implements Monster { + + public CraftMonster(CraftServer server, EntityMonster entity) { + super(server, entity); + } + + @Override + public EntityMonster getHandle() { + return (EntityMonster) entity; + } + + @Override + public String toString() { + return "CraftMonster"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMushroomCow.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMushroomCow.java new file mode 100644 index 0000000..3dd7ea3 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMushroomCow.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMushroomCow; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.MushroomCow; + +public class CraftMushroomCow extends CraftCow implements MushroomCow { + public CraftMushroomCow(CraftServer server, EntityMushroomCow entity) { + super(server, entity); + } + + @Override + public EntityMushroomCow getHandle() { + return (EntityMushroomCow) entity; + } + + @Override + public String toString() { + return "CraftMushroomCow"; + } + + public EntityType getType() { + return EntityType.MUSHROOM_COW; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftOcelot.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftOcelot.java new file mode 100644 index 0000000..37cc315 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftOcelot.java @@ -0,0 +1,32 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityOcelot; +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Ocelot; + +public class CraftOcelot extends CraftTameableAnimal implements Ocelot { + public CraftOcelot(CraftServer server, EntityOcelot wolf) { + super(server, wolf); + } + + @Override + public EntityOcelot getHandle() { + return (EntityOcelot) entity; + } + + public Type getCatType() { + return Type.getType(getHandle().getCatType()); + } + + public void setCatType(Type type) { + Validate.notNull(type, "Cat type cannot be null"); + getHandle().setCatType(type.getId()); + } + + @Override + public EntityType getType() { + return EntityType.OCELOT; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java new file mode 100644 index 0000000..925a15f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java @@ -0,0 +1,81 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPainting; +import net.minecraft.server.EnumArt; +import net.minecraft.server.WorldServer; + +import org.bukkit.Art; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftArt; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Painting; + +public class CraftPainting extends CraftHanging implements Painting { + + public CraftPainting(CraftServer server, EntityPainting entity) { + super(server, entity); + } + + public Art getArt() { + EnumArt art = getHandle().art; + return CraftArt.NotchToBukkit(art); + } + + public boolean setArt(Art art) { + return setArt(art, false); + } + + public boolean setArt(Art art, boolean force) { + EntityPainting painting = this.getHandle(); + EnumArt oldArt = painting.art; + painting.art = CraftArt.BukkitToNotch(art); + painting.setDirection(painting.direction); + if (!force && !painting.survives()) { + // Revert painting since it doesn't fit + painting.art = oldArt; + painting.setDirection(painting.direction); + return false; + } + this.update(); + return true; + } + + public boolean setFacingDirection(BlockFace face, boolean force) { + if (super.setFacingDirection(face, force)) { + update(); + return true; + } + + return false; + } + + private void update() { + WorldServer world = ((CraftWorld) getWorld()).getHandle(); + EntityPainting painting = new EntityPainting(world); + painting.x = getHandle().x; + painting.y = getHandle().y; + painting.z = getHandle().z; + painting.art = getHandle().art; + painting.setDirection(getHandle().direction); + getHandle().die(); + getHandle().velocityChanged = true; // because this occurs when the painting is broken, so it might be important + world.addEntity(painting); + this.entity = painting; + } + + @Override + public EntityPainting getHandle() { + return (EntityPainting) entity; + } + + @Override + public String toString() { + return "CraftPainting{art=" + getArt() + "}"; + } + + public EntityType getType() { + return EntityType.PAINTING; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java new file mode 100644 index 0000000..82bc7a6 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java @@ -0,0 +1,34 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPig; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Pig; + +public class CraftPig extends CraftAnimals implements Pig { + public CraftPig(CraftServer server, EntityPig entity) { + super(server, entity); + } + + public boolean hasSaddle() { + return getHandle().hasSaddle(); + } + + public void setSaddle(boolean saddled) { + getHandle().setSaddle(saddled); + } + + public EntityPig getHandle() { + return (EntityPig) entity; + } + + @Override + public String toString() { + return "CraftPig"; + } + + public EntityType getType() { + return EntityType.PIG; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPigZombie.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPigZombie.java new file mode 100644 index 0000000..0e6ccea --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPigZombie.java @@ -0,0 +1,44 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPigZombie; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.PigZombie; + +public class CraftPigZombie extends CraftZombie implements PigZombie { + + public CraftPigZombie(CraftServer server, EntityPigZombie entity) { + super(server, entity); + } + + public int getAnger() { + return getHandle().angerLevel; + } + + public void setAnger(int level) { + getHandle().angerLevel = level; + } + + public void setAngry(boolean angry) { + setAnger(angry ? 400 : 0); + } + + public boolean isAngry() { + return getAnger() > 0; + } + + @Override + public EntityPigZombie getHandle() { + return (EntityPigZombie) entity; + } + + @Override + public String toString() { + return "CraftPigZombie"; + } + + public EntityType getType() { + return EntityType.PIG_ZOMBIE; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java new file mode 100644 index 0000000..49adbdd --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -0,0 +1,1596 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.collect.ImmutableSet; +import net.md_5.bungee.api.chat.BaseComponent; +import net.minecraft.server.*; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import org.apache.commons.lang.NotImplementedException; +import org.apache.commons.lang.Validate; +import org.bukkit.Achievement; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.*; +import org.bukkit.World; +import org.bukkit.Statistic.Type; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.conversations.Conversation; +import org.bukkit.conversations.ConversationAbandonedEvent; +import org.bukkit.conversations.ManuallyAbandonedConversationCanceller; +import org.bukkit.craftbukkit.*; +import org.bukkit.craftbukkit.block.CraftSign; +import org.bukkit.craftbukkit.conversations.ConversationTracker; +import org.bukkit.craftbukkit.map.CraftMapView; +import org.bukkit.craftbukkit.map.RenderData; +import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.player.*; +import org.bukkit.inventory.InventoryView.Property; +import org.bukkit.map.MapView; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.util.Vector; +import org.spigotmc.SpigotConfig; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +@DelegateDeserialization(CraftOfflinePlayer.class) +public class CraftPlayer extends CraftHumanEntity implements Player { + private long firstPlayed = 0; + private long lastPlayed = 0; + private boolean hasPlayedBefore = false; + private final ConversationTracker conversationTracker = new ConversationTracker(); + private final Set channels = new HashSet(); + private final Set hiddenPlayers = new HashSet(); + private int hash = 0; + private double health = 20; + private boolean scaledHealth = false; + private double healthScale = 20; + // MineHQ start - Disguises + private String disguisedName; + private String originalPlayerListName; + public GameProfile disguisedProfile; + // MineHQ end + + public CraftPlayer(CraftServer server, EntityPlayer entity) { + super(server, entity); + + firstPlayed = System.currentTimeMillis(); + } + + @Override + public void setVelocity(Vector vel) { + // To be consistent with old behavior, set the velocity before firing the event + this.setVelocityDirect(vel); + + PlayerVelocityEvent event = new PlayerVelocityEvent(this, vel.clone()); + this.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + // Set the velocity again in case it was changed by event handlers + this.setVelocityDirect(event.getVelocity()); + + // Send the new velocity to the player's client immediately, so it isn't affected by + // any movement packets from this player that may be processed before the end of the tick. + // Without this, player velocity changes tend to be very inconsistent. + this.getHandle().playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.getHandle())); + } + + // Note that cancelling the event does not restore the old velocity, it only prevents + // the packet from sending. Again, this is to be consistent with old behavior. + } + + public void setVelocityDirect(Vector vel) { + entity.motX = vel.getX(); + entity.motY = vel.getY(); + entity.motZ = vel.getZ(); + } + + public GameProfile getProfile() { + return getHandle().getProfile(); + } + + @Override + public boolean isOp() { + return server.getHandle().isOp(getProfile()); + } + + @Override + public void setOp(boolean value) { + if (value == isOp()) return; + + if (value) { + server.getHandle().addOp(getProfile()); + } else { + server.getHandle().removeOp(getProfile()); + } + + perm.recalculatePermissions(); + } + + public boolean isOnline() { + return server.getHandle().uuidMap.get(getUniqueId()) != null; // PaperSpigot - replace whole method + } + + public InetSocketAddress getAddress() { + if (getHandle().playerConnection == null) return null; + + SocketAddress addr = getHandle().playerConnection.networkManager.getSocketAddress(); + if (addr instanceof InetSocketAddress) { + return (InetSocketAddress) addr; + } else { + return null; + } + } + + @Override + public double getEyeHeight() { + return getEyeHeight(false); + } + + @Override + public double getEyeHeight(boolean ignoreSneaking) { + if (ignoreSneaking) { + return 1.62D; + } else { + if (isSneaking()) { + return 1.54D; + } else { + return 1.62D; + } + } + } + + @Override + public void sendRawMessage(String message) { + if (getHandle().playerConnection == null) return; + + for (IChatBaseComponent component : CraftChatMessage.fromString(message)) { + getHandle().playerConnection.sendPacket(new PacketPlayOutChat(component)); + } + } + + @Override + public void sendMessage(String message) { + if (!conversationTracker.isConversingModaly()) { + this.sendRawMessage(message); + } + } + + @Override + public void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + @Override + public String getDisplayName() { + return disguisedName != null ? disguisedName : getHandle().displayName; // MineHQ - Disguises + } + + @Override + public void setDisplayName(final String name) { + getHandle().displayName = name == null ? getName() : name; + } + + // MineHQ start - Disguises + @Override + public String getDisguisedName() { + return disguisedName != null ? disguisedName : getName(); + } + + @Override + public boolean isDisguised() { + return disguisedName != null; + } + + @Override + public void disguise(String name, String texture,String textureSignature) { + Validate.isTrue(!isDisguised(), "Player is already disguised"); + Validate.isTrue(!MinecraftServer.getServer().getPlayerList().disguisePlayerMap.containsKey(name), "Disguise name is already in use"); + + // we construct this here, before we actually make any changes to their profile and whatnot. + PacketPlayOutPlayerInfo removeTabPacket = PacketPlayOutPlayerInfo.removePlayer(getHandle()); + + disguisedName = name; + disguisedProfile = new GameProfile(getUniqueId(), disguisedName); + + if (texture != null) { + disguisedProfile.getProperties().put("texture", new net.minecraft.util.com.mojang.authlib.properties.Property("textures", texture, textureSignature)); + } + + originalPlayerListName = getPlayerListName(); + setPlayerListName(disguisedName); + + MinecraftServer.getServer().getPlayerList().disguisePlayerMap.put(disguisedName, getHandle()); + + PacketPlayOutPlayerInfo addThisTabPacket = PacketPlayOutPlayerInfo.addPlayer(getHandle()); + + for (Object playerObj : MinecraftServer.getServer().getPlayerList().players) { + EntityPlayer player = (EntityPlayer) playerObj; + + if (player.playerConnection != null) { + if (SpigotConfig.playerListPackets) player.playerConnection.sendPacket(removeTabPacket); + if (SpigotConfig.playerListPackets || player.playerConnection.networkManager.getVersion() > 27) player.playerConnection.sendPacket(addThisTabPacket); + } + } + + EntityTrackerEntry trackerEntry = (EntityTrackerEntry)((WorldServer)this.entity.world).getTracker().trackedEntities.get(getEntityId()); + + if (trackerEntry != null) { + PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(getEntityId()); + PacketPlayOutNamedEntitySpawn spawnPacket = new PacketPlayOutNamedEntitySpawn(getHandle()); + + trackerEntry.broadcast(destroyPacket); + trackerEntry.broadcast(spawnPacket); + } + + PacketPlayOutPlayerInfo removeThisTabPacket = PacketPlayOutPlayerInfo.removePlayer(getHandle()); + for (Object playerObj : MinecraftServer.getServer().getPlayerList().players) { + EntityPlayer player = (EntityPlayer) playerObj; + + if (player.playerConnection != null) { + if (!SpigotConfig.playerListPackets && player.playerConnection.networkManager.getVersion() > 27) player.playerConnection.sendPacket(removeThisTabPacket); + } + } + } + + @Override + public void disguise(String name) { + disguise(name, null, null); + } + + @Override + public void undisguise() { + Validate.isTrue(isDisguised(), "Player is not disguised"); + + PacketPlayOutPlayerInfo removeTabPacket = PacketPlayOutPlayerInfo.removePlayer(getHandle()); + + setPlayerListName(originalPlayerListName); + + MinecraftServer.getServer().getPlayerList().disguisePlayerMap.remove(disguisedName); + + disguisedName = null; + disguisedProfile = null; + originalPlayerListName = null; + + PacketPlayOutNamedEntitySpawn spawnPacket = new PacketPlayOutNamedEntitySpawn(getHandle()); + PacketPlayOutPlayerInfo addThisTabPacket = PacketPlayOutPlayerInfo.addPlayer(getHandle()); + + for (Object playerObj : MinecraftServer.getServer().getPlayerList().players) { + EntityPlayer player = (EntityPlayer) playerObj; + + if (player.playerConnection != null) { + player.playerConnection.sendPacket(removeTabPacket); + player.playerConnection.sendPacket(addThisTabPacket); + } + } + + EntityTrackerEntry trackerEntry = (EntityTrackerEntry) ((WorldServer) this.entity.world).getTracker().trackedEntities.get(getEntityId()); + + if (trackerEntry != null) { + PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(getEntityId()); + + trackerEntry.broadcast(destroyPacket); + trackerEntry.broadcast(spawnPacket); + } + + PacketPlayOutPlayerInfo removeThisTabPacket = PacketPlayOutPlayerInfo.removePlayer(getHandle()); + for (Object playerObj : MinecraftServer.getServer().getPlayerList().players) { + EntityPlayer player = (EntityPlayer) playerObj; + if (!SpigotConfig.playerListPackets) { + player.playerConnection.sendPacket(removeThisTabPacket); + } + } + } + // MineHQ end + + @Override + public String getPlayerListName() { + return getHandle().listName; + } + + @Override + public void setPlayerListName(String name) { + String oldName = getHandle().listName; + + if (name == null) { + name = getName(); + } + + if (oldName.equals(name)) { + return; + } + + if (name.length() > 16) { + throw new IllegalArgumentException("Player list names can only be a maximum of 16 characters long"); + } + + // Collisions will make for invisible people + for (int i = 0; i < server.getHandle().players.size(); ++i) { + if (((EntityPlayer) server.getHandle().players.get(i)).listName.equals(name)) { + throw new IllegalArgumentException(name + " is already assigned as a player list name for someone"); + } + } + + getHandle().listName = name; + + if (!SpigotConfig.playerListPackets) return; // MineHQ + + // Change the name on the client side + // Spigot start - protocol patch + String temp = getHandle().listName; + getHandle().listName = oldName; + PacketPlayOutPlayerInfo oldpacket = PacketPlayOutPlayerInfo.removePlayer(getHandle()); + getHandle().listName = temp; + PacketPlayOutPlayerInfo packet = PacketPlayOutPlayerInfo.addPlayer(getHandle()); + PacketPlayOutPlayerInfo newPacket = PacketPlayOutPlayerInfo.updateDisplayName(getHandle()); + for (int i = 0; i < server.getHandle().players.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) server.getHandle().players.get(i); + if (entityplayer.playerConnection == null) continue; + + if (entityplayer.getBukkitEntity().canSee(this)) { + if (entityplayer.playerConnection.networkManager.getVersion() < 28) { + entityplayer.playerConnection.sendPacket(oldpacket); + entityplayer.playerConnection.sendPacket(packet); + } else { + entityplayer.playerConnection.sendPacket(newPacket); + } + } + } + + // Spigot end + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof OfflinePlayer)) { + return false; + } + OfflinePlayer other = (OfflinePlayer) obj; + if ((this.getUniqueId() == null) || (other.getUniqueId() == null)) { + return false; + } + + boolean uuidEquals = this.getUniqueId().equals(other.getUniqueId()); + boolean idEquals = true; + + if (other instanceof CraftPlayer) { + idEquals = this.getEntityId() == ((CraftPlayer) other).getEntityId(); + } + + return uuidEquals && idEquals; + } + + @Override + public void kickPlayer(String message) { + org.spigotmc.AsyncCatcher.catchOp("player kick"); // Spigot + if (getHandle().playerConnection == null) return; + + if (message != null && message.length() > 200) { + message = message.substring(0, 200); + } + + getHandle().playerConnection.disconnect(message); + } + + @Override + public void setCompassTarget(Location loc) { + if (getHandle().playerConnection == null) return; + + // Do not directly assign here, from the packethandler we'll assign it. + getHandle().playerConnection.sendPacket(new PacketPlayOutSpawnPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())); + } + + @Override + public Location getCompassTarget() { + return getHandle().compassTarget; + } + + @Override + public void chat(String msg) { + if (getHandle().playerConnection == null) return; + + getHandle().playerConnection.chat(msg, false); + } + + @Override + public boolean performCommand(String command) { + return server.dispatchCommand(this, command); + } + + @Override + public void playNote(Location loc, byte instrument, byte note) { + if (getHandle().playerConnection == null) return; + + String instrumentName = null; + switch (instrument) { + case 0: + instrumentName = "harp"; + break; + case 1: + instrumentName = "bd"; + break; + case 2: + instrumentName = "snare"; + break; + case 3: + instrumentName = "hat"; + break; + case 4: + instrumentName = "bassattack"; + break; + } + getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect("note." + instrumentName, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, note)); + } + + @Override + public void playNote(Location loc, Instrument instrument, Note note) { + if (getHandle().playerConnection == null) return; + + String instrumentName = null; + switch (instrument.ordinal()) { + case 0: + instrumentName = "harp"; + break; + case 1: + instrumentName = "bd"; + break; + case 2: + instrumentName = "snare"; + break; + case 3: + instrumentName = "hat"; + break; + case 4: + instrumentName = "bassattack"; + break; + } + getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect("note." + instrumentName, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, note.getId())); + } + + @Override + public void playSound(Location loc, Sound sound, float volume, float pitch) { + if (sound == null) { + return; + } + playSound(loc, CraftSound.getSound(sound), volume, pitch); + } + + @Override + public void playSound(Location loc, String sound, float volume, float pitch) { + if (loc == null || sound == null || getHandle().playerConnection == null) return; + + double x = loc.getBlockX() + 0.5; + double y = loc.getBlockY() + 0.5; + double z = loc.getBlockZ() + 0.5; + + PacketPlayOutNamedSoundEffect packet = new PacketPlayOutNamedSoundEffect(sound, x, y, z, volume, pitch); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void playEffect(Location loc, Effect effect, int data) { + if (getHandle().playerConnection == null) return; + + int packetData = effect.getId(); + PacketPlayOutWorldEvent packet = new PacketPlayOutWorldEvent(packetData, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), data, false); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void playEffect(Location loc, Effect effect, T data) { + if (data != null) { + Validate.isTrue(data.getClass().equals(effect.getData()), "Wrong kind of data for this effect!"); + } else { + Validate.isTrue(effect.getData() == null, "Wrong kind of data for this effect!"); + } + + int datavalue = data == null ? 0 : CraftEffect.getDataValue(effect, data); + playEffect(loc, effect, datavalue); + } + + @Override + public void sendBlockChange(Location loc, Material material, byte data) { + sendBlockChange(loc, material.getId(), data); + } + + @Override + public void sendBlockChange(Location loc, int material, byte data) { + if (getHandle().playerConnection == null) return; + + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), ((CraftWorld) loc.getWorld()).getHandle()); + + packet.block = CraftMagicNumbers.getBlock(material); + packet.data = data; + packet.fake = true; + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void sendSignChange(Location loc, String[] lines) { + if (getHandle().playerConnection == null) { + return; + } + + if (lines == null) { + lines = new String[4]; + } + + Validate.notNull(loc, "Location can not be null"); + if (lines.length < 4) { + throw new IllegalArgumentException("Must have at least 4 lines"); + } + + // Limit to 15 chars per line and set null lines to blank + String[] astring = CraftSign.sanitizeLines(lines); + + getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateSign(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), astring)); + } + + @Override + public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) { + if (getHandle().playerConnection == null) return false; + + /* + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + + int cx = x >> 4; + int cz = z >> 4; + + if (sx <= 0 || sy <= 0 || sz <= 0) { + return false; + } + + if ((x + sx - 1) >> 4 != cx || (z + sz - 1) >> 4 != cz || y < 0 || y + sy > 128) { + return false; + } + + if (data.length != (sx * sy * sz * 5) / 2) { + return false; + } + + Packet51MapChunk packet = new Packet51MapChunk(x, y, z, sx, sy, sz, data); + + getHandle().playerConnection.sendPacket(packet); + + return true; + */ + + throw new NotImplementedException("Chunk changes do not yet work"); // TODO: Chunk changes. + } + + @Override + public void sendMap(MapView map) { + if (getHandle().playerConnection == null) return; + + RenderData data = ((CraftMapView) map).render(this); + for (int x = 0; x < 128; ++x) { + byte[] bytes = new byte[131]; + bytes[1] = (byte) x; + for (int y = 0; y < 128; ++y) { + bytes[y + 3] = data.buffer[y * 128 + x]; + } + PacketPlayOutMap packet = new PacketPlayOutMap(map.getId(), bytes, map.getScale().getValue()); // Spigot - protocol patch + getHandle().playerConnection.sendPacket(packet); + } + } + + @Override + public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) { + EntityPlayer entity = getHandle(); + + if (getHealth() == 0 || entity.dead) { + return false; + } + + if (entity.playerConnection == null || entity.playerConnection.isDisconnected()) { + return false; + } + + if (entity.passenger != null) { + return false; + } + + // MineHQ start - don't allow excessive teleports + int locationChunkX = location.getBlockX() >> 4; + int locationChunkZ = location.getBlockZ() >> 4; + + if (46340 <= Math.abs(locationChunkX) || 46340 <= Math.abs(locationChunkZ)) { + throw new IllegalArgumentException("Invalid teleportation destination for " + this.getName() + "! Offending location: " + location.toString()); + } + // MineHQ end + + // From = Players current Location + Location from = this.getLocation(); + // To = Players new Location if Teleport is Successful + Location to = location; + // Create & Call the Teleport Event. + PlayerTeleportEvent event = new PlayerTeleportEvent(this, from, to, cause); + server.getPluginManager().callEvent(event); + + // Return False to inform the Plugin that the Teleport was unsuccessful/cancelled. + if (event.isCancelled()) { + return false; + } + + // If this player is riding another entity, we must dismount before teleporting. + entity.mount(null); + + // PaperSpigot start + Entity vehicle = entity.vehicle; + Entity passenger = entity.passenger; + if (vehicle != null) { + vehicle.passenger = null; + vehicle.teleportTo(location, false); + vehicle = vehicle.getBukkitEntity().getHandle(); + entity.vehicle = vehicle; + vehicle.passenger = entity; + } + + if (passenger != null) { + passenger.vehicle = null; + passenger.teleportTo(location, false); + passenger = passenger.getBukkitEntity().getHandle(); + entity.passenger = passenger; + entity.vehicle = entity; + } + // PaperSpigot end + + // Update the From Location + from = event.getFrom(); + // Grab the new To Location dependent on whether the event was cancelled. + to = event.getTo(); + // Grab the To and From World Handles. + WorldServer fromWorld = ((CraftWorld) from.getWorld()).getHandle(); + WorldServer toWorld = ((CraftWorld) to.getWorld()).getHandle(); + + // Close any foreign inventory + if (getHandle().activeContainer != getHandle().defaultContainer) { + getHandle().closeInventory(); + } + + // Check if the fromWorld and toWorld are the same. + if (fromWorld == toWorld) { + entity.playerConnection.teleport(to); + } else { + server.getHandle().moveToWorld(entity, toWorld.dimension, true, to, true); + } + + // PaperSpigot start + if (vehicle != null) { + vehicle.retrack(); + //entity.retrack(); + } + if (passenger != null) { + passenger.retrack(); + } + // PaperSpigot end + return true; + } + + @Override + public void setSneaking(boolean sneak) { + getHandle().setSneaking(sneak); + } + + @Override + public boolean isSneaking() { + return getHandle().isSneaking(); + } + + @Override + public boolean isSprinting() { + return getHandle().isSprinting(); + } + + @Override + public void setSprinting(boolean sprinting) { + getHandle().setSprinting(sprinting); + } + + @Override + public void loadData() { + server.getHandle().playerFileData.load(getHandle()); + } + + @Override + public void saveData() { + server.getHandle().playerFileData.save(getHandle()); + } + + @Deprecated + @Override + public void updateInventory() { + getHandle().updateInventory(getHandle().activeContainer); + } + + @Override + public void setSleepingIgnored(boolean isSleeping) { + getHandle().fauxSleeping = isSleeping; + ((CraftWorld) getWorld()).getHandle().checkSleepStatus(); + } + + @Override + public boolean isSleepingIgnored() { + return getHandle().fauxSleeping; + } + + @Override + public void awardAchievement(Achievement achievement) { + Validate.notNull(achievement, "Achievement cannot be null"); + if (achievement.hasParent() && !hasAchievement(achievement.getParent())) { + awardAchievement(achievement.getParent()); + } + getHandle().getStatisticManager().setStatistic(getHandle(), CraftStatistic.getNMSAchievement(achievement), 1); + getHandle().getStatisticManager().updateStatistics(getHandle()); + } + + @Override + public void removeAchievement(Achievement achievement) { + Validate.notNull(achievement, "Achievement cannot be null"); + for (Achievement achieve : Achievement.values()) { + if (achieve.getParent() == achievement && hasAchievement(achieve)) { + removeAchievement(achieve); + } + } + getHandle().getStatisticManager().setStatistic(getHandle(), CraftStatistic.getNMSAchievement(achievement), 0); + } + + @Override + public boolean hasAchievement(Achievement achievement) { + Validate.notNull(achievement, "Achievement cannot be null"); + return getHandle().getStatisticManager().hasAchievement(CraftStatistic.getNMSAchievement(achievement)); + } + + @Override + public void incrementStatistic(Statistic statistic) { + incrementStatistic(statistic, 1); + } + + @Override + public void decrementStatistic(Statistic statistic) { + decrementStatistic(statistic, 1); + } + + @Override + public int getStatistic(Statistic statistic) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic"); + return getHandle().getStatisticManager().getStatisticValue(CraftStatistic.getNMSStatistic(statistic)); + } + + @Override + public void incrementStatistic(Statistic statistic, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, getStatistic(statistic) + amount); + } + + @Override + public void decrementStatistic(Statistic statistic, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, getStatistic(statistic) - amount); + } + + @Override + public void setStatistic(Statistic statistic, int newValue) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic"); + Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getNMSStatistic(statistic); + getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue); + } + + @Override + public void incrementStatistic(Statistic statistic, Material material) { + incrementStatistic(statistic, material, 1); + } + + @Override + public void decrementStatistic(Statistic statistic, Material material) { + decrementStatistic(statistic, material, 1); + } + + @Override + public int getStatistic(Statistic statistic, Material material) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(material, "Material cannot be null"); + Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material); + Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic"); + return getHandle().getStatisticManager().getStatisticValue(nmsStatistic); + } + + @Override + public void incrementStatistic(Statistic statistic, Material material, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, material, getStatistic(statistic, material) + amount); + } + + @Override + public void decrementStatistic(Statistic statistic, Material material, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, material, getStatistic(statistic, material) - amount); + } + + @Override + public void setStatistic(Statistic statistic, Material material, int newValue) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(material, "Material cannot be null"); + Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0"); + Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material); + Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic"); + getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue); + } + + @Override + public void incrementStatistic(Statistic statistic, EntityType entityType) { + incrementStatistic(statistic, entityType, 1); + } + + @Override + public void decrementStatistic(Statistic statistic, EntityType entityType) { + decrementStatistic(statistic, entityType, 1); + } + + @Override + public int getStatistic(Statistic statistic, EntityType entityType) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(entityType, "EntityType cannot be null"); + Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType); + Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic"); + return getHandle().getStatisticManager().getStatisticValue(nmsStatistic); + } + + @Override + public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, entityType, getStatistic(statistic, entityType) + amount); + } + + @Override + public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, entityType, getStatistic(statistic, entityType) - amount); + } + + @Override + public void setStatistic(Statistic statistic, EntityType entityType, int newValue) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(entityType, "EntityType cannot be null"); + Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0"); + Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType); + Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic"); + getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue); + } + + @Override + public void setPlayerTime(long time, boolean relative) { + getHandle().timeOffset = time; + getHandle().relativeTime = relative; + } + + @Override + public long getPlayerTimeOffset() { + return getHandle().timeOffset; + } + + @Override + public long getPlayerTime() { + return getHandle().getPlayerTime(); + } + + @Override + public boolean isPlayerTimeRelative() { + return getHandle().relativeTime; + } + + @Override + public void resetPlayerTime() { + setPlayerTime(0, true); + } + + @Override + public void setPlayerWeather(WeatherType type) { + getHandle().setPlayerWeather(type, true); + } + + @Override + public WeatherType getPlayerWeather() { + return getHandle().getPlayerWeather(); + } + + @Override + public void resetPlayerWeather() { + getHandle().resetPlayerWeather(); + } + + @Override + public boolean isBanned() { + return server.getBanList(BanList.Type.NAME).isBanned(getName()); + } + + @Override + public void setBanned(boolean value) { + if (value) { + server.getBanList(BanList.Type.NAME).addBan(getName(), null, null, null); + } else { + server.getBanList(BanList.Type.NAME).pardon(getName()); + } + } + + @Override + public boolean isWhitelisted() { + return server.getHandle().getWhitelist().isWhitelisted(getProfile()); + } + + @Override + public void setWhitelisted(boolean value) { + if (value) { + server.getHandle().addWhitelist(getProfile()); + } else { + server.getHandle().removeWhitelist(getProfile()); + } + } + + @Override + public void setGameMode(GameMode mode) { + if (getHandle().playerConnection == null) return; + + if (mode == null) { + throw new IllegalArgumentException("Mode cannot be null"); + } + + if (mode != getGameMode()) { + PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(this, mode); + server.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + + getHandle().playerInteractManager.setGameMode(EnumGamemode.getById(mode.getValue())); + getHandle().fallDistance = 0; + getHandle().playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, mode.getValue())); + } + } + + @Override + public GameMode getGameMode() { + return GameMode.getByValue(getHandle().playerInteractManager.getGameMode().getId()); + } + + public void giveExp(int exp) { + getHandle().giveExp(exp); + } + + public void giveExpLevels(int levels) { + getHandle().levelDown(levels); + } + + public float getExp() { + return getHandle().exp; + } + + public void setExp(float exp) { + getHandle().exp = exp; + getHandle().lastSentExp = -1; + } + + public int getLevel() { + return getHandle().expLevel; + } + + public void setLevel(int level) { + getHandle().expLevel = level; + getHandle().lastSentExp = -1; + } + + public int getTotalExperience() { + return getHandle().expTotal; + } + + public void setTotalExperience(int exp) { + getHandle().expTotal = exp; + } + + public float getExhaustion() { + return getHandle().getFoodData().exhaustionLevel; + } + + public void setExhaustion(float value) { + getHandle().getFoodData().exhaustionLevel = value; + } + + public float getSaturation() { + return getHandle().getFoodData().saturationLevel; + } + + public void setSaturation(float value) { + getHandle().getFoodData().saturationLevel = value; + } + + public int getFoodLevel() { + return getHandle().getFoodData().foodLevel; + } + + public void setFoodLevel(int value) { + getHandle().getFoodData().foodLevel = value; + } + + public Location getBedSpawnLocation() { + World world = getServer().getWorld(getHandle().spawnWorld); + ChunkCoordinates bed = getHandle().getBed(); + + if (world != null && bed != null) { + bed = EntityHuman.getBed(((CraftWorld) world).getHandle(), bed, getHandle().isRespawnForced()); + if (bed != null) { + return new Location(world, bed.x, bed.y, bed.z); + } + } + return null; + } + + public void setBedSpawnLocation(Location location) { + setBedSpawnLocation(location, false); + } + + public void setBedSpawnLocation(Location location, boolean override) { + if (location == null) { + getHandle().setRespawnPosition(null, override); + } else { + getHandle().setRespawnPosition(new ChunkCoordinates(location.getBlockX(), location.getBlockY(), location.getBlockZ()), override); + getHandle().spawnWorld = location.getWorld().getName(); + } + } + + public void hidePlayer(Player player) { + Validate.notNull(player, "hidden player cannot be null"); + if (getHandle().playerConnection == null) return; + if (equals(player)) return; + if (hiddenPlayers.contains(player.getUniqueId())) return; + hiddenPlayers.add(player.getUniqueId()); + + //remove this player from the hidden player's EntityTrackerEntry + EntityTracker tracker = ((WorldServer) entity.world).tracker; + EntityPlayer other = ((CraftPlayer) player).getHandle(); + EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(other.getId()); + if (entry != null) { + entry.clear(getHandle()); + } + + //remove the hidden player from this player user list + getHandle().playerConnection.sendPacket(PacketPlayOutPlayerInfo.removePlayer(((CraftPlayer) player).getHandle())); // Spigot - protocol patch + } + + public void showPlayer(Player player) { + Validate.notNull(player, "shown player cannot be null"); + if (getHandle().playerConnection == null) return; + if (equals(player)) return; + if (!hiddenPlayers.contains(player.getUniqueId())) return; + hiddenPlayers.remove(player.getUniqueId()); + + EntityTracker tracker = ((WorldServer) entity.world).tracker; + EntityPlayer other = ((CraftPlayer) player).getHandle(); + EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(other.getId()); + if (entry != null && !entry.trackedPlayers.contains(getHandle())) { + entry.updatePlayer(getHandle()); + } + + getHandle().playerConnection.sendPacket(PacketPlayOutPlayerInfo.addPlayer( ( (CraftPlayer) player ).getHandle ())); // Spigot - protocol patch // MineHQ - unneeded + } + + public void removeDisconnectingPlayer(Player player) { + hiddenPlayers.remove(player.getUniqueId()); + } + + public boolean canSee(Player player) { + return !hiddenPlayers.contains(player.getUniqueId()); + } + + public Map serialize() { + Map result = new LinkedHashMap(); + + result.put("name", getName()); + + return result; + } + + public Player getPlayer() { + return this; + } + + @Override + public EntityPlayer getHandle() { + return (EntityPlayer) entity; + } + + public void setHandle(final EntityPlayer entity) { + super.setHandle(entity); + } + + @Override + public String toString() { + return "CraftPlayer{" + "name=" + getName() + '}'; + } + + @Override + public int hashCode() { + if (hash == 0 || hash == 485) { + hash = 97 * 5 + (this.getUniqueId() != null ? this.getUniqueId().hashCode() : 0); + } + return hash; + } + + public long getFirstPlayed() { + return firstPlayed; + } + + public long getLastPlayed() { + return lastPlayed; + } + + public boolean hasPlayedBefore() { + return hasPlayedBefore; + } + + public void setFirstPlayed(long firstPlayed) { + this.firstPlayed = firstPlayed; + } + + public void readExtraData(NBTTagCompound nbttagcompound) { + hasPlayedBefore = true; + if (nbttagcompound.hasKey("bukkit")) { + NBTTagCompound data = nbttagcompound.getCompound("bukkit"); + + if (data.hasKey("firstPlayed")) { + firstPlayed = data.getLong("firstPlayed"); + lastPlayed = data.getLong("lastPlayed"); + } + + if (data.hasKey("newExp")) { + EntityPlayer handle = getHandle(); + handle.newExp = data.getInt("newExp"); + handle.newTotalExp = data.getInt("newTotalExp"); + handle.newLevel = data.getInt("newLevel"); + handle.expToDrop = data.getInt("expToDrop"); + handle.keepLevel = data.getBoolean("keepLevel"); + } + } + } + + public void setExtraData(NBTTagCompound nbttagcompound) { + if (!nbttagcompound.hasKey("bukkit")) { + nbttagcompound.set("bukkit", new NBTTagCompound()); + } + + NBTTagCompound data = nbttagcompound.getCompound("bukkit"); + EntityPlayer handle = getHandle(); + data.setInt("newExp", handle.newExp); + data.setInt("newTotalExp", handle.newTotalExp); + data.setInt("newLevel", handle.newLevel); + data.setInt("expToDrop", handle.expToDrop); + data.setBoolean("keepLevel", handle.keepLevel); + data.setLong("firstPlayed", getFirstPlayed()); + data.setLong("lastPlayed", System.currentTimeMillis()); + data.setString("lastKnownName", handle.getName()); + } + + public boolean beginConversation(Conversation conversation) { + return conversationTracker.beginConversation(conversation); + } + + public void abandonConversation(Conversation conversation) { + conversationTracker.abandonConversation(conversation, new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller())); + } + + public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) { + conversationTracker.abandonConversation(conversation, details); + } + + public void acceptConversationInput(String input) { + conversationTracker.acceptConversationInput(input); + } + + public boolean isConversing() { + return conversationTracker.isConversing(); + } + + public void sendPluginMessage(Plugin source, String channel, byte[] message) { + StandardMessenger.validatePluginMessage(server.getMessenger(), source, channel, message); + if (getHandle().playerConnection == null) return; + + if (channels.contains(channel)) { + PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload(channel, message); + getHandle().playerConnection.sendPacket(packet); + } + } + + public void setTexturePack(String url) { + setResourcePack(url); + } + + @Override + public void setResourcePack(String url) { + Validate.notNull(url, "Resource pack URL cannot be null"); + + getHandle().setResourcePack(url); + } + + public void addChannel(String channel) { + com.google.common.base.Preconditions.checkState(channels.size() < 128, "Too many channels registered"); // Spigot + if (channels.add(channel)) { + server.getPluginManager().callEvent(new PlayerRegisterChannelEvent(this, channel)); + } + } + + public void removeChannel(String channel) { + if (channels.remove(channel)) { + server.getPluginManager().callEvent(new PlayerUnregisterChannelEvent(this, channel)); + } + } + + public Set getListeningPluginChannels() { + return ImmutableSet.copyOf(channels); + } + + public void sendSupportedChannels() { + if (getHandle().playerConnection == null) return; + Set listening = server.getMessenger().getIncomingChannels(); + + if (!listening.isEmpty()) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + for (String channel : listening) { + try { + stream.write(channel.getBytes("UTF8")); + stream.write((byte) 0); + } catch (IOException ex) { + Logger.getLogger(CraftPlayer.class.getName()).log(Level.SEVERE, "Could not send Plugin Channel REGISTER to " + getName(), ex); + } + } + + getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload("REGISTER", stream.toByteArray())); + } + } + + @Override + public EntityType getType() { + return EntityType.PLAYER; + } + + @Override + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + server.getPlayerMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + @Override + public List getMetadata(String metadataKey) { + return server.getPlayerMetadata().getMetadata(this, metadataKey); + } + + @Override + public boolean hasMetadata(String metadataKey) { + return server.getPlayerMetadata().hasMetadata(this, metadataKey); + } + + @Override + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + server.getPlayerMetadata().removeMetadata(this, metadataKey, owningPlugin); + } + + @Override + public boolean setWindowProperty(Property prop, int value) { + Container container = getHandle().activeContainer; + if (container.getBukkitView().getType() != prop.getType()) { + return false; + } + getHandle().setContainerData(container, prop.getId(), value); + return true; + } + + public void disconnect(String reason) { + conversationTracker.abandonAllConversations(); + perm.clearPermissions(); + } + + public boolean isFlying() { + return getHandle().abilities.isFlying; + } + + public void setFlying(boolean value) { + boolean needsUpdate = getHandle().abilities.canFly != value; // PaperSpigot - Only refresh abilities if needed + if (!getAllowFlight() && value) { + throw new IllegalArgumentException("Cannot make player fly if getAllowFlight() is false"); + } + + getHandle().abilities.isFlying = value; + if (needsUpdate) getHandle().updateAbilities(); // PaperSpigot - Only refresh abilities if needed + } + + public boolean getAllowFlight() { + return getHandle().abilities.canFly; + } + + public void setAllowFlight(boolean value) { + if (isFlying() && !value) { + getHandle().abilities.isFlying = false; + } + + getHandle().abilities.canFly = value; + getHandle().updateAbilities(); + } + + @Override + public int getNoDamageTicks() { + if (getHandle().invulnerableTicks > 0) { + return Math.max(getHandle().invulnerableTicks, getHandle().noDamageTicks); + } else { + return getHandle().noDamageTicks; + } + } + + public void setFlySpeed(float value) { + validateSpeed(value); + EntityPlayer player = getHandle(); + player.abilities.flySpeed = Math.max(value, 0.0001f) / 2f; // Spigot + player.updateAbilities(); + + } + + public void setWalkSpeed(float value) { + validateSpeed(value); + EntityPlayer player = getHandle(); + player.abilities.walkSpeed = Math.max(value, 0.0001f) / 2f; // Spigot + player.updateAbilities(); + } + + public float getFlySpeed() { + return getHandle().abilities.flySpeed * 2f; + } + + public float getWalkSpeed() { + return getHandle().abilities.walkSpeed * 2f; + } + + private void validateSpeed(float value) { + if (value < 0) { + if (value < -1f) { + throw new IllegalArgumentException(value + " is too low"); + } + } else { + if (value > 1f) { + throw new IllegalArgumentException(value + " is too high"); + } + } + } + + @Override + public void setMaxHealth(double amount) { + super.setMaxHealth(amount); + this.health = Math.min(this.health, health); + getHandle().triggerHealthUpdate(); + } + + @Override + public void resetMaxHealth() { + super.resetMaxHealth(); + getHandle().triggerHealthUpdate(); + } + + public CraftScoreboard getScoreboard() { + return this.server.getScoreboardManager().getPlayerBoard(this); + } + + public void setScoreboard(Scoreboard scoreboard) { + Validate.notNull(scoreboard, "Scoreboard cannot be null"); + PlayerConnection playerConnection = getHandle().playerConnection; + if (playerConnection == null) { + throw new IllegalStateException("Cannot set scoreboard yet"); + } + if (playerConnection.isDisconnected()) { + // throw new IllegalStateException("Cannot set scoreboard for invalid CraftPlayer"); // Spigot - remove this as Mojang's semi asynchronous Netty implementation can lead to races + } + + this.server.getScoreboardManager().setPlayerBoard(this, scoreboard); + } + + public void setHealthScale(double value) { + Validate.isTrue((float) value > 0F, "Must be greater than 0"); + healthScale = value; + scaledHealth = true; + updateScaledHealth(); + } + + public double getHealthScale() { + return healthScale; + } + + public void setHealthScaled(boolean scale) { + if (scaledHealth != (scaledHealth = scale)) { + updateScaledHealth(); + } + } + + public boolean isHealthScaled() { + return scaledHealth; + } + + public float getScaledHealth() { + return (float) (isHealthScaled() ? getHealth() * getHealthScale() / getMaxHealth() : getHealth()); + } + + @Override + public double getHealth() { + return health; + } + + public void setRealHealth(double health) { + double previous = this.health; // MineHQ + + this.health = health; + + // MineHQ start + if (previous != health) { + Bukkit.getPluginManager().callEvent(new PlayerHealthChangeEvent(this, previous, health)); + } + // MineHQ end + } + + public void updateScaledHealth() { + AttributeMapServer attributemapserver = (AttributeMapServer) getHandle().getAttributeMap(); + Set set = attributemapserver.getAttributes(); + + injectScaledMaxHealth(set, true); + + getHandle().getDataWatcher().watch(6, (float) getScaledHealth()); + getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel())); + getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(getHandle().getId(), set)); + + set.clear(); + getHandle().maxHealthCache = getMaxHealth(); + } + + public void injectScaledMaxHealth(Collection collection, boolean force) { + if (!scaledHealth && !force) { + return; + } + for (Object genericInstance : collection) { + IAttribute attribute = ((AttributeInstance) genericInstance).getAttribute(); + if (attribute.getName().equals("generic.maxHealth")) { + collection.remove(genericInstance); + break; + } + } + // Spigot start + double healthMod = scaledHealth ? healthScale : getMaxHealth(); + if (healthMod >= Float.MAX_VALUE || healthMod <= 0) { + healthMod = 20; // Reset health + getServer().getLogger().warning(getName() + " tried to crash the server with a large health attribute"); + } + collection.add(new AttributeModifiable(getHandle().getAttributeMap(), (new AttributeRanged("generic.maxHealth", healthMod, 0.0D, Float.MAX_VALUE)).a("Max Health").a(true))); + // Spigot end + } + + // Spigot start + private final Player.Spigot spigot = new Player.Spigot() { + + @Override + public InetSocketAddress getRawAddress() { + return (InetSocketAddress) getHandle().playerConnection.networkManager.getRawAddress(); + } + + @Override + public boolean getCollidesWithEntities() { + return getHandle().collidesWithEntities; + } + + @Override + public void setCollidesWithEntities(boolean collides) { + getHandle().collidesWithEntities = collides; + getHandle().k = collides; // First boolean of Entity + } + + @Override + public void respawn() { + if (getHealth() <= 0 && isOnline()) { + server.getServer().getPlayerList().moveToWorld(getHandle(), 0, false); + } + } + + @Override + public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) { + Validate.notNull(location, "Location cannot be null"); + Validate.notNull(effect, "Effect cannot be null"); + Validate.notNull(location.getWorld(), "World cannot be null"); + Packet packet; + if (effect.getType() != Effect.Type.PARTICLE) { + int packetData = effect.getId(); + packet = new PacketPlayOutWorldEvent(packetData, location.getBlockX(), location.getBlockY(), location.getBlockZ(), id, false); + } else { + StringBuilder particleFullName = new StringBuilder(); + particleFullName.append(effect.getName()); + if (effect.getData() != null && (effect.getData().equals(Material.class) || effect.getData().equals(org.bukkit.material.MaterialData.class))) { + particleFullName.append('_').append(id); + } + if (effect.getData() != null && effect.getData().equals(org.bukkit.material.MaterialData.class)) { + particleFullName.append('_').append(data); + } + packet = new PacketPlayOutWorldParticles(particleFullName.toString(), (float) location.getX(), (float) location.getY(), (float) location.getZ(), offsetX, offsetY, offsetZ, speed, particleCount); + } + int distance; + radius *= radius; + if (getHandle().playerConnection == null) { + return; + } + if (!location.getWorld().equals(getWorld())) { + return; + } + + distance = (int) getLocation().distanceSquared(location); + if (distance <= radius) { + getHandle().playerConnection.sendPacket(packet); + } + } + + @Override + public String getLocale() { + return getHandle().locale; + } + + @Override + public Set getHiddenPlayers() { + Set ret = new HashSet(); + for (UUID u : hiddenPlayers) { + ret.add(getServer().getPlayer(u)); + } + + return java.util.Collections.unmodifiableSet(ret); + } + + @Override + public void sendMessage(BaseComponent component) { + sendMessage(new BaseComponent[]{component}); + } + + @Override + public void sendMessage(BaseComponent... components) { + if (getHandle().playerConnection == null) return; + + PacketPlayOutChat packet = new PacketPlayOutChat(); + packet.components = components; + getHandle().playerConnection.sendPacket(packet); + } + + // PaperSpigot start - Add affects spawning API + public void setAffectsSpawning(boolean affects) { + getHandle().affectsSpawning = affects; + } + + public boolean getAffectsSpawning() { + return getHandle().affectsSpawning; + } + // PaperSpigot end + + }; + + public Player.Spigot spigot() { + return spigot; + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java new file mode 100644 index 0000000..2f29f2f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java @@ -0,0 +1,62 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityProjectile; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.projectiles.ProjectileSource; + +public abstract class CraftProjectile extends AbstractProjectile implements Projectile { + public CraftProjectile(CraftServer server, net.minecraft.server.Entity entity) { + super(server, entity); + } + + public ProjectileSource getShooter() { + return getHandle().projectileSource; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof CraftLivingEntity) { + getHandle().shooter = (EntityLiving) ((CraftLivingEntity) shooter).entity; + if (shooter instanceof CraftHumanEntity) { + getHandle().shooterName = ((CraftHumanEntity) shooter).getName(); + } + } else { + getHandle().shooter = null; + getHandle().shooterName = null; + } + getHandle().projectileSource = shooter; + } + + @Override + public EntityProjectile getHandle() { + return (EntityProjectile) entity; + } + + @Override + public String toString() { + return "CraftProjectile"; + } + + + @Deprecated + public LivingEntity _INVALID_getShooter() { + if (getHandle().shooter == null) { + return null; + } + return (LivingEntity) getHandle().shooter.getBukkitEntity(); + } + + @Deprecated + public void _INVALID_setShooter(LivingEntity shooter) { + if (shooter == null) { + return; + } + getHandle().shooter = ((CraftLivingEntity) shooter).getHandle(); + if (shooter instanceof CraftHumanEntity) { + getHandle().shooterName = ((CraftHumanEntity) shooter).getName(); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSheep.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSheep.java new file mode 100644 index 0000000..81b938a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSheep.java @@ -0,0 +1,44 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySheep; + +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Sheep; + +public class CraftSheep extends CraftAnimals implements Sheep { + public CraftSheep(CraftServer server, EntitySheep entity) { + super(server, entity); + } + + public DyeColor getColor() { + return DyeColor.getByWoolData((byte) getHandle().getColor()); + } + + public void setColor(DyeColor color) { + getHandle().setColor(color.getWoolData()); + } + + public boolean isSheared() { + return getHandle().isSheared(); + } + + public void setSheared(boolean flag) { + getHandle().setSheared(flag); + } + + @Override + public EntitySheep getHandle() { + return (EntitySheep) entity; + } + + @Override + public String toString() { + return "CraftSheep"; + } + + public EntityType getType() { + return EntityType.SHEEP; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSilverfish.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSilverfish.java new file mode 100644 index 0000000..ef94775 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSilverfish.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySilverfish; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Silverfish; + +public class CraftSilverfish extends CraftMonster implements Silverfish { + public CraftSilverfish(CraftServer server, EntitySilverfish entity) { + super(server, entity); + } + + @Override + public EntitySilverfish getHandle() { + return (EntitySilverfish) entity; + } + + @Override + public String toString() { + return "CraftSilverfish"; + } + + public EntityType getType() { + return EntityType.SILVERFISH; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java new file mode 100644 index 0000000..58da899 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySkeleton; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Skeleton; + +public class CraftSkeleton extends CraftMonster implements Skeleton { + + public CraftSkeleton(CraftServer server, EntitySkeleton entity) { + super(server, entity); + } + + @Override + public EntitySkeleton getHandle() { + return (EntitySkeleton) entity; + } + + @Override + public String toString() { + return "CraftSkeleton"; + } + + public EntityType getType() { + return EntityType.SKELETON; + } + + public SkeletonType getSkeletonType() { + return SkeletonType.getType(getHandle().getSkeletonType()); + } + + public void setSkeletonType(SkeletonType type) { + Validate.notNull(type); + getHandle().setSkeletonType(type.getId()); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java new file mode 100644 index 0000000..da3df00 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySlime; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Slime; + +public class CraftSlime extends CraftLivingEntity implements Slime { + + public CraftSlime(CraftServer server, EntitySlime entity) { + super(server, entity); + } + + public int getSize() { + return getHandle().getSize(); + } + + public void setSize(int size) { + getHandle().setSize(size); + } + + @Override + public EntitySlime getHandle() { + return (EntitySlime) entity; + } + + @Override + public String toString() { + return "CraftSlime"; + } + + public EntityType getType() { + return EntityType.SLIME; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSmallFireball.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSmallFireball.java new file mode 100644 index 0000000..7200970 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSmallFireball.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySmallFireball; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.SmallFireball; + +public class CraftSmallFireball extends CraftFireball implements SmallFireball { + public CraftSmallFireball(CraftServer server, EntitySmallFireball entity) { + super(server, entity); + } + + @Override + public EntitySmallFireball getHandle() { + return (EntitySmallFireball) entity; + } + + @Override + public String toString() { + return "CraftSmallFireball"; + } + + public EntityType getType() { + return EntityType.SMALL_FIREBALL; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowball.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowball.java new file mode 100644 index 0000000..44de749 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowball.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySnowball; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Snowball; + +public class CraftSnowball extends CraftProjectile implements Snowball { + public CraftSnowball(CraftServer server, EntitySnowball entity) { + super(server, entity); + } + + @Override + public EntitySnowball getHandle() { + return (EntitySnowball) entity; + } + + @Override + public String toString() { + return "CraftSnowball"; + } + + public EntityType getType() { + return EntityType.SNOWBALL; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java new file mode 100644 index 0000000..319a3f3 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySnowman; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Snowman; + +public class CraftSnowman extends CraftGolem implements Snowman { + public CraftSnowman(CraftServer server, EntitySnowman entity) { + super(server, entity); + } + + @Override + public EntitySnowman getHandle() { + return (EntitySnowman) entity; + } + + @Override + public String toString() { + return "CraftSnowman"; + } + + public EntityType getType() { + return EntityType.SNOWMAN; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSpider.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSpider.java new file mode 100644 index 0000000..2a07fe1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSpider.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySpider; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spider; + +public class CraftSpider extends CraftMonster implements Spider { + + public CraftSpider(CraftServer server, EntitySpider entity) { + super(server, entity); + } + + @Override + public EntitySpider getHandle() { + return (EntitySpider) entity; + } + + @Override + public String toString() { + return "CraftSpider"; + } + + public EntityType getType() { + return EntityType.SPIDER; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSquid.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSquid.java new file mode 100644 index 0000000..433a7c6 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSquid.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySquid; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Squid; + +public class CraftSquid extends CraftWaterMob implements Squid { + + public CraftSquid(CraftServer server, EntitySquid entity) { + super(server, entity); + } + + @Override + public EntitySquid getHandle() { + return (EntitySquid) entity; + } + + @Override + public String toString() { + return "CraftSquid"; + } + + public EntityType getType() { + return EntityType.SQUID; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java new file mode 100644 index 0000000..d4167b7 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java @@ -0,0 +1,75 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityTNTPrimed; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.TNTPrimed; + +public class CraftTNTPrimed extends CraftEntity implements TNTPrimed { + + public CraftTNTPrimed(CraftServer server, EntityTNTPrimed entity) { + super(server, entity); + } + + public float getYield() { + return getHandle().yield; + } + + public boolean isIncendiary() { + return getHandle().isIncendiary; + } + + public void setIsIncendiary(boolean isIncendiary) { + getHandle().isIncendiary = isIncendiary; + } + + public void setYield(float yield) { + getHandle().yield = yield; + } + + public int getFuseTicks() { + return getHandle().fuseTicks; + } + + public void setFuseTicks(int fuseTicks) { + getHandle().fuseTicks = fuseTicks; + } + + @Override + public EntityTNTPrimed getHandle() { + return (EntityTNTPrimed) entity; + } + + @Override + public String toString() { + return "CraftTNTPrimed"; + } + + public EntityType getType() { + return EntityType.PRIMED_TNT; + } + + public Entity getSource() { + EntityLiving source = getHandle().getSource(); + + if (source != null) { + Entity bukkitEntity = source.getBukkitEntity(); + + if (bukkitEntity.isValid()) { + return bukkitEntity; + } + } + + return null; + } + + // PaperSpigot start - Add FallingBlock and TNT source location API + @Override + public org.bukkit.Location getSourceLoc() { + return getHandle().sourceLoc; + } + // PaperSpigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java new file mode 100644 index 0000000..d4bf3a0 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java @@ -0,0 +1,84 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityTameableAnimal; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Creature; +import org.bukkit.entity.Tameable; + +import java.util.UUID; + +public class CraftTameableAnimal extends CraftAnimals implements Tameable, Creature { + public CraftTameableAnimal(CraftServer server, EntityTameableAnimal entity) { + super(server, entity); + } + + @Override + public EntityTameableAnimal getHandle() { + return (EntityTameableAnimal)super.getHandle(); + } + + public UUID getOwnerUUID() { + try { + return UUID.fromString(getHandle().getOwnerUUID()); + } catch (IllegalArgumentException ex) { + return null; + } + } + + public void setOwnerUUID(UUID uuid) { + if (uuid == null) { + getHandle().setOwnerUUID(""); + } else { + getHandle().setOwnerUUID(uuid.toString()); + } + } + + public AnimalTamer getOwner() { + if (getOwnerUUID() == null) { + return null; + } + + AnimalTamer owner = getServer().getPlayer(getOwnerUUID()); + if (owner == null) { + owner = getServer().getOfflinePlayer(getOwnerUUID()); + } + + return owner; + } + + public boolean isTamed() { + return getHandle().isTamed(); + } + + public void setOwner(AnimalTamer tamer) { + if (tamer != null) { + setTamed(true); + getHandle().setPathEntity(null); + setOwnerUUID(tamer.getUniqueId()); + } else { + setTamed(false); + setOwnerUUID(null); + } + } + + public void setTamed(boolean tame) { + getHandle().setTamed(tame); + if (!tame) { + setOwnerUUID(null); + } + } + + public boolean isSitting() { + return getHandle().isSitting(); + } + + public void setSitting(boolean sitting) { + getHandle().getGoalSit().setSitting(sitting); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{owner=" + getOwner() + ",tamed=" + isTamed() + "}"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownExpBottle.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownExpBottle.java new file mode 100644 index 0000000..fb3416e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownExpBottle.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityThrownExpBottle; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ThrownExpBottle; + +public class CraftThrownExpBottle extends CraftProjectile implements ThrownExpBottle { + public CraftThrownExpBottle(CraftServer server, EntityThrownExpBottle entity) { + super(server, entity); + } + + @Override + public EntityThrownExpBottle getHandle() { + return (EntityThrownExpBottle) entity; + } + + @Override + public String toString() { + return "EntityThrownExpBottle"; + } + + public EntityType getType() { + return EntityType.THROWN_EXP_BOTTLE; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java new file mode 100644 index 0000000..18da426 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java @@ -0,0 +1,58 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.Collection; + +import net.minecraft.server.EntityPotion; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.Potion; +import org.bukkit.potion.PotionEffect; + +public class CraftThrownPotion extends CraftProjectile implements ThrownPotion { + public CraftThrownPotion(CraftServer server, EntityPotion entity) { + super(server, entity); + } + + // TODO: This one does not handle custom NBT potion effects does it? + // In that case this method could be said to be misleading or incorrect + public Collection getEffects() { + return Potion.getBrewer().getEffectsFromDamage(getHandle().getPotionValue()); + } + + public ItemStack getItem() { + // We run this method once since it will set the item stack if there is none. + getHandle().getPotionValue(); + + return CraftItemStack.asBukkitCopy(getHandle().item); + } + + public void setItem(ItemStack item) { + // The ItemStack must not be null. + Validate.notNull(item, "ItemStack cannot be null."); + + // The ItemStack must be a potion. + Validate.isTrue(item.getType() == Material.POTION, "ItemStack must be a potion. This item stack was " + item.getType() + "."); + + getHandle().item = CraftItemStack.asNMSCopy(item); + } + + @Override + public EntityPotion getHandle() { + return (EntityPotion) entity; + } + + @Override + public String toString() { + return "CraftThrownPotion"; + } + + public EntityType getType() { + return EntityType.SPLASH_POTION; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVehicle.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVehicle.java new file mode 100644 index 0000000..8e4af46 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVehicle.java @@ -0,0 +1,15 @@ +package org.bukkit.craftbukkit.entity; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Vehicle; + +public abstract class CraftVehicle extends CraftEntity implements Vehicle { + public CraftVehicle(CraftServer server, net.minecraft.server.Entity entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftVehicle{passenger=" + getPassenger() + '}'; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java new file mode 100644 index 0000000..398029b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityVillager; +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Villager; + +public class CraftVillager extends CraftAgeable implements Villager { + public CraftVillager(CraftServer server, EntityVillager entity) { + super(server, entity); + } + + @Override + public EntityVillager getHandle() { + return (EntityVillager) entity; + } + + @Override + public String toString() { + return "CraftVillager"; + } + + public EntityType getType() { + return EntityType.VILLAGER; + } + + public Profession getProfession() { + return Profession.getProfession(getHandle().getProfession()); + } + + public void setProfession(Profession profession) { + Validate.notNull(profession); + getHandle().setProfession(profession.getId()); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWaterMob.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWaterMob.java new file mode 100644 index 0000000..39e8d89 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWaterMob.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWaterAnimal; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.WaterMob; + +public class CraftWaterMob extends CraftCreature implements WaterMob { + + public CraftWaterMob(CraftServer server, EntityWaterAnimal entity) { + super(server, entity); + } + + @Override + public EntityWaterAnimal getHandle() { + return (EntityWaterAnimal) entity; + } + + @Override + public String toString() { + return "CraftWaterMob"; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWeather.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWeather.java new file mode 100644 index 0000000..91a7493 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWeather.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWeather; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Weather; + +public class CraftWeather extends CraftEntity implements Weather { + public CraftWeather(final CraftServer server, final EntityWeather entity) { + super(server, entity); + } + + @Override + public EntityWeather getHandle() { + return (EntityWeather) entity; + } + + @Override + public String toString() { + return "CraftWeather"; + } + + public EntityType getType() { + return EntityType.WEATHER; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java new file mode 100644 index 0000000..c08833c --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWitch; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Witch; +import org.bukkit.entity.EntityType; + +public class CraftWitch extends CraftMonster implements Witch { + public CraftWitch(CraftServer server, EntityWitch entity) { + super(server, entity); + } + + @Override + public EntityWitch getHandle() { + return (EntityWitch) entity; + } + + @Override + public String toString() { + return "CraftWitch"; + } + + public EntityType getType() { + return EntityType.WITCH; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java new file mode 100644 index 0000000..fad3db8 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWither; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Wither; +import org.bukkit.entity.EntityType; + +public class CraftWither extends CraftMonster implements Wither { + public CraftWither(CraftServer server, EntityWither entity) { + super(server, entity); + } + + @Override + public EntityWither getHandle() { + return (EntityWither) entity; + } + + @Override + public String toString() { + return "CraftWither"; + } + + public EntityType getType() { + return EntityType.WITHER; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkull.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkull.java new file mode 100644 index 0000000..563177e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkull.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWitherSkull; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.WitherSkull; + +public class CraftWitherSkull extends CraftFireball implements WitherSkull { + public CraftWitherSkull(CraftServer server, EntityWitherSkull entity) { + super(server, entity); + } + + @Override + public void setCharged(boolean charged) { + getHandle().setCharged(charged); + } + + @Override + public boolean isCharged() { + return getHandle().isCharged(); + } + + @Override + public EntityWitherSkull getHandle() { + return (EntityWitherSkull) entity; + } + + @Override + public String toString() { + return "CraftWitherSkull"; + } + + public EntityType getType() { + return EntityType.WITHER_SKULL; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java new file mode 100644 index 0000000..3d0e7cb --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java @@ -0,0 +1,39 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWolf; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Wolf; + +public class CraftWolf extends CraftTameableAnimal implements Wolf { + public CraftWolf(CraftServer server, EntityWolf wolf) { + super(server, wolf); + } + + public boolean isAngry() { + return getHandle().isAngry(); + } + + public void setAngry(boolean angry) { + getHandle().setAngry(angry); + } + + @Override + public EntityWolf getHandle() { + return (EntityWolf) entity; + } + + @Override + public EntityType getType() { + return EntityType.WOLF; + } + + public DyeColor getCollarColor() { + return DyeColor.getByWoolData((byte) getHandle().getCollarColor()); + } + + public void setCollarColor(DyeColor color) { + getHandle().setCollarColor(color.getWoolData()); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java new file mode 100644 index 0000000..619579d --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java @@ -0,0 +1,44 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityZombie; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Zombie; + +public class CraftZombie extends CraftMonster implements Zombie { + + public CraftZombie(CraftServer server, EntityZombie entity) { + super(server, entity); + } + + @Override + public EntityZombie getHandle() { + return (EntityZombie) entity; + } + + @Override + public String toString() { + return "CraftZombie"; + } + + public EntityType getType() { + return EntityType.ZOMBIE; + } + + public boolean isBaby() { + return getHandle().isBaby(); + } + + public void setBaby(boolean flag) { + getHandle().setBaby(flag); + } + + public boolean isVillager() { + return getHandle().isVillager(); + } + + public void setVillager(boolean flag) { + getHandle().setVillager(flag); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java new file mode 100644 index 0000000..220574b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -0,0 +1,932 @@ +package org.bukkit.craftbukkit.event; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +import com.google.common.base.Function; +import com.google.common.base.Functions; + +import net.minecraft.server.ChunkCoordinates; +import net.minecraft.server.Container; +import net.minecraft.server.DamageSource; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityArrow; +import net.minecraft.server.EntityDamageSource; +import net.minecraft.server.EntityDamageSourceIndirect; +import net.minecraft.server.EntityEnderCrystal; +import net.minecraft.server.EntityEnderDragon; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EntityItem; +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.EntityPotion; +import net.minecraft.server.Explosion; +import net.minecraft.server.InventoryCrafting; +import net.minecraft.server.ItemStack; +import net.minecraft.server.Items; +import net.minecraft.server.PacketPlayInCloseWindow; +import net.minecraft.server.PacketPlayOutSetSlot; +import net.minecraft.server.Slot; +import net.minecraft.server.World; +import net.minecraft.server.WorldServer; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.Statistic.Type; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftStatistic; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftDamageSource; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse; +import org.bukkit.entity.LightningStrike; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Pig; +import org.bukkit.entity.PigZombie; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.ThrownExpBottle; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.block.*; +import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; +import org.bukkit.event.entity.*; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.inventory.PrepareItemCraftEvent; +import org.bukkit.event.player.*; +import org.bukkit.event.server.ServerListPingEvent; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.meta.BookMeta; + +public class CraftEventFactory { + public static final DamageSource MELTING = CraftDamageSource.copyOf(DamageSource.BURN); + public static final DamageSource POISON = CraftDamageSource.copyOf(DamageSource.MAGIC); + public static org.bukkit.block.Block blockDamage; // For use in EntityDamageByBlockEvent + public static Entity entityDamage; // For use in EntityDamageByEntityEvent + + // helper methods + private static boolean canBuild(CraftWorld world, Player player, int x, int z) { + WorldServer worldServer = world.getHandle(); + int spawnSize = Bukkit.getServer().getSpawnRadius(); + + if (world.getHandle().dimension != 0) return true; + if (spawnSize <= 0) return true; + if (((CraftServer) Bukkit.getServer()).getHandle().getOPs().isEmpty()) return true; + if (player.isOp()) return true; + + ChunkCoordinates chunkcoordinates = worldServer.getSpawn(); + + int distanceFromSpawn = Math.max(Math.abs(x - chunkcoordinates.x), Math.abs(z - chunkcoordinates.z)); + return distanceFromSpawn > spawnSize; + } + + public static T callEvent(T event) { + Bukkit.getServer().getPluginManager().callEvent(event); + return event; + } + + /** + * Block place methods + */ + public static BlockMultiPlaceEvent callBlockMultiPlaceEvent(World world, EntityHuman who, List blockStates, int clickedX, int clickedY, int clickedZ) { + CraftWorld craftWorld = world.getWorld(); + CraftServer craftServer = world.getServer(); + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + + Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ); + + boolean canBuild = true; + for (int i = 0; i < blockStates.size(); i++) { + if (!canBuild(craftWorld, player, blockStates.get(i).getX(), blockStates.get(i).getZ())) { + canBuild = false; + break; + } + } + + BlockMultiPlaceEvent event = new BlockMultiPlaceEvent(blockStates, blockClicked, player.getItemInHand(), player, canBuild); + craftServer.getPluginManager().callEvent(event); + + return event; + } + + public static BlockPlaceEvent callBlockPlaceEvent(World world, EntityHuman who, BlockState replacedBlockState, int clickedX, int clickedY, int clickedZ) { + CraftWorld craftWorld = world.getWorld(); + CraftServer craftServer = world.getServer(); + + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + + Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ); + Block placedBlock = replacedBlockState.getBlock(); + + boolean canBuild = canBuild(craftWorld, player, placedBlock.getX(), placedBlock.getZ()); + + BlockPlaceEvent event = new BlockPlaceEvent(placedBlock, replacedBlockState, blockClicked, player.getItemInHand(), player, canBuild); + craftServer.getPluginManager().callEvent(event); + + return event; + } + + /** + * Mob spawner event + */ + public static SpawnerSpawnEvent callSpawnerSpawnEvent(Entity spawnee, int spawnerX, int spawnerY, int spawnerZ) { + org.bukkit.craftbukkit.entity.CraftEntity entity = spawnee.getBukkitEntity(); + BlockState state = entity.getWorld().getBlockAt(spawnerX, spawnerY, spawnerZ).getState(); + + if (!(state instanceof CreatureSpawner)) { + state = null; + } + + SpawnerSpawnEvent event = new SpawnerSpawnEvent(entity, (CreatureSpawner) state); + entity.getServer().getPluginManager().callEvent(event); + return event; + } + + /** + * Bucket methods + */ + public static PlayerBucketEmptyEvent callPlayerBucketEmptyEvent(EntityHuman who, int clickedX, int clickedY, int clickedZ, int clickedFace, ItemStack itemInHand) { + return (PlayerBucketEmptyEvent) getPlayerBucketEvent(false, who, clickedX, clickedY, clickedZ, clickedFace, itemInHand, Items.BUCKET); + } + + public static PlayerBucketFillEvent callPlayerBucketFillEvent(EntityHuman who, int clickedX, int clickedY, int clickedZ, int clickedFace, ItemStack itemInHand, net.minecraft.server.Item bucket) { + return (PlayerBucketFillEvent) getPlayerBucketEvent(true, who, clickedX, clickedY, clickedZ, clickedFace, itemInHand, bucket); + } + + private static PlayerEvent getPlayerBucketEvent(boolean isFilling, EntityHuman who, int clickedX, int clickedY, int clickedZ, int clickedFace, ItemStack itemstack, net.minecraft.server.Item item) { + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asNewCraftStack(item); + Material bucket = CraftMagicNumbers.getMaterial(itemstack.getItem()); + + CraftWorld craftWorld = (CraftWorld) player.getWorld(); + CraftServer craftServer = (CraftServer) player.getServer(); + + Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ); + BlockFace blockFace = CraftBlock.notchToBlockFace(clickedFace); + + PlayerEvent event = null; + if (isFilling) { + event = new PlayerBucketFillEvent(player, blockClicked, blockFace, bucket, itemInHand); + ((PlayerBucketFillEvent) event).setCancelled(!canBuild(craftWorld, player, clickedX, clickedZ)); + } else { + event = new PlayerBucketEmptyEvent(player, blockClicked, blockFace, bucket, itemInHand); + ((PlayerBucketEmptyEvent) event).setCancelled(!canBuild(craftWorld, player, clickedX, clickedZ)); + } + + craftServer.getPluginManager().callEvent(event); + + return event; + } + + /** + * Player Interact event + */ + public static PlayerInteractEvent callPlayerInteractEvent(EntityHuman who, Action action, ItemStack itemstack) { + if (action != Action.LEFT_CLICK_AIR && action != Action.RIGHT_CLICK_AIR) { + throw new IllegalArgumentException(String.format("%s performing %s with %s", who, action, itemstack)); // Spigot + } + return callPlayerInteractEvent(who, action, 0, 256, 0, 0, itemstack); + } + + public static PlayerInteractEvent callPlayerInteractEvent(EntityHuman who, Action action, int clickedX, int clickedY, int clickedZ, int clickedFace, ItemStack itemstack) { + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asCraftMirror(itemstack); + + CraftWorld craftWorld = (CraftWorld) player.getWorld(); + CraftServer craftServer = (CraftServer) player.getServer(); + + Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ); + BlockFace blockFace = CraftBlock.notchToBlockFace(clickedFace); + + if (clickedY > 255) { + blockClicked = null; + switch (action) { + case LEFT_CLICK_BLOCK: + action = Action.LEFT_CLICK_AIR; + break; + case RIGHT_CLICK_BLOCK: + action = Action.RIGHT_CLICK_AIR; + break; + } + } + + if (itemInHand.getType() == Material.AIR || itemInHand.getAmount() == 0) { + itemInHand = null; + } + + PlayerInteractEvent event = new PlayerInteractEvent(player, action, itemInHand, blockClicked, blockFace); + craftServer.getPluginManager().callEvent(event); + + return event; + } + + /** + * EntityShootBowEvent + */ + public static EntityShootBowEvent callEntityShootBowEvent(EntityLiving who, ItemStack itemstack, EntityArrow entityArrow, float force) { + LivingEntity shooter = (LivingEntity) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asCraftMirror(itemstack); + Arrow arrow = (Arrow) entityArrow.getBukkitEntity(); + + if (itemInHand != null && (itemInHand.getType() == Material.AIR || itemInHand.getAmount() == 0)) { + itemInHand = null; + } + + EntityShootBowEvent event = new EntityShootBowEvent(shooter, itemInHand, arrow, force); + Bukkit.getPluginManager().callEvent(event); + + return event; + } + + /** + * BlockDamageEvent + */ + public static BlockDamageEvent callBlockDamageEvent(EntityHuman who, int x, int y, int z, ItemStack itemstack, boolean instaBreak) { + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asCraftMirror(itemstack); + + CraftWorld craftWorld = (CraftWorld) player.getWorld(); + CraftServer craftServer = (CraftServer) player.getServer(); + + Block blockClicked = craftWorld.getBlockAt(x, y, z); + + BlockDamageEvent event = new BlockDamageEvent(player, blockClicked, itemInHand, instaBreak); + craftServer.getPluginManager().callEvent(event); + + return event; + } + + /** + * CreatureSpawnEvent + */ + public static CreatureSpawnEvent callCreatureSpawnEvent(EntityLiving entityliving, SpawnReason spawnReason) { + LivingEntity entity = (LivingEntity) entityliving.getBukkitEntity(); + CraftServer craftServer = (CraftServer) entity.getServer(); + + CreatureSpawnEvent event = new CreatureSpawnEvent(entity, spawnReason); + craftServer.getPluginManager().callEvent(event); + return event; + } + + /** + * EntityTameEvent + */ + public static EntityTameEvent callEntityTameEvent(EntityInsentient entity, EntityHuman tamer) { + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + org.bukkit.entity.AnimalTamer bukkitTamer = (tamer != null ? tamer.getBukkitEntity() : null); + CraftServer craftServer = (CraftServer) bukkitEntity.getServer(); + + entity.persistent = true; + + EntityTameEvent event = new EntityTameEvent((LivingEntity) bukkitEntity, bukkitTamer); + craftServer.getPluginManager().callEvent(event); + return event; + } + + /** + * ItemSpawnEvent + */ + public static ItemSpawnEvent callItemSpawnEvent(EntityItem entityitem) { + org.bukkit.entity.Item entity = (org.bukkit.entity.Item) entityitem.getBukkitEntity(); + CraftServer craftServer = (CraftServer) entity.getServer(); + + ItemSpawnEvent event = new ItemSpawnEvent(entity, entity.getLocation()); + + craftServer.getPluginManager().callEvent(event); + return event; + } + + /** + * ItemDespawnEvent + */ + public static ItemDespawnEvent callItemDespawnEvent(EntityItem entityitem) { + org.bukkit.entity.Item entity = (org.bukkit.entity.Item) entityitem.getBukkitEntity(); + + ItemDespawnEvent event = new ItemDespawnEvent(entity, entity.getLocation()); + + entity.getServer().getPluginManager().callEvent(event); + return event; + } + + /** + * PotionSplashEvent + */ + public static PotionSplashEvent callPotionSplashEvent(EntityPotion potion, Map affectedEntities) { + ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); + + PotionSplashEvent event = new PotionSplashEvent(thrownPotion, affectedEntities); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + /** + * BlockFadeEvent + */ + public static BlockFadeEvent callBlockFadeEvent(Block block, net.minecraft.server.Block type) { + BlockState state = block.getState(); + state.setTypeId(net.minecraft.server.Block.getId(type)); + + BlockFadeEvent event = new BlockFadeEvent(block, state); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static void handleBlockSpreadEvent(Block block, Block source, net.minecraft.server.Block type, int data) { + BlockState state = block.getState(); + state.setTypeId(net.minecraft.server.Block.getId(type)); + state.setRawData((byte) data); + + BlockSpreadEvent event = new BlockSpreadEvent(block, source, state); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + state.update(true); + } + } + + public static EntityDeathEvent callEntityDeathEvent(EntityLiving victim) { + return callEntityDeathEvent(victim, new ArrayList(0)); + } + + public static EntityDeathEvent callEntityDeathEvent(EntityLiving victim, List drops) { + return callEntityDeathEvent(victim, drops, victim.getExpReward()); + } + + public static EntityDeathEvent callEntityDeathEvent(EntityLiving victim, List drops, int exp) { + CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity(); + EntityDeathEvent event = new EntityDeathEvent(entity, drops, exp); + CraftWorld world = (CraftWorld) entity.getWorld(); + Bukkit.getServer().getPluginManager().callEvent(event); + + victim.expToDrop = event.getDroppedExp(); + + for (org.bukkit.inventory.ItemStack stack : event.getDrops()) { + if (stack == null || stack.getType() == Material.AIR || stack.getAmount() == 0) continue; + + world.dropItemNaturally(entity.getLocation(), stack); + } + + return event; + } + + public static PlayerDeathEvent callPlayerDeathEvent(EntityPlayer victim, List drops, String deathMessage, boolean keepInventory) { + CraftPlayer entity = victim.getBukkitEntity(); + PlayerDeathEvent event = new PlayerDeathEvent(entity, drops, victim.getExpReward(), 0, deathMessage); + event.setKeepInventory(keepInventory); + org.bukkit.World world = entity.getWorld(); + Bukkit.getServer().getPluginManager().callEvent(event); + + victim.keepLevel = event.getKeepLevel(); + victim.newLevel = event.getNewLevel(); + victim.newTotalExp = event.getNewTotalExp(); + victim.expToDrop = event.getDroppedExp(); + victim.newExp = event.getNewExp(); + + if (event.getKeepInventory()) { + return event; + } + + for (org.bukkit.inventory.ItemStack stack : event.getDrops()) { + if (stack == null || stack.getType() == Material.AIR) continue; + + world.dropItemNaturally(entity.getLocation(), stack); + } + + return event; + } + + /** + * Server methods + */ + public static ServerListPingEvent callServerListPingEvent(Server craftServer, InetAddress address, String motd, int numPlayers, int maxPlayers) { + ServerListPingEvent event = new ServerListPingEvent(address, motd, numPlayers, maxPlayers); + craftServer.getPluginManager().callEvent(event); + return event; + } + + private static EntityDamageEvent handleEntityDamageEvent(Entity entity, DamageSource source, Map modifiers, Map> modifierFunctions) { + if (source.isExplosion()) { + DamageCause damageCause; + Entity damager = entityDamage; + entityDamage = null; + EntityDamageEvent event; + if (damager == null) { + event = new EntityDamageByBlockEvent(null, entity.getBukkitEntity(), DamageCause.BLOCK_EXPLOSION, modifiers, modifierFunctions); + } else if (entity instanceof EntityEnderDragon && ((EntityEnderDragon) entity).bC == damager) { + event = new EntityDamageEvent(entity.getBukkitEntity(), DamageCause.ENTITY_EXPLOSION, modifiers, modifierFunctions); + } else { + if (damager instanceof org.bukkit.entity.TNTPrimed) { + damageCause = DamageCause.BLOCK_EXPLOSION; + } else { + damageCause = DamageCause.ENTITY_EXPLOSION; + } + event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), entity.getBukkitEntity(), damageCause, modifiers, modifierFunctions); + } + + callEvent(event); + + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (source instanceof EntityDamageSource) { + Entity damager = source.getEntity(); + DamageCause cause = DamageCause.ENTITY_ATTACK; + + if (source instanceof EntityDamageSourceIndirect) { + damager = ((EntityDamageSourceIndirect) source).getProximateDamageSource(); + if (damager.getBukkitEntity() instanceof ThrownPotion) { + cause = DamageCause.MAGIC; + } else if (damager.getBukkitEntity() instanceof Projectile) { + cause = DamageCause.PROJECTILE; + } + } else if ("thorns".equals(source.translationIndex)) { + cause = DamageCause.THORNS; + } + + return callEntityDamageEvent(damager, entity, cause, modifiers, modifierFunctions); + } else if (source == DamageSource.OUT_OF_WORLD) { + EntityDamageEvent event = callEvent(new EntityDamageByBlockEvent(null, entity.getBukkitEntity(), DamageCause.VOID, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (source == DamageSource.LAVA) { + EntityDamageEvent event = callEvent(new EntityDamageByBlockEvent(null, entity.getBukkitEntity(), DamageCause.LAVA, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (blockDamage != null) { + DamageCause cause = null; + Block damager = blockDamage; + blockDamage = null; + if (source == DamageSource.CACTUS) { + cause = DamageCause.CONTACT; + } else { + throw new RuntimeException(String.format("Unhandled damage of %s by %s from %s", entity, damager, source.translationIndex)); // Spigot + } + EntityDamageEvent event = callEvent(new EntityDamageByBlockEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (entityDamage != null) { + DamageCause cause = null; + CraftEntity damager = entityDamage.getBukkitEntity(); + entityDamage = null; + if (source == DamageSource.ANVIL || source == DamageSource.FALLING_BLOCK) { + cause = DamageCause.FALLING_BLOCK; + } else if (damager instanceof LightningStrike) { + cause = DamageCause.LIGHTNING; + } else if (source == DamageSource.FALL) { + cause = DamageCause.FALL; + } else { + throw new RuntimeException(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.translationIndex)); // Spigot + } + EntityDamageEvent event = callEvent(new EntityDamageByEntityEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } + + DamageCause cause = null; + if (source == DamageSource.FIRE) { + cause = DamageCause.FIRE; + } else if (source == DamageSource.STARVE) { + cause = DamageCause.STARVATION; + } else if (source == DamageSource.WITHER) { + cause = DamageCause.WITHER; + } else if (source == DamageSource.STUCK) { + cause = DamageCause.SUFFOCATION; + } else if (source == DamageSource.DROWN) { + cause = DamageCause.DROWNING; + } else if (source == DamageSource.BURN) { + cause = DamageCause.FIRE_TICK; + } else if (source == MELTING) { + cause = DamageCause.MELTING; + } else if (source == POISON) { + cause = DamageCause.POISON; + } else if (source == DamageSource.MAGIC) { + cause = DamageCause.MAGIC; + } else if (source == DamageSource.FALL) { + cause = DamageCause.FALL; + } else if (source == DamageSource.GENERIC) { + return new EntityDamageEvent(entity.getBukkitEntity(), null, modifiers, modifierFunctions); + } + + if (cause != null) { + return callEntityDamageEvent(null, entity, cause, modifiers, modifierFunctions); + } + + throw new RuntimeException(String.format("Unhandled damage of %s from %s", entity, source.translationIndex)); // Spigot + } + + private static EntityDamageEvent callEntityDamageEvent(Entity damager, Entity damagee, DamageCause cause, Map modifiers, Map> modifierFunctions) { + EntityDamageEvent event; + if (damager != null) { + event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, modifiers, modifierFunctions); + } else { + event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, modifiers, modifierFunctions); + } + + callEvent(event); + + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + + return event; + } + + private static final Function ZERO = Functions.constant(-0.0); + + public static EntityDamageEvent handleLivingEntityDamageEvent(Entity damagee, DamageSource source, double rawDamage, double hardHatModifier, double blockingModifier, double armorModifier, double resistanceModifier, double magicModifier, double absorptionModifier, Function hardHat, Function blocking, Function armor, Function resistance, Function magic, Function absorption) { + Map modifiers = new EnumMap(DamageModifier.class); + Map> modifierFunctions = new EnumMap>(DamageModifier.class); + modifiers.put(DamageModifier.BASE, rawDamage); + modifierFunctions.put(DamageModifier.BASE, ZERO); + if (source == DamageSource.FALLING_BLOCK || source == DamageSource.ANVIL) { + modifiers.put(DamageModifier.HARD_HAT, hardHatModifier); + modifierFunctions.put(DamageModifier.HARD_HAT, hardHat); + } + if (damagee instanceof EntityHuman) { + modifiers.put(DamageModifier.BLOCKING, blockingModifier); + modifierFunctions.put(DamageModifier.BLOCKING, blocking); + } + modifiers.put(DamageModifier.ARMOR, armorModifier); + modifierFunctions.put(DamageModifier.ARMOR, armor); + modifiers.put(DamageModifier.RESISTANCE, resistanceModifier); + modifierFunctions.put(DamageModifier.RESISTANCE, resistance); + modifiers.put(DamageModifier.MAGIC, magicModifier); + modifierFunctions.put(DamageModifier.MAGIC, magic); + modifiers.put(DamageModifier.ABSORPTION, absorptionModifier); + modifierFunctions.put(DamageModifier.ABSORPTION, absorption); + return handleEntityDamageEvent(damagee, source, modifiers, modifierFunctions); + } + + // Non-Living Entities such as EntityEnderCrystal and EntityFireball need to call this + public static boolean handleNonLivingEntityDamageEvent(Entity entity, DamageSource source, double damage) { + return handleNonLivingEntityDamageEvent(entity, source, damage, true); + } + + public static boolean handleNonLivingEntityDamageEvent(Entity entity, DamageSource source, double damage, boolean cancelOnZeroDamage) { + if (entity instanceof EntityEnderCrystal && !(source instanceof EntityDamageSource)) { + return false; + } + + final EnumMap modifiers = new EnumMap(DamageModifier.class); + final EnumMap> functions = new EnumMap(DamageModifier.class); + + modifiers.put(DamageModifier.BASE, damage); + functions.put(DamageModifier.BASE, ZERO); + + final EntityDamageEvent event = handleEntityDamageEvent(entity, source, modifiers, functions); + if (event == null) { + return false; + } + return event.isCancelled() || (cancelOnZeroDamage && event.getDamage() == 0); + } + + public static PlayerLevelChangeEvent callPlayerLevelChangeEvent(Player player, int oldLevel, int newLevel) { + PlayerLevelChangeEvent event = new PlayerLevelChangeEvent(player, oldLevel, newLevel); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static PlayerExpChangeEvent callPlayerExpChangeEvent(EntityHuman entity, int expAmount) { + Player player = (Player) entity.getBukkitEntity(); + PlayerExpChangeEvent event = new PlayerExpChangeEvent(player, expAmount); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static void handleBlockGrowEvent(World world, int x, int y, int z, net.minecraft.server.Block type, int data) { + Block block = world.getWorld().getBlockAt(x, y, z); + CraftBlockState state = (CraftBlockState) block.getState(); + state.setTypeId(net.minecraft.server.Block.getId(type)); + state.setRawData((byte) data); + + BlockGrowEvent event = new BlockGrowEvent(block, state); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + state.update(true); + } + } + + public static FoodLevelChangeEvent callFoodLevelChangeEvent(EntityHuman entity, int level) { + FoodLevelChangeEvent event = new FoodLevelChangeEvent(entity.getBukkitEntity(), level); + entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static PigZapEvent callPigZapEvent(Entity pig, Entity lightning, Entity pigzombie) { + PigZapEvent event = new PigZapEvent((Pig) pig.getBukkitEntity(), (LightningStrike) lightning.getBukkitEntity(), (PigZombie) pigzombie.getBukkitEntity()); + pig.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static HorseJumpEvent callHorseJumpEvent(Entity horse, float power) { + HorseJumpEvent event = new HorseJumpEvent((Horse) horse.getBukkitEntity(), power); + horse.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(org.bukkit.entity.Entity entity, Block block, Material material) { + return callEntityChangeBlockEvent(entity, block, material, 0); + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(Entity entity, Block block, Material material) { + return callEntityChangeBlockEvent(entity.getBukkitEntity(), block, material, 0); + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(Entity entity, Block block, Material material, boolean cancelled) { + return callEntityChangeBlockEvent(entity.getBukkitEntity(), block, material, 0, cancelled); + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(Entity entity, int x, int y, int z, net.minecraft.server.Block type, int data) { + Block block = entity.world.getWorld().getBlockAt(x, y, z); + Material material = CraftMagicNumbers.getMaterial(type); + + return callEntityChangeBlockEvent(entity.getBukkitEntity(), block, material, data); + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(org.bukkit.entity.Entity entity, Block block, Material material, int data) { + return callEntityChangeBlockEvent(entity, block, material, data, false); + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(org.bukkit.entity.Entity entity, Block block, Material material, int data, boolean cancelled) { + EntityChangeBlockEvent event = new EntityChangeBlockEvent(entity, block, material, (byte) data); + event.setCancelled(cancelled); + entity.getServer().getPluginManager().callEvent(event); + return event; + } + + public static CreeperPowerEvent callCreeperPowerEvent(Entity creeper, Entity lightning, CreeperPowerEvent.PowerCause cause) { + CreeperPowerEvent event = new CreeperPowerEvent((Creeper) creeper.getBukkitEntity(), (LightningStrike) lightning.getBukkitEntity(), cause); + creeper.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityTargetEvent callEntityTargetEvent(Entity entity, Entity target, EntityTargetEvent.TargetReason reason) { + EntityTargetEvent event = new EntityTargetEvent(entity.getBukkitEntity(), target == null ? null : target.getBukkitEntity(), reason); + entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityTargetLivingEntityEvent callEntityTargetLivingEvent(Entity entity, EntityLiving target, EntityTargetEvent.TargetReason reason) { + EntityTargetLivingEntityEvent event = new EntityTargetLivingEntityEvent(entity.getBukkitEntity(), (LivingEntity) target.getBukkitEntity(), reason); + entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityBreakDoorEvent callEntityBreakDoorEvent(Entity entity, int x, int y, int z) { + org.bukkit.entity.Entity entity1 = entity.getBukkitEntity(); + Block block = entity1.getWorld().getBlockAt(x, y, z); + + EntityBreakDoorEvent event = new EntityBreakDoorEvent((LivingEntity) entity1, block); + entity1.getServer().getPluginManager().callEvent(event); + + return event; + } + + public static Container callInventoryOpenEvent(EntityPlayer player, Container container) { + if (player.activeContainer != player.defaultContainer) { // fire INVENTORY_CLOSE if one already open + player.playerConnection.a(new PacketPlayInCloseWindow(player.activeContainer.windowId)); + } + + CraftServer server = player.world.getServer(); + CraftPlayer craftPlayer = player.getBukkitEntity(); + player.activeContainer.transferTo(container, craftPlayer); + + InventoryOpenEvent event = new InventoryOpenEvent(container.getBukkitView()); + server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + container.transferTo(player.activeContainer, craftPlayer); + return null; + } + + return container; + } + + public static ItemStack callPreCraftEvent(InventoryCrafting matrix, ItemStack result, InventoryView lastCraftView, boolean isRepair) { + CraftInventoryCrafting inventory = new CraftInventoryCrafting(matrix, matrix.resultInventory); + inventory.setResult(CraftItemStack.asCraftMirror(result)); + + PrepareItemCraftEvent event = new PrepareItemCraftEvent(inventory, lastCraftView, isRepair); + Bukkit.getPluginManager().callEvent(event); + + org.bukkit.inventory.ItemStack bitem = event.getInventory().getResult(); + + return CraftItemStack.asNMSCopy(bitem); + } + + public static ProjectileLaunchEvent callProjectileLaunchEvent(Entity entity) { + Projectile bukkitEntity = (Projectile) entity.getBukkitEntity(); + ProjectileLaunchEvent event = new ProjectileLaunchEvent(bukkitEntity); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static ProjectileHitEvent callProjectileHitEvent(Entity entity) { + ProjectileHitEvent event = new ProjectileHitEvent((Projectile) entity.getBukkitEntity()); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static ExpBottleEvent callExpBottleEvent(Entity entity, int exp) { + ThrownExpBottle bottle = (ThrownExpBottle) entity.getBukkitEntity(); + ExpBottleEvent event = new ExpBottleEvent(bottle, exp); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static BlockRedstoneEvent callRedstoneChange(World world, int x, int y, int z, int oldCurrent, int newCurrent) { + BlockRedstoneEvent event = new BlockRedstoneEvent(world.getWorld().getBlockAt(x, y, z), oldCurrent, newCurrent); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static NotePlayEvent callNotePlayEvent(World world, int x, int y, int z, byte instrument, byte note) { + NotePlayEvent event = new NotePlayEvent(world.getWorld().getBlockAt(x, y, z), org.bukkit.Instrument.getByType(instrument), new org.bukkit.Note(note)); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static void callPlayerItemBreakEvent(EntityHuman human, ItemStack brokenItem) { + CraftItemStack item = CraftItemStack.asCraftMirror(brokenItem); + PlayerItemBreakEvent event = new PlayerItemBreakEvent((Player) human.getBukkitEntity(), item); + Bukkit.getPluginManager().callEvent(event); + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, int x, int y, int z, int igniterX, int igniterY, int igniterZ) { + org.bukkit.World bukkitWorld = world.getWorld(); + Block igniter = bukkitWorld.getBlockAt(igniterX, igniterY, igniterZ); + IgniteCause cause; + switch (igniter.getType()) { + case LAVA: + case STATIONARY_LAVA: + cause = IgniteCause.LAVA; + break; + case DISPENSER: + cause = IgniteCause.FLINT_AND_STEEL; + break; + case FIRE: // Fire or any other unknown block counts as SPREAD. + default: + cause = IgniteCause.SPREAD; + } + + BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(x, y, z), cause, igniter); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, int x, int y, int z, Entity igniter) { + org.bukkit.World bukkitWorld = world.getWorld(); + org.bukkit.entity.Entity bukkitIgniter = igniter.getBukkitEntity(); + IgniteCause cause; + switch (bukkitIgniter.getType()) { + case ENDER_CRYSTAL: + cause = IgniteCause.ENDER_CRYSTAL; + break; + case LIGHTNING: + cause = IgniteCause.LIGHTNING; + break; + case SMALL_FIREBALL: + case FIREBALL: + cause = IgniteCause.FIREBALL; + break; + default: + cause = IgniteCause.FLINT_AND_STEEL; + } + + BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(x, y, z), cause, bukkitIgniter); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, int x, int y, int z, Explosion explosion) { + org.bukkit.World bukkitWorld = world.getWorld(); + org.bukkit.entity.Entity igniter = explosion.source == null ? null : explosion.source.getBukkitEntity(); + + BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(x, y, z), IgniteCause.EXPLOSION, igniter); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, int x, int y, int z, IgniteCause cause, Entity igniter) { + BlockIgniteEvent event = new BlockIgniteEvent(world.getWorld().getBlockAt(x, y, z), cause, igniter.getBukkitEntity()); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static void handleInventoryCloseEvent(EntityHuman human) { + if (human.activeContainer == human.defaultContainer) { // Spigot Update - 20141001a + return; + } + InventoryCloseEvent event = new InventoryCloseEvent(human.activeContainer.getBukkitView()); + human.world.getServer().getPluginManager().callEvent(event); + human.activeContainer.transferTo(human.defaultContainer, human.getBukkitEntity()); + } + + public static void handleEditBookEvent(EntityPlayer player, ItemStack newBookItem) { + int itemInHandIndex = player.inventory.itemInHandIndex; + + PlayerEditBookEvent editBookEvent = new PlayerEditBookEvent(player.getBukkitEntity(), player.inventory.itemInHandIndex, (BookMeta) CraftItemStack.getItemMeta(player.inventory.getItemInHand()), (BookMeta) CraftItemStack.getItemMeta(newBookItem), newBookItem.getItem() == Items.WRITTEN_BOOK); + player.world.getServer().getPluginManager().callEvent(editBookEvent); + ItemStack itemInHand = player.inventory.getItem(itemInHandIndex); + + // If they've got the same item in their hand, it'll need to be updated. + if (itemInHand != null && itemInHand.getItem() == Items.BOOK_AND_QUILL) { + if (!editBookEvent.isCancelled()) { + CraftItemStack.setItemMeta(itemInHand, editBookEvent.getNewBookMeta()); + if (editBookEvent.isSigning()) { + itemInHand.setItem(Items.WRITTEN_BOOK); + } + } + + // Client will have updated its idea of the book item; we need to overwrite that + Slot slot = player.activeContainer.getSlot(player.inventory, itemInHandIndex); + player.playerConnection.sendPacket(new PacketPlayOutSetSlot(player.activeContainer.windowId, slot.rawSlotIndex, itemInHand)); + } + } + + public static PlayerUnleashEntityEvent callPlayerUnleashEntityEvent(EntityInsentient entity, EntityHuman player) { + PlayerUnleashEntityEvent event = new PlayerUnleashEntityEvent(entity.getBukkitEntity(), (Player) player.getBukkitEntity()); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static PlayerLeashEntityEvent callPlayerLeashEntityEvent(EntityInsentient entity, Entity leashHolder, EntityHuman player) { + PlayerLeashEntityEvent event = new PlayerLeashEntityEvent(entity.getBukkitEntity(), leashHolder.getBukkitEntity(), (Player) player.getBukkitEntity()); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static Cancellable handleStatisticsIncrease(EntityHuman entityHuman, net.minecraft.server.Statistic statistic, int current, int incrementation) { + Player player = ((EntityPlayer) entityHuman).getBukkitEntity(); + Event event; + if (statistic instanceof net.minecraft.server.Achievement) { + if (current != 0) { + return null; + } + event = new PlayerAchievementAwardedEvent(player, CraftStatistic.getBukkitAchievement((net.minecraft.server.Achievement) statistic)); + } else { + org.bukkit.Statistic stat = CraftStatistic.getBukkitStatistic(statistic); + switch (stat) { + case FALL_ONE_CM: + case BOAT_ONE_CM: + case CLIMB_ONE_CM: + case DIVE_ONE_CM: + case FLY_ONE_CM: + case HORSE_ONE_CM: + case MINECART_ONE_CM: + case PIG_ONE_CM: + case PLAY_ONE_TICK: + case SWIM_ONE_CM: + case WALK_ONE_CM: + // Do not process event for these - too spammy + return null; + default: + } + if (stat.getType() == Type.UNTYPED) { + event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation); + } else if (stat.getType() == Type.ENTITY) { + EntityType entityType = CraftStatistic.getEntityTypeFromStatistic(statistic); + event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation, entityType); + } else { + Material material = CraftStatistic.getMaterialFromStatistic(statistic); + event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation, material); + } + } + entityHuman.world.getServer().getPluginManager().callEvent(event); + return (Cancellable) event; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java new file mode 100644 index 0000000..9a46d0c --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java @@ -0,0 +1,230 @@ +package org.bukkit.craftbukkit.generator; + +import java.util.List; +import java.util.Random; + +import net.minecraft.server.BiomeBase; +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkPosition; +import net.minecraft.server.ChunkSection; +import net.minecraft.server.EnumCreatureType; +import net.minecraft.server.IChunkProvider; +import net.minecraft.server.IProgressUpdate; +import net.minecraft.server.World; +import net.minecraft.server.WorldGenStronghold; +import net.minecraft.server.WorldServer; + +import org.bukkit.block.Biome; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.craftbukkit.block.CraftBlock; + +public class CustomChunkGenerator extends InternalChunkGenerator { + private final ChunkGenerator generator; + private final WorldServer world; + private final Random random; + private final WorldGenStronghold strongholdGen = new WorldGenStronghold(); + + private static class CustomBiomeGrid implements BiomeGrid { + BiomeBase[] biome; + + public Biome getBiome(int x, int z) { + return CraftBlock.biomeBaseToBiome(biome[(z << 4) | x]); + } + + public void setBiome(int x, int z, Biome bio) { + biome[(z << 4) | x] = CraftBlock.biomeToBiomeBase(bio); + } + } + + public CustomChunkGenerator(World world, long seed, ChunkGenerator generator) { + this.world = (WorldServer) world; + this.generator = generator; + + this.random = new Random(seed); + } + + public boolean isChunkLoaded(int x, int z) { + return true; + } + + public Chunk getOrCreateChunk(int x, int z) { + random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); + + Chunk chunk; + + // Get default biome data for chunk + CustomBiomeGrid biomegrid = new CustomBiomeGrid(); + biomegrid.biome = new BiomeBase[256]; + world.getWorldChunkManager().getBiomeBlock(biomegrid.biome, x << 4, z << 4, 16, 16); + + // Try extended block method (1.2+) + short[][] xbtypes = generator.generateExtBlockSections(this.world.getWorld(), this.random, x, z, biomegrid); + if (xbtypes != null) { + chunk = new Chunk(this.world, x, z); + + ChunkSection[] csect = chunk.getSections(); + int scnt = Math.min(csect.length, xbtypes.length); + + // Loop through returned sections + for (int sec = 0; sec < scnt; sec++) { + if (xbtypes[sec] == null) { + continue; + } + byte[] secBlkID = new byte[4096]; // Allocate blk ID bytes + byte[] secExtBlkID = null; // Delay getting extended ID nibbles + short[] bdata = xbtypes[sec]; + // Loop through data, 2 blocks at a time + for (int i = 0, j = 0; i < bdata.length; i += 2, j++) { + short b1 = bdata[i]; + short b2 = bdata[i + 1]; + byte extb = (byte) ((b1 >> 8) | ((b2 >> 4) & 0xF0)); + + secBlkID[i] = (byte) b1; + secBlkID[(i + 1)] = (byte) b2; + + if (extb != 0) { // If extended block ID data + if (secExtBlkID == null) { // Allocate if needed + secExtBlkID = new byte[2048]; + } + secExtBlkID[j] = extb; + } + } + // Build chunk section + csect[sec] = new ChunkSection(sec << 4, true, secBlkID, secExtBlkID); + } + } + else { // Else check for byte-per-block section data + byte[][] btypes = generator.generateBlockSections(this.world.getWorld(), this.random, x, z, biomegrid); + + if (btypes != null) { + chunk = new Chunk(this.world, x, z); + + ChunkSection[] csect = chunk.getSections(); + int scnt = Math.min(csect.length, btypes.length); + + for (int sec = 0; sec < scnt; sec++) { + if (btypes[sec] == null) { + continue; + } + csect[sec] = new ChunkSection(sec << 4, true, btypes[sec], null); + } + } + else { // Else, fall back to pre 1.2 method + @SuppressWarnings("deprecation") + byte[] types = generator.generate(this.world.getWorld(), this.random, x, z); + int ydim = types.length / 256; + int scnt = ydim / 16; + + chunk = new Chunk(this.world, x, z); // Create empty chunk + + ChunkSection[] csect = chunk.getSections(); + + scnt = Math.min(scnt, csect.length); + // Loop through sections + for (int sec = 0; sec < scnt; sec++) { + ChunkSection cs = null; // Add sections when needed + byte[] csbytes = null; + + for (int cy = 0; cy < 16; cy++) { + int cyoff = cy | (sec << 4); + + for (int cx = 0; cx < 16; cx++) { + int cxyoff = (cx * ydim * 16) + cyoff; + + for (int cz = 0; cz < 16; cz++) { + byte blk = types[cxyoff + (cz * ydim)]; + + if (blk != 0) { // If non-empty + if (cs == null) { // If no section yet, get one + cs = csect[sec] = new ChunkSection(sec << 4, true); + csbytes = cs.getIdArray(); + } + csbytes[(cy << 8) | (cz << 4) | cx] = blk; + } + } + } + } + // If section built, finish prepping its state + if (cs != null) { + cs.recalcBlockCounts(); + } + } + } + } + // Set biome grid + byte[] biomeIndex = chunk.m(); + for (int i = 0; i < biomeIndex.length; i++) { + biomeIndex[i] = (byte) (biomegrid.biome[i].id & 0xFF); + } + // Initialize lighting + chunk.initLighting(); + + return chunk; + } + + public void getChunkAt(IChunkProvider icp, int i, int i1) { + // Nothing! + } + + public boolean saveChunks(boolean bln, IProgressUpdate ipu) { + return true; + } + + public boolean unloadChunks() { + return false; + } + + public boolean canSave() { + return true; + } + + @SuppressWarnings("deprecation") + public byte[] generate(org.bukkit.World world, Random random, int x, int z) { + return generator.generate(world, random, x, z); + } + + public byte[][] generateBlockSections(org.bukkit.World world, Random random, int x, int z, BiomeGrid biomes) { + return generator.generateBlockSections(world, random, x, z, biomes); + } + + public short[][] generateExtBlockSections(org.bukkit.World world, Random random, int x, int z, BiomeGrid biomes) { + return generator.generateExtBlockSections(world, random, x, z, biomes); + } + + public Chunk getChunkAt(int x, int z) { + return getOrCreateChunk(x, z); + } + + @Override + public boolean canSpawn(org.bukkit.World world, int x, int z) { + return generator.canSpawn(world, x, z); + } + + @Override + public List getDefaultPopulators(org.bukkit.World world) { + return generator.getDefaultPopulators(world); + } + + public List getMobsFor(EnumCreatureType type, int x, int y, int z) { + BiomeBase biomebase = world.getBiome(x, z); + + return biomebase == null ? null : biomebase.getMobs(type); + } + + public ChunkPosition findNearestMapFeature(World world, String type, int x, int y, int z) { + return "Stronghold".equals(type) && this.strongholdGen != null ? this.strongholdGen.getNearestGeneratedFeature(world, x, y, z) : null; + } + + public void recreateStructures(int i, int j) {} + + public int getLoadedChunks() { + return 0; + } + + public String getName() { + return "CustomChunkGenerator"; + } + + public void c() {} +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java new file mode 100644 index 0000000..19565e5 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java @@ -0,0 +1,8 @@ +package org.bukkit.craftbukkit.generator; + +import net.minecraft.server.IChunkProvider; +import org.bukkit.generator.ChunkGenerator; + +// Do not implement functions to this class, add to NormalChunkGenerator +public abstract class InternalChunkGenerator extends ChunkGenerator implements IChunkProvider { +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java new file mode 100644 index 0000000..59d7bbe --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java @@ -0,0 +1,12 @@ +package org.bukkit.craftbukkit.generator; + +import net.minecraft.server.World; + +/** + * This class is useless. Just fyi. + */ +public class NetherChunkGenerator extends NormalChunkGenerator { + public NetherChunkGenerator(World world, long seed) { + super(world, seed); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java new file mode 100644 index 0000000..ceab581 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java @@ -0,0 +1,86 @@ +package org.bukkit.craftbukkit.generator; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkPosition; +import net.minecraft.server.EnumCreatureType; +import net.minecraft.server.IChunkProvider; +import net.minecraft.server.IProgressUpdate; +import net.minecraft.server.World; + +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.generator.BlockPopulator; + +public class NormalChunkGenerator extends InternalChunkGenerator { + private final IChunkProvider provider; + + public NormalChunkGenerator(World world, long seed) { + provider = world.worldProvider.getChunkProvider(); + } + + public byte[] generate(org.bukkit.World world, Random random, int x, int z) { + throw new UnsupportedOperationException("Not supported."); + } + + public boolean canSpawn(org.bukkit.World world, int x, int z) { + return ((CraftWorld) world).getHandle().worldProvider.canSpawn(x, z); + } + + public List getDefaultPopulators(org.bukkit.World world) { + return new ArrayList(); + } + + public boolean isChunkLoaded(int i, int i1) { + return provider.isChunkLoaded(i, i1); + } + + public Chunk getOrCreateChunk(int i, int i1) { + return provider.getOrCreateChunk(i, i1); + } + + public Chunk getChunkAt(int i, int i1) { + return provider.getChunkAt(i, i1); + } + + public void getChunkAt(IChunkProvider icp, int i, int i1) { + provider.getChunkAt(icp, i, i1); + } + + public boolean saveChunks(boolean bln, IProgressUpdate ipu) { + return provider.saveChunks(bln, ipu); + } + + public boolean unloadChunks() { + return provider.unloadChunks(); + } + + public boolean canSave() { + return provider.canSave(); + } + + public List getMobsFor(EnumCreatureType ect, int i, int i1, int i2) { + return provider.getMobsFor(ect, i, i1, i2); + } + + public ChunkPosition findNearestMapFeature(World world, String string, int i, int i1, int i2) { + return provider.findNearestMapFeature(world, string, i, i1, i2); + } + + public void recreateStructures(int i, int j) { + provider.recreateStructures(i, j); + } + + // n.m.s implementations always return 0. (The true implementation is in ChunkProviderServer) + public int getLoadedChunks() { + return 0; + } + + public String getName() { + return "NormalWorldGenerator"; + } + + public void c() {} +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java new file mode 100644 index 0000000..e327996 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java @@ -0,0 +1,12 @@ +package org.bukkit.craftbukkit.generator; + +import net.minecraft.server.World; + +/** + * This class is useless. Just fyi. + */ +public class SkyLandsChunkGenerator extends NormalChunkGenerator { + public SkyLandsChunkGenerator(World world, long seed) { + super(world, seed); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CommandAliasHelpTopic.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CommandAliasHelpTopic.java new file mode 100644 index 0000000..9f2238c --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CommandAliasHelpTopic.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.help; + +import org.apache.commons.lang.Validate; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.help.HelpMap; +import org.bukkit.help.HelpTopic; + +public class CommandAliasHelpTopic extends HelpTopic { + + private final String aliasFor; + private final HelpMap helpMap; + + public CommandAliasHelpTopic(String alias, String aliasFor, HelpMap helpMap) { + this.aliasFor = aliasFor.startsWith("/") ? aliasFor : "/" + aliasFor; + this.helpMap = helpMap; + this.name = alias.startsWith("/") ? alias : "/" + alias; + Validate.isTrue(!this.name.equals(this.aliasFor), "Command " + this.name + " cannot be alias for itself"); + this.shortText = ChatColor.YELLOW + "Alias for " + ChatColor.WHITE + this.aliasFor; + } + + @Override + public String getFullText(CommandSender forWho) { + StringBuilder sb = new StringBuilder(shortText); + HelpTopic aliasForTopic = helpMap.getHelpTopic(aliasFor); + if (aliasForTopic != null) { + sb.append("\n"); + sb.append(aliasForTopic.getFullText(forWho)); + } + return sb.toString(); + } + + @Override + public boolean canSee(CommandSender commandSender) { + if (amendedPermission == null) { + HelpTopic aliasForTopic = helpMap.getHelpTopic(aliasFor); + if (aliasForTopic != null) { + return aliasForTopic.canSee(commandSender); + } else { + return false; + } + } else { + return commandSender.hasPermission(amendedPermission); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CustomHelpTopic.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CustomHelpTopic.java new file mode 100644 index 0000000..6dee229 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CustomHelpTopic.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.help.HelpTopic; + +/** + * This is a help topic implementation for general topics registered in the help.yml file. + */ +public class CustomHelpTopic extends HelpTopic { + private final String permissionNode; + + public CustomHelpTopic(String name, String shortText, String fullText, String permissionNode) { + this.permissionNode = permissionNode; + this.name = name; + this.shortText = shortText; + this.fullText = shortText + "\n" + fullText; + } + + public boolean canSee(CommandSender sender) { + if (sender instanceof ConsoleCommandSender) { + return true; + } + + if (!permissionNode.equals("")) { + return sender.hasPermission(permissionNode); + } else { + return true; + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CustomIndexHelpTopic.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CustomIndexHelpTopic.java new file mode 100644 index 0000000..2089a5f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/CustomIndexHelpTopic.java @@ -0,0 +1,40 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.command.CommandSender; +import org.bukkit.help.HelpMap; +import org.bukkit.help.HelpTopic; +import org.bukkit.help.IndexHelpTopic; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + */ +public class CustomIndexHelpTopic extends IndexHelpTopic { + private List futureTopics; + private final HelpMap helpMap; + + public CustomIndexHelpTopic(HelpMap helpMap, String name, String shortText, String permission, List futureTopics, String preamble) { + super(name, shortText, permission, new HashSet(), preamble); + this.helpMap = helpMap; + this.futureTopics = futureTopics; + } + + @Override + public String getFullText(CommandSender sender) { + if (futureTopics != null) { + List topics = new LinkedList(); + for (String futureTopic : futureTopics) { + HelpTopic topic = helpMap.getHelpTopic(futureTopic); + if (topic != null) { + topics.add(topic); + } + } + setTopicsCollection(topics); + futureTopics = null; + } + + return super.getFullText(sender); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/HelpTopicAmendment.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/HelpTopicAmendment.java new file mode 100644 index 0000000..4f0e00e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/HelpTopicAmendment.java @@ -0,0 +1,50 @@ +package org.bukkit.craftbukkit.help; + +/** + * A HelpTopicAmendment represents the contents of a topic amendment from the help.yml + */ +public class HelpTopicAmendment { + private final String topicName; + private final String shortText; + private final String fullText; + private final String permission; + + public HelpTopicAmendment(String topicName, String shortText, String fullText, String permission) { + this.fullText = fullText; + this.shortText = shortText; + this.topicName = topicName; + this.permission = permission; + } + + /** + * Gets the amended full text + * @return the full text + */ + public String getFullText() { + return fullText; + } + + /** + * Gets the amended short text + * @return the short text + */ + public String getShortText() { + return shortText; + } + + /** + * Gets the name of the topic being amended + * @return the topic name + */ + public String getTopicName() { + return topicName; + } + + /** + * Gets the amended permission + * @return the permission + */ + public String getPermission() { + return permission; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java new file mode 100644 index 0000000..98b4eee --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java @@ -0,0 +1,119 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.ChatColor; +import org.bukkit.Server; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.help.HelpTopic; + +import com.google.common.base.Charsets; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; + +/** + * HelpYamlReader is responsible for processing the contents of the help.yml file. + */ +public class HelpYamlReader { + + private YamlConfiguration helpYaml; + private final char ALT_COLOR_CODE = '&'; + private final Server server; + + public HelpYamlReader(Server server) { + this.server = server; + + File helpYamlFile = new File("config/misc", "help.yml"); // MineHQ - Dedicated config directory + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/help.yml"), Charsets.UTF_8)); + + try { + helpYaml = YamlConfiguration.loadConfiguration(helpYamlFile); + helpYaml.options().copyDefaults(true); + helpYaml.setDefaults(defaultConfig); + + try { + if (!helpYamlFile.exists()) { + helpYaml.save(helpYamlFile); + } + } catch (IOException ex) { + server.getLogger().log(Level.SEVERE, "Could not save " + helpYamlFile, ex); + } + } catch (Exception ex) { + server.getLogger().severe("Failed to load help.yml. Verify the yaml indentation is correct. Reverting to default help.yml."); + helpYaml = defaultConfig; + } + } + + /** + * Extracts a list of all general help topics from help.yml + * + * @return A list of general topics. + */ + public List getGeneralTopics() { + List topics = new LinkedList(); + ConfigurationSection generalTopics = helpYaml.getConfigurationSection("general-topics"); + if (generalTopics != null) { + for (String topicName : generalTopics.getKeys(false)) { + ConfigurationSection section = generalTopics.getConfigurationSection(topicName); + String shortText = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("shortText", "")); + String fullText = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("fullText", "")); + String permission = section.getString("permission", ""); + topics.add(new CustomHelpTopic(topicName, shortText, fullText, permission)); + } + } + return topics; + } + + /** + * Extracts a list of all index topics from help.yml + * + * @return A list of index topics. + */ + public List getIndexTopics() { + List topics = new LinkedList(); + ConfigurationSection indexTopics = helpYaml.getConfigurationSection("index-topics"); + if (indexTopics != null) { + for (String topicName : indexTopics.getKeys(false)) { + ConfigurationSection section = indexTopics.getConfigurationSection(topicName); + String shortText = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("shortText", "")); + String preamble = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("preamble", "")); + String permission = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("permission", "")); + List commands = section.getStringList("commands"); + topics.add(new CustomIndexHelpTopic(server.getHelpMap(), topicName, shortText, permission, commands, preamble)); + } + } + return topics; + } + + /** + * Extracts a list of topic amendments from help.yml + * + * @return A list of amendments. + */ + public List getTopicAmendments() { + List amendments = new LinkedList(); + ConfigurationSection commandTopics = helpYaml.getConfigurationSection("amended-topics"); + if (commandTopics != null) { + for (String topicName : commandTopics.getKeys(false)) { + ConfigurationSection section = commandTopics.getConfigurationSection(topicName); + String description = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("shortText", "")); + String usage = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("fullText", "")); + String permission = section.getString("permission", ""); + amendments.add(new HelpTopicAmendment(topicName, description, usage, permission)); + } + } + return amendments; + } + + public List getIgnoredPlugins() { + return helpYaml.getStringList("ignore-plugins"); + } + + public boolean commandTopicsInMasterIndex() { + return helpYaml.getBoolean("command-topics-in-master-index", true); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopic.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopic.java new file mode 100644 index 0000000..6f4b22b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopic.java @@ -0,0 +1,54 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.MultipleCommandAlias; +import org.bukkit.help.HelpTopic; + +/** + * This is a help topic implementation for {@link MultipleCommandAlias} commands. + */ +public class MultipleCommandAliasHelpTopic extends HelpTopic { + + private final MultipleCommandAlias alias; + + public MultipleCommandAliasHelpTopic(MultipleCommandAlias alias) { + this.alias = alias; + + name = "/" + alias.getLabel(); + + // Build short text + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < alias.getCommands().length; i++) { + if (i != 0) { + sb.append(ChatColor.GOLD + " > " + ChatColor.WHITE); + } + sb.append("/"); + sb.append(alias.getCommands()[i].getLabel()); + } + shortText = sb.toString(); + + // Build full text + fullText = ChatColor.GOLD + "Alias for: " + ChatColor.WHITE + getShortText(); + } + + public boolean canSee(CommandSender sender) { + if (amendedPermission == null) { + if (sender instanceof ConsoleCommandSender) { + return true; + } + + for (Command command : alias.getCommands()) { + if (!command.testPermissionSilent(sender)) { + return false; + } + } + + return true; + } else { + return sender.hasPermission(amendedPermission); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopicFactory.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopicFactory.java new file mode 100644 index 0000000..36ddc97 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopicFactory.java @@ -0,0 +1,15 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.command.MultipleCommandAlias; +import org.bukkit.help.HelpTopic; +import org.bukkit.help.HelpTopicFactory; + +/** + * This class creates {@link MultipleCommandAliasHelpTopic} help topics from {@link MultipleCommandAlias} commands. + */ +public class MultipleCommandAliasHelpTopicFactory implements HelpTopicFactory { + + public HelpTopic createTopic(MultipleCommandAlias multipleCommandAlias) { + return new MultipleCommandAliasHelpTopic(multipleCommandAlias); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java new file mode 100644 index 0000000..7dd5afe --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java @@ -0,0 +1,219 @@ +package org.bukkit.craftbukkit.help; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; + +import org.bukkit.command.*; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.command.defaults.VanillaCommand; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.help.*; + +import java.util.*; + +/** + * Standard implementation of {@link HelpMap} for CraftBukkit servers. + */ +public class SimpleHelpMap implements HelpMap { + + private HelpTopic defaultTopic; + private final Map helpTopics; + private final Map> topicFactoryMap; + private final CraftServer server; + private HelpYamlReader yaml; + + @SuppressWarnings("unchecked") + public SimpleHelpMap(CraftServer server) { + this.helpTopics = new TreeMap(HelpTopicComparator.topicNameComparatorInstance()); // Using a TreeMap for its explicit sorting on key + this.topicFactoryMap = new HashMap>(); + this.server = server; + this.yaml = new HelpYamlReader(server); + + Predicate indexFilter = Predicates.not(Predicates.instanceOf(CommandAliasHelpTopic.class)); + if (!yaml.commandTopicsInMasterIndex()) { + indexFilter = Predicates.and(indexFilter, Predicates.not(new IsCommandTopicPredicate())); + } + + this.defaultTopic = new IndexHelpTopic("Index", null, null, Collections2.filter(helpTopics.values(), indexFilter), "Use /help [n] to get page n of help."); + + registerHelpTopicFactory(MultipleCommandAlias.class, new MultipleCommandAliasHelpTopicFactory()); + } + + public synchronized HelpTopic getHelpTopic(String topicName) { + if (topicName.equals("")) { + return defaultTopic; + } + + if (helpTopics.containsKey(topicName)) { + return helpTopics.get(topicName); + } + + return null; + } + + public Collection getHelpTopics() { + return helpTopics.values(); + } + + public synchronized void addTopic(HelpTopic topic) { + // Existing topics take priority + if (!helpTopics.containsKey(topic.getName())) { + helpTopics.put(topic.getName(), topic); + } + } + + public synchronized void clear() { + helpTopics.clear(); + } + + public List getIgnoredPlugins() { + return yaml.getIgnoredPlugins(); + } + + /** + * Reads the general topics from help.yml and adds them to the help index. + */ + public synchronized void initializeGeneralTopics() { + yaml = new HelpYamlReader(server); + + // Initialize general help topics from the help.yml file + for (HelpTopic topic : yaml.getGeneralTopics()) { + addTopic(topic); + } + + // Initialize index help topics from the help.yml file + for (HelpTopic topic : yaml.getIndexTopics()) { + if (topic.getName().equals("Default")) { + defaultTopic = topic; + } else { + addTopic(topic); + } + } + } + + /** + * Processes all the commands registered in the server and creates help topics for them. + */ + public synchronized void initializeCommands() { + // ** Load topics from highest to lowest priority order ** + Set ignoredPlugins = new HashSet(yaml.getIgnoredPlugins()); + + // Don't load any automatic help topics if All is ignored + if (ignoredPlugins.contains("All")) { + return; + } + + // Initialize help topics from the server's command map + outer: for (Command command : server.getCommandMap().getCommands()) { + if (commandInIgnoredPlugin(command, ignoredPlugins)) { + continue; + } + + // Register a topic + for (Class c : topicFactoryMap.keySet()) { + if (c.isAssignableFrom(command.getClass())) { + HelpTopic t = topicFactoryMap.get(c).createTopic(command); + if (t != null) addTopic(t); + continue outer; + } + if (command instanceof PluginCommand && c.isAssignableFrom(((PluginCommand)command).getExecutor().getClass())) { + HelpTopic t = topicFactoryMap.get(c).createTopic(command); + if (t != null) addTopic(t); + continue outer; + } + } + addTopic(new GenericCommandHelpTopic(command)); + } + + // Initialize command alias help topics + for (Command command : server.getCommandMap().getCommands()) { + if (commandInIgnoredPlugin(command, ignoredPlugins)) { + continue; + } + for (String alias : command.getAliases()) { + // Only register if this command owns the alias + if (server.getCommandMap().getCommand(alias) == command) { + addTopic(new CommandAliasHelpTopic("/" + alias, "/" + command.getLabel(), this)); + } + } + } + + // Add alias sub-index + Collection filteredTopics = Collections2.filter(helpTopics.values(), Predicates.instanceOf(CommandAliasHelpTopic.class)); + if (!filteredTopics.isEmpty()) { + addTopic(new IndexHelpTopic("Aliases", "Lists command aliases", null, filteredTopics)); + } + + // Initialize plugin-level sub-topics + Map> pluginIndexes = new HashMap>(); + fillPluginIndexes(pluginIndexes, server.getCommandMap().getCommands()); + + for (Map.Entry> entry : pluginIndexes.entrySet()) { + addTopic(new IndexHelpTopic(entry.getKey(), "All commands for " + entry.getKey(), null, entry.getValue(), "Below is a list of all " + entry.getKey() + " commands:")); + } + + // Amend help topics from the help.yml file + for (HelpTopicAmendment amendment : yaml.getTopicAmendments()) { + if (helpTopics.containsKey(amendment.getTopicName())) { + helpTopics.get(amendment.getTopicName()).amendTopic(amendment.getShortText(), amendment.getFullText()); + if (amendment.getPermission() != null) { + helpTopics.get(amendment.getTopicName()).amendCanSee(amendment.getPermission()); + } + } + } + } + + private void fillPluginIndexes(Map> pluginIndexes, Collection commands) { + for (Command command : commands) { + String pluginName = getCommandPluginName(command); + if (pluginName != null) { + HelpTopic topic = getHelpTopic("/" + command.getLabel()); + if (topic != null) { + if (!pluginIndexes.containsKey(pluginName)) { + pluginIndexes.put(pluginName, new TreeSet(HelpTopicComparator.helpTopicComparatorInstance())); //keep things in topic order + } + pluginIndexes.get(pluginName).add(topic); + } + } + } + } + + private String getCommandPluginName(Command command) { + if (command instanceof VanillaCommandWrapper) { + return "Minecraft"; + } + if (command instanceof BukkitCommand || command instanceof VanillaCommand) { + return "Bukkit"; + } + if (command instanceof PluginIdentifiableCommand) { + return ((PluginIdentifiableCommand)command).getPlugin().getName(); + } + return null; + } + + private boolean commandInIgnoredPlugin(Command command, Set ignoredPlugins) { + if ((command instanceof BukkitCommand || command instanceof VanillaCommand) && ignoredPlugins.contains("Bukkit")) { + return true; + } + if (command instanceof PluginIdentifiableCommand && ignoredPlugins.contains(((PluginIdentifiableCommand)command).getPlugin().getName())) { + return true; + } + return false; + } + + public void registerHelpTopicFactory(Class commandClass, HelpTopicFactory factory) { + if (!Command.class.isAssignableFrom(commandClass) && !CommandExecutor.class.isAssignableFrom(commandClass)) { + throw new IllegalArgumentException("commandClass must implement either Command or CommandExecutor!"); + } + topicFactoryMap.put(commandClass, factory); + } + + private class IsCommandTopicPredicate implements Predicate { + + public boolean apply(HelpTopic topic) { + return topic.getName().charAt(0) == '/'; + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java new file mode 100644 index 0000000..42568a2 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java @@ -0,0 +1,315 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; + +import net.minecraft.server.Container; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IInventory; +import net.minecraft.server.PacketPlayOutOpenWindow; +import net.minecraft.server.Slot; + +public class CraftContainer extends Container { + private final InventoryView view; + private InventoryType cachedType; + private String cachedTitle; + private final int cachedSize; + + public CraftContainer(InventoryView view, int id) { + this.view = view; + this.windowId = id; + // TODO: Do we need to check that it really is a CraftInventory? + IInventory top = ((CraftInventory)view.getTopInventory()).getInventory(); + IInventory bottom = ((CraftInventory)view.getBottomInventory()).getInventory(); + cachedType = view.getType(); + cachedTitle = view.getTitle(); + cachedSize = getSize(); + setupSlots(top, bottom); + } + + public CraftContainer(final Inventory inventory, final HumanEntity player, int id) { + this(new InventoryView() { + @Override + public Inventory getTopInventory() { + return inventory; + } + + @Override + public Inventory getBottomInventory() { + return player.getInventory(); + } + + @Override + public HumanEntity getPlayer() { + return player; + } + + @Override + public InventoryType getType() { + return inventory.getType(); + } + }, id); + } + + @Override + public InventoryView getBukkitView() { + return view; + } + + private int getSize() { + return view.getTopInventory().getSize(); + } + + @Override + public boolean c(EntityHuman entityhuman) { + if (cachedType == view.getType() && cachedSize == getSize() && cachedTitle.equals(view.getTitle())) { + return true; + } + // If the window type has changed for some reason, update the player + // This method will be called every tick or something, so it's + // as good a place as any to put something like this. + boolean typeChanged = (cachedType != view.getType()); + cachedType = view.getType(); + cachedTitle = view.getTitle(); + if (view.getPlayer() instanceof CraftPlayer) { + CraftPlayer player = (CraftPlayer) view.getPlayer(); + int type = getNotchInventoryType(cachedType); + IInventory top = ((CraftInventory)view.getTopInventory()).getInventory(); + IInventory bottom = ((CraftInventory)view.getBottomInventory()).getInventory(); + this.b.clear(); + this.c.clear(); + if (typeChanged) { + setupSlots(top, bottom); + } + int size = getSize(); + player.getHandle().playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.windowId, type, cachedTitle, size, true)); + player.updateInventory(); + } + return true; + } + + public static int getNotchInventoryType(InventoryType type) { + int typeID; + switch(type) { + case WORKBENCH: + typeID = 1; + break; + case FURNACE: + typeID = 2; + break; + case DISPENSER: + typeID = 3; + break; + case ENCHANTING: + typeID = 4; + break; + case BREWING: + typeID = 5; + break; + case BEACON: + typeID = 7; + break; + case ANVIL: + typeID = 8; + break; + case HOPPER: + typeID = 9; + break; + default: + typeID = 0; + break; + } + return typeID; + } + + private void setupSlots(IInventory top, IInventory bottom) { + switch(cachedType) { + case CREATIVE: + break; // TODO: This should be an error? + case PLAYER: + case CHEST: + setupChest(top, bottom); + break; + case DISPENSER: + setupDispenser(top, bottom); + break; + case FURNACE: + setupFurnace(top, bottom); + break; + case CRAFTING: // TODO: This should be an error? + case WORKBENCH: + setupWorkbench(top, bottom); + break; + case ENCHANTING: + setupEnchanting(top, bottom); + break; + case BREWING: + setupBrewing(top, bottom); + break; + case HOPPER: + setupHopper(top, bottom); + break; + } + } + + private void setupChest(IInventory top, IInventory bottom) { + int rows = top.getSize() / 9; + int row; + int col; + // This code copied from ContainerChest + int i = (rows - 4) * 18; + for (row = 0; row < rows; ++row) { + for (col = 0; col < 9; ++col) { + this.a(new Slot(top, col + row * 9, 8 + col * 18, 18 + row * 18)); + } + } + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 103 + row * 18 + i)); + } + } + + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col, 8 + col * 18, 161 + i)); + } + // End copy from ContainerChest + } + + private void setupWorkbench(IInventory top, IInventory bottom) { + // This code copied from ContainerWorkbench + this.a(new Slot(top, 0, 124, 35)); + + int row; + int col; + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 3; ++col) { + this.a(new Slot(top, 1 + col + row * 3, 30 + col * 18, 17 + row * 18)); + } + } + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 84 + row * 18)); + } + } + + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col, 8 + col * 18, 142)); + } + // End copy from ContainerWorkbench + } + + private void setupFurnace(IInventory top, IInventory bottom) { + // This code copied from ContainerFurnace + this.a(new Slot(top, 0, 56, 17)); + this.a(new Slot(top, 1, 56, 53)); + this.a(new Slot(top, 2, 116, 35)); + + int row; + int col; + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 84 + row * 18)); + } + } + + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col, 8 + col * 18, 142)); + } + // End copy from ContainerFurnace + } + + private void setupDispenser(IInventory top, IInventory bottom) { + // This code copied from ContainerDispenser + int row; + int col; + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 3; ++col) { + this.a(new Slot(top, col + row * 3, 61 + col * 18, 17 + row * 18)); + } + } + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 84 + row * 18)); + } + } + + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col, 8 + col * 18, 142)); + } + // End copy from ContainerDispenser + } + + private void setupEnchanting(IInventory top, IInventory bottom) { + // This code copied from ContainerEnchantTable + this.a((new Slot(top, 0, 25, 47))); + + int row; + + for (row = 0; row < 3; ++row) { + for (int i1 = 0; i1 < 9; ++i1) { + this.a(new Slot(bottom, i1 + row * 9 + 9, 8 + i1 * 18, 84 + row * 18)); + } + } + + for (row = 0; row < 9; ++row) { + this.a(new Slot(bottom, row, 8 + row * 18, 142)); + } + // End copy from ContainerEnchantTable + } + + private void setupBrewing(IInventory top, IInventory bottom) { + // This code copied from ContainerBrewingStand + this.a(new Slot(top, 0, 56, 46)); + this.a(new Slot(top, 1, 79, 53)); + this.a(new Slot(top, 2, 102, 46)); + this.a(new Slot(top, 3, 79, 17)); + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(bottom, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(bottom, i, 8 + i * 18, 142)); + } + // End copy from ContainerBrewingStand + } + + private void setupHopper(IInventory top, IInventory bottom) { + // This code copied from ContainerHopper + byte b0 = 51; + + int i; + + for (i = 0; i < top.getSize(); ++i) { + this.a(new Slot(top, i, 44 + i * 18, 20)); + } + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(bottom, j + i * 9 + 9, 8 + j * 18, i * 18 + b0)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(bottom, i, 8 + i * 18, 58 + b0)); + } + // End copy from ContainerHopper + } + + public boolean a(EntityHuman entity) { + return true; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java new file mode 100644 index 0000000..2213482 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java @@ -0,0 +1,144 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.EntityInsentient; + +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +public class CraftEntityEquipment implements EntityEquipment { + private static final int WEAPON_SLOT = 0; + private static final int HELMET_SLOT = 4; + private static final int CHEST_SLOT = 3; + private static final int LEG_SLOT = 2; + private static final int BOOT_SLOT = 1; + private static final int INVENTORY_SLOTS = 5; + + private final CraftLivingEntity entity; + + public CraftEntityEquipment(CraftLivingEntity entity) { + this.entity = entity; + } + + public ItemStack getItemInHand() { + return getEquipment(WEAPON_SLOT); + } + + public void setItemInHand(ItemStack stack) { + setEquipment(WEAPON_SLOT, stack); + } + + public ItemStack getHelmet() { + return getEquipment(HELMET_SLOT); + } + + public void setHelmet(ItemStack helmet) { + setEquipment(HELMET_SLOT, helmet); + } + + public ItemStack getChestplate() { + return getEquipment(CHEST_SLOT); + } + + public void setChestplate(ItemStack chestplate) { + setEquipment(CHEST_SLOT, chestplate); + } + + public ItemStack getLeggings() { + return getEquipment(LEG_SLOT); + } + + public void setLeggings(ItemStack leggings) { + setEquipment(LEG_SLOT, leggings); + } + + public ItemStack getBoots() { + return getEquipment(BOOT_SLOT); + } + + public void setBoots(ItemStack boots) { + setEquipment(BOOT_SLOT, boots); + } + + public ItemStack[] getArmorContents() { + ItemStack[] armor = new ItemStack[INVENTORY_SLOTS - 1]; + for(int slot = WEAPON_SLOT + 1; slot < INVENTORY_SLOTS; slot++) { + armor[slot - 1] = getEquipment(slot); + } + return armor; + } + + public void setArmorContents(ItemStack[] items) { + for(int slot = WEAPON_SLOT + 1; slot < INVENTORY_SLOTS; slot++) { + ItemStack equipment = items != null && slot <= items.length ? items[slot - 1] : null; + setEquipment(slot, equipment); + } + } + + private ItemStack getEquipment(int slot) { + return CraftItemStack.asBukkitCopy(entity.getHandle().getEquipment(slot)); + } + + private void setEquipment(int slot, ItemStack stack) { + entity.getHandle().setEquipment(slot, CraftItemStack.asNMSCopy(stack)); + } + + public void clear() { + for(int i = 0; i < INVENTORY_SLOTS; i++) { + setEquipment(i, null); + } + } + + public Entity getHolder() { + return entity; + } + + public float getItemInHandDropChance() { + return getDropChance(WEAPON_SLOT); + } + + public void setItemInHandDropChance(float chance) { + setDropChance(WEAPON_SLOT, chance); + } + + public float getHelmetDropChance() { + return getDropChance(HELMET_SLOT); + } + + public void setHelmetDropChance(float chance) { + setDropChance(HELMET_SLOT, chance); + } + + public float getChestplateDropChance() { + return getDropChance(CHEST_SLOT); + } + + public void setChestplateDropChance(float chance) { + setDropChance(CHEST_SLOT, chance); + } + + public float getLeggingsDropChance() { + return getDropChance(LEG_SLOT); + } + + public void setLeggingsDropChance(float chance) { + setDropChance(LEG_SLOT, chance); + } + + public float getBootsDropChance() { + return getDropChance(BOOT_SLOT); + } + + public void setBootsDropChance(float chance) { + setDropChance(BOOT_SLOT, chance); + } + + private void setDropChance(int slot, float chance) { + ((EntityInsentient) entity.getHandle()).dropChances[slot] = chance - 0.1F; + } + + private float getDropChance(int slot) { + return ((EntityInsentient) entity.getHandle()).dropChances[slot] + 0.1F; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java new file mode 100644 index 0000000..69f17db --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.RecipesFurnace; + +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.FurnaceRecipe; +import org.bukkit.inventory.ItemStack; + +public class CraftFurnaceRecipe extends FurnaceRecipe implements CraftRecipe { + public CraftFurnaceRecipe(ItemStack result, ItemStack source) { + super(result, source.getType(), source.getDurability()); + } + + public static CraftFurnaceRecipe fromBukkitRecipe(FurnaceRecipe recipe) { + if (recipe instanceof CraftFurnaceRecipe) { + return (CraftFurnaceRecipe) recipe; + } + return new CraftFurnaceRecipe(recipe.getResult(), recipe.getInput()); + } + + @Override + public void addToCraftingManager() { + ItemStack result = this.getResult(); + ItemStack input = this.getInput(); + RecipesFurnace.getInstance().registerRecipe(CraftItemStack.asNMSCopy(input), CraftItemStack.asNMSCopy(result)); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java new file mode 100644 index 0000000..08ccef1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java @@ -0,0 +1,486 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; + +import net.minecraft.server.ContainerAnvilInventory; +import net.minecraft.server.ContainerEnchantTableInventory; +import net.minecraft.server.IHopper; +import net.minecraft.server.IInventory; +import net.minecraft.server.InventoryCrafting; +import net.minecraft.server.InventoryEnderChest; +import net.minecraft.server.InventoryMerchant; +import net.minecraft.server.PlayerInventory; +import net.minecraft.server.TileEntityBeacon; +import net.minecraft.server.TileEntityBrewingStand; +import net.minecraft.server.TileEntityDispenser; +import net.minecraft.server.TileEntityDropper; +import net.minecraft.server.TileEntityFurnace; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.Bukkit; +import org.bukkit.Material; + +public class CraftInventory implements Inventory { + protected final IInventory inventory; + + public CraftInventory(IInventory inventory) { + this.inventory = inventory; + } + + public IInventory getInventory() { + return inventory; + } + + public int getSize() { + return getInventory().getSize(); + } + + public String getName() { + return getInventory().getInventoryName(); + } + + public ItemStack getItem(int index) { + net.minecraft.server.ItemStack item = getInventory().getItem(index); + return item == null ? null : CraftItemStack.asCraftMirror(item); + } + + public ItemStack[] getContents() { + ItemStack[] items = new ItemStack[getSize()]; + net.minecraft.server.ItemStack[] mcItems = getInventory().getContents(); + + int size = Math.min(items.length, mcItems.length); + for (int i = 0; i < size; i++) { + items[i] = mcItems[i] == null ? null : CraftItemStack.asCraftMirror(mcItems[i]); + } + + return items; + } + + public void setContents(ItemStack[] items) { + if (getInventory().getContents().length < items.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + getInventory().getContents().length + " or less"); + } + + net.minecraft.server.ItemStack[] mcItems = getInventory().getContents(); + + for (int i = 0; i < mcItems.length; i++) { + if (i >= items.length) { + mcItems[i] = null; + } else { + mcItems[i] = CraftItemStack.asNMSCopy(items[i]); + } + } + } + + public void setItem(int index, ItemStack item) { + getInventory().setItem(index, ((item == null || item.getTypeId() == 0) ? null : CraftItemStack.asNMSCopy(item))); + } + + public boolean contains(int materialId) { + for (ItemStack item : getContents()) { + if (item != null && item.getTypeId() == materialId) { + return true; + } + } + return false; + } + + public boolean contains(Material material) { + Validate.notNull(material, "Material cannot be null"); + return contains(material.getId()); + } + + public boolean contains(ItemStack item) { + if (item == null) { + return false; + } + for (ItemStack i : getContents()) { + if (item.equals(i)) { + return true; + } + } + return false; + } + + public boolean contains(int materialId, int amount) { + if (amount <= 0) { + return true; + } + for (ItemStack item : getContents()) { + if (item != null && item.getTypeId() == materialId) { + if ((amount -= item.getAmount()) <= 0) { + return true; + } + } + } + return false; + } + + public boolean contains(Material material, int amount) { + Validate.notNull(material, "Material cannot be null"); + return contains(material.getId(), amount); + } + + public boolean contains(ItemStack item, int amount) { + if (item == null) { + return false; + } + if (amount <= 0) { + return true; + } + for (ItemStack i : getContents()) { + if (item.equals(i) && --amount <= 0) { + return true; + } + } + return false; + } + + public boolean containsAtLeast(ItemStack item, int amount) { + if (item == null) { + return false; + } + if (amount <= 0) { + return true; + } + for (ItemStack i : getContents()) { + if (item.isSimilar(i) && (amount -= i.getAmount()) <= 0) { + return true; + } + } + return false; + } + + public HashMap all(int materialId) { + HashMap slots = new HashMap(); + + ItemStack[] inventory = getContents(); + for (int i = 0; i < inventory.length; i++) { + ItemStack item = inventory[i]; + if (item != null && item.getTypeId() == materialId) { + slots.put(i, item); + } + } + return slots; + } + + public HashMap all(Material material) { + Validate.notNull(material, "Material cannot be null"); + return all(material.getId()); + } + + public HashMap all(ItemStack item) { + HashMap slots = new HashMap(); + if (item != null) { + ItemStack[] inventory = getContents(); + for (int i = 0; i < inventory.length; i++) { + if (item.equals(inventory[i])) { + slots.put(i, inventory[i]); + } + } + } + return slots; + } + + public int first(int materialId) { + ItemStack[] inventory = getContents(); + for (int i = 0; i < inventory.length; i++) { + ItemStack item = inventory[i]; + if (item != null && item.getTypeId() == materialId) { + return i; + } + } + return -1; + } + + public int first(Material material) { + Validate.notNull(material, "Material cannot be null"); + return first(material.getId()); + } + + public int first(ItemStack item) { + return first(item, true); + } + + private int first(ItemStack item, boolean withAmount) { + if (item == null) { + return -1; + } + ItemStack[] inventory = getContents(); + for (int i = 0; i < inventory.length; i++) { + if (inventory[i] == null) continue; + + if (withAmount ? item.equals(inventory[i]) : item.isSimilar(inventory[i])) { + return i; + } + } + return -1; + } + + public int firstEmpty() { + ItemStack[] inventory = getContents(); + for (int i = 0; i < inventory.length; i++) { + if (inventory[i] == null) { + return i; + } + } + return -1; + } + + public int firstPartial(int materialId) { + ItemStack[] inventory = getContents(); + for (int i = 0; i < inventory.length; i++) { + ItemStack item = inventory[i]; + if (item != null && item.getTypeId() == materialId && item.getAmount() < item.getMaxStackSize()) { + return i; + } + } + return -1; + } + + public int firstPartial(Material material) { + Validate.notNull(material, "Material cannot be null"); + return firstPartial(material.getId()); + } + + private int firstPartial(ItemStack item) { + ItemStack[] inventory = getContents(); + ItemStack filteredItem = CraftItemStack.asCraftCopy(item); + if (item == null) { + return -1; + } + for (int i = 0; i < inventory.length; i++) { + ItemStack cItem = inventory[i]; + if (cItem != null && cItem.getAmount() < cItem.getMaxStackSize() && cItem.isSimilar(filteredItem)) { + return i; + } + } + return -1; + } + + public HashMap addItem(ItemStack... items) { + Validate.noNullElements(items, "Item cannot be null"); + HashMap leftover = new HashMap(); + + /* TODO: some optimization + * - Create a 'firstPartial' with a 'fromIndex' + * - Record the lastPartial per Material + * - Cache firstEmpty result + */ + + for (int i = 0; i < items.length; i++) { + ItemStack item = items[i]; + while (true) { + // Do we already have a stack of it? + int firstPartial = firstPartial(item); + + // Drat! no partial stack + if (firstPartial == -1) { + // Find a free spot! + int firstFree = firstEmpty(); + + if (firstFree == -1) { + // No space at all! + leftover.put(i, item); + break; + } else { + // More than a single stack! + if (item.getAmount() > getMaxItemStack()) { + CraftItemStack stack = CraftItemStack.asCraftCopy(item); + stack.setAmount(getMaxItemStack()); + setItem(firstFree, stack); + item.setAmount(item.getAmount() - getMaxItemStack()); + } else { + // Just store it + setItem(firstFree, item); + break; + } + } + } else { + // So, apparently it might only partially fit, well lets do just that + ItemStack partialItem = getItem(firstPartial); + + int amount = item.getAmount(); + int partialAmount = partialItem.getAmount(); + int maxAmount = partialItem.getMaxStackSize(); + + // Check if it fully fits + if (amount + partialAmount <= maxAmount) { + partialItem.setAmount(amount + partialAmount); + // To make sure the packet is sent to the client + setItem(firstPartial, partialItem); + break; + } + + // It fits partially + partialItem.setAmount(maxAmount); + // To make sure the packet is sent to the client + setItem(firstPartial, partialItem); + item.setAmount(amount + partialAmount - maxAmount); + } + } + } + return leftover; + } + + public HashMap removeItem(ItemStack... items) { + Validate.notNull(items, "Items cannot be null"); + HashMap leftover = new HashMap(); + + // TODO: optimization + + for (int i = 0; i < items.length; i++) { + ItemStack item = items[i]; + int toDelete = item.getAmount(); + + while (true) { + int first = first(item, false); + + // Drat! we don't have this type in the inventory + if (first == -1) { + item.setAmount(toDelete); + leftover.put(i, item); + break; + } else { + ItemStack itemStack = getItem(first); + int amount = itemStack.getAmount(); + + if (amount <= toDelete) { + toDelete -= amount; + // clear the slot, all used up + clear(first); + } else { + // split the stack and store + itemStack.setAmount(amount - toDelete); + setItem(first, itemStack); + toDelete = 0; + } + } + + // Bail when done + if (toDelete <= 0) { + break; + } + } + } + return leftover; + } + + private int getMaxItemStack() { + return getInventory().getMaxStackSize(); + } + + public void remove(int materialId) { + ItemStack[] items = getContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getTypeId() == materialId) { + clear(i); + } + } + } + + public void remove(Material material) { + Validate.notNull(material, "Material cannot be null"); + remove(material.getId()); + } + + public void remove(ItemStack item) { + ItemStack[] items = getContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].equals(item)) { + clear(i); + } + } + } + + public void clear(int index) { + setItem(index, null); + } + + public void clear() { + for (int i = 0; i < getSize(); i++) { + clear(i); + } + } + + public ListIterator iterator() { + return new InventoryIterator(this); + } + + public ListIterator iterator(int index) { + if (index < 0) { + index += getSize() + 1; // ie, with -1, previous() will return the last element + } + return new InventoryIterator(this, index); + } + + public List getViewers() { + return this.inventory.getViewers(); + } + + public String getTitle() { + return inventory.getInventoryName(); + } + + public InventoryType getType() { + // Thanks to Droppers extending Dispensers, order is important. + if (inventory instanceof InventoryCrafting) { + return inventory.getSize() >= 9 ? InventoryType.WORKBENCH : InventoryType.CRAFTING; + } else if (inventory instanceof PlayerInventory) { + return InventoryType.PLAYER; + } else if (inventory instanceof TileEntityDropper) { + return InventoryType.DROPPER; + } else if (inventory instanceof TileEntityDispenser) { + return InventoryType.DISPENSER; + } else if (inventory instanceof TileEntityFurnace) { + return InventoryType.FURNACE; + } else if (inventory instanceof ContainerEnchantTableInventory) { + return InventoryType.ENCHANTING; + } else if (inventory instanceof TileEntityBrewingStand) { + return InventoryType.BREWING; + } else if (inventory instanceof CraftInventoryCustom.MinecraftInventory) { + return ((CraftInventoryCustom.MinecraftInventory) inventory).getType(); + } else if (inventory instanceof InventoryEnderChest) { + return InventoryType.ENDER_CHEST; + } else if (inventory instanceof InventoryMerchant) { + return InventoryType.MERCHANT; + } else if (inventory instanceof TileEntityBeacon) { + return InventoryType.BEACON; + } else if (inventory instanceof ContainerAnvilInventory) { + return InventoryType.ANVIL; + } else if (inventory instanceof IHopper) { + return InventoryType.HOPPER; + } else { + return InventoryType.CHEST; + } + } + + public InventoryHolder getHolder() { + return inventory.getOwner(); + } + + public int getMaxStackSize() { + return inventory.getMaxStackSize(); + } + + public void setMaxStackSize(int size) { + inventory.setMaxStackSize(size); + } + + @Override + public int hashCode() { + return inventory.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + return obj instanceof CraftInventory && ((CraftInventory) obj).inventory.equals(this.inventory); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java new file mode 100644 index 0000000..46a1d38 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IInventory; + +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryAnvil extends CraftInventory implements AnvilInventory { + private final IInventory resultInventory; + + public CraftInventoryAnvil(IInventory inventory, IInventory resultInventory) { + super(inventory); + this.resultInventory = resultInventory; + } + + public IInventory getResultInventory() { + return resultInventory; + } + + public IInventory getIngredientsInventory() { + return inventory; + } + + @Override + public ItemStack getItem(int slot) { + if (slot < getIngredientsInventory().getSize()) { + net.minecraft.server.ItemStack item = getIngredientsInventory().getItem(slot); + return item == null ? null : CraftItemStack.asCraftMirror(item); + } else { + net.minecraft.server.ItemStack item = getResultInventory().getItem(slot - getIngredientsInventory().getSize()); + return item == null ? null : CraftItemStack.asCraftMirror(item); + } + } + + @Override + public void setItem(int index, ItemStack item) { + if (index < getIngredientsInventory().getSize()) { + getIngredientsInventory().setItem(index, (item == null ? null : CraftItemStack.asNMSCopy(item))); + } else { + getResultInventory().setItem((index - getIngredientsInventory().getSize()), (item == null ? null : CraftItemStack.asNMSCopy(item))); + } + } + + @Override + public int getSize() { + return getResultInventory().getSize() + getIngredientsInventory().getSize(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBeacon.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBeacon.java new file mode 100644 index 0000000..43c4107 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBeacon.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.TileEntityBeacon; +import org.bukkit.inventory.BeaconInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryBeacon extends CraftInventory implements BeaconInventory { + public CraftInventoryBeacon(TileEntityBeacon beacon) { + super(beacon); + } + + public void setItem(ItemStack item) { + setItem(0, item); + } + + public ItemStack getItem() { + return getItem(0); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBrewer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBrewer.java new file mode 100644 index 0000000..6de6e6e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBrewer.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.block.BrewingStand; +import org.bukkit.inventory.BrewerInventory; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.IInventory; + +public class CraftInventoryBrewer extends CraftInventory implements BrewerInventory { + public CraftInventoryBrewer(IInventory inventory) { + super(inventory); + } + + public ItemStack getIngredient() { + return getItem(3); + } + + public void setIngredient(ItemStack ingredient) { + setItem(3, ingredient); + } + + @Override + public BrewingStand getHolder() { + return (BrewingStand) inventory.getOwner(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCrafting.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCrafting.java new file mode 100644 index 0000000..7bbf1df --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCrafting.java @@ -0,0 +1,138 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IRecipe; +import net.minecraft.server.IInventory; +import net.minecraft.server.InventoryCrafting; + +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; +import org.bukkit.util.Java15Compat; + +public class CraftInventoryCrafting extends CraftInventory implements CraftingInventory { + private final IInventory resultInventory; + + public CraftInventoryCrafting(InventoryCrafting inventory, IInventory resultInventory) { + super(inventory); + this.resultInventory = resultInventory; + } + + public IInventory getResultInventory() { + return resultInventory; + } + + public IInventory getMatrixInventory() { + return inventory; + } + + @Override + public int getSize() { + return getResultInventory().getSize() + getMatrixInventory().getSize(); + } + + @Override + public void setContents(ItemStack[] items) { + int resultLen = getResultInventory().getContents().length; + int len = getMatrixInventory().getContents().length + resultLen; + if (len > items.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + len + " or less"); + } + setContents(items[0], Java15Compat.Arrays_copyOfRange(items, 1, items.length)); + } + + @Override + public ItemStack[] getContents() { + ItemStack[] items = new ItemStack[getSize()]; + net.minecraft.server.ItemStack[] mcResultItems = getResultInventory().getContents(); + + int i = 0; + for (i = 0; i < mcResultItems.length; i++ ) { + items[i] = CraftItemStack.asCraftMirror(mcResultItems[i]); + } + + net.minecraft.server.ItemStack[] mcItems = getMatrixInventory().getContents(); + + for (int j = 0; j < mcItems.length; j++) { + items[i + j] = CraftItemStack.asCraftMirror(mcItems[j]); + } + + return items; + } + + public void setContents(ItemStack result, ItemStack[] contents) { + setResult(result); + setMatrix(contents); + } + + @Override + public CraftItemStack getItem(int index) { + if (index < getResultInventory().getSize()) { + net.minecraft.server.ItemStack item = getResultInventory().getItem(index); + return item == null ? null : CraftItemStack.asCraftMirror(item); + } else { + net.minecraft.server.ItemStack item = getMatrixInventory().getItem(index - getResultInventory().getSize()); + return item == null ? null : CraftItemStack.asCraftMirror(item); + } + } + + @Override + public void setItem(int index, ItemStack item) { + if (index < getResultInventory().getSize()) { + getResultInventory().setItem(index, (item == null ? null : CraftItemStack.asNMSCopy(item))); + } else { + getMatrixInventory().setItem((index - getResultInventory().getSize()), (item == null ? null : CraftItemStack.asNMSCopy(item))); + } + } + + public ItemStack[] getMatrix() { + ItemStack[] items = new ItemStack[getSize()]; + net.minecraft.server.ItemStack[] matrix = getMatrixInventory().getContents(); + + for (int i = 0; i < matrix.length; i++ ) { + items[i] = CraftItemStack.asCraftMirror(matrix[i]); + } + + return items; + } + + public ItemStack getResult() { + net.minecraft.server.ItemStack item = getResultInventory().getItem(0); + if(item != null) return CraftItemStack.asCraftMirror(item); + return null; + } + + public void setMatrix(ItemStack[] contents) { + if (getMatrixInventory().getContents().length > contents.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + getMatrixInventory().getContents().length + " or less"); + } + + net.minecraft.server.ItemStack[] mcItems = getMatrixInventory().getContents(); + + for (int i = 0; i < mcItems.length; i++ ) { + if (i < contents.length) { + ItemStack item = contents[i]; + if (item == null || item.getTypeId() <= 0) { + mcItems[i] = null; + } else { + mcItems[i] = CraftItemStack.asNMSCopy(item); + } + } else { + mcItems[i] = null; + } + } + } + + public void setResult(ItemStack item) { + net.minecraft.server.ItemStack[] contents = getResultInventory().getContents(); + if (item == null || item.getTypeId() <= 0) { + contents[0] = null; + } else { + contents[0] = CraftItemStack.asNMSCopy(item); + } + } + + public Recipe getRecipe() { + IRecipe recipe = ((InventoryCrafting)getInventory()).currentRecipe; + return recipe == null ? null : recipe.toBukkitRecipe(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java new file mode 100644 index 0000000..8b8a317 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java @@ -0,0 +1,163 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.InventoryHolder; + +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IInventory; +import net.minecraft.server.ItemStack; + +public class CraftInventoryCustom extends CraftInventory { + public CraftInventoryCustom(InventoryHolder owner, InventoryType type) { + super(new MinecraftInventory(owner, type)); + } + + public CraftInventoryCustom(InventoryHolder owner, InventoryType type, String title) { + super(new MinecraftInventory(owner, type, title)); + } + + public CraftInventoryCustom(InventoryHolder owner, int size) { + super(new MinecraftInventory(owner, size)); + } + + public CraftInventoryCustom(InventoryHolder owner, int size, String title) { + super(new MinecraftInventory(owner, size, title)); + } + + static class MinecraftInventory implements IInventory { + private final ItemStack[] items; + private int maxStack = MAX_STACK; + private final List viewers; + private final String title; + private InventoryType type; + private final InventoryHolder owner; + + public MinecraftInventory(InventoryHolder owner, InventoryType type) { + this(owner, type.getDefaultSize(), type.getDefaultTitle()); + this.type = type; + } + + public MinecraftInventory(InventoryHolder owner, InventoryType type, String title) { + this(owner, type.getDefaultSize(), title); + this.type = type; + } + + public MinecraftInventory(InventoryHolder owner, int size) { + this(owner, size, "Chest"); + } + + public MinecraftInventory(InventoryHolder owner, int size, String title) { + Validate.notNull(title, "Title cannot be null"); + Validate.isTrue(title.length() <= 32, "Title cannot be longer than 32 characters"); + this.items = new ItemStack[size]; + this.title = title; + this.viewers = new ArrayList(); + this.owner = owner; + this.type = InventoryType.CHEST; + } + + public int getSize() { + return items.length; + } + + public ItemStack getItem(int i) { + return items[i]; + } + + public ItemStack splitStack(int i, int j) { + ItemStack stack = this.getItem(i); + ItemStack result; + if (stack == null) return null; + if (stack.count <= j) { + this.setItem(i, null); + result = stack; + } else { + result = CraftItemStack.copyNMSStack(stack, j); + stack.count -= j; + } + this.update(); + return result; + } + + public ItemStack splitWithoutUpdate(int i) { + ItemStack stack = this.getItem(i); + ItemStack result; + if (stack == null) return null; + if (stack.count <= 1) { + this.setItem(i, null); + result = stack; + } else { + result = CraftItemStack.copyNMSStack(stack, 1); + stack.count -= 1; + } + return result; + } + + public void setItem(int i, ItemStack itemstack) { + items[i] = itemstack; + if (itemstack != null && this.getMaxStackSize() > 0 && itemstack.count > this.getMaxStackSize()) { + itemstack.count = this.getMaxStackSize(); + } + } + + public String getInventoryName() { + return title; + } + + public int getMaxStackSize() { + return maxStack; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public ItemStack[] getContents() { + return items; + } + + public void onOpen(CraftHumanEntity who) { + viewers.add(who); + } + + public void onClose(CraftHumanEntity who) { + viewers.remove(who); + } + + public List getViewers() { + return viewers; + } + + public InventoryType getType() { + return type; + } + + public void closeContainer() {} + + public InventoryHolder getOwner() { + return owner; + } + + public void startOpen() {} + + public boolean k_() { + return false; + } + + public boolean b(int i, ItemStack itemstack) { + return true; + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java new file mode 100644 index 0000000..0459f41 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java @@ -0,0 +1,60 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.block.DoubleChest; +import org.bukkit.inventory.DoubleChestInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.InventoryLargeChest; + +public class CraftInventoryDoubleChest extends CraftInventory implements DoubleChestInventory { + private final CraftInventory left; + private final CraftInventory right; + + public CraftInventoryDoubleChest(CraftInventory left, CraftInventory right) { + super(new InventoryLargeChest("Large chest", left.getInventory(), right.getInventory())); + this.left = left; + this.right = right; + } + + public CraftInventoryDoubleChest(InventoryLargeChest largeChest) { + super(largeChest); + if (largeChest.left instanceof InventoryLargeChest) { + left = new CraftInventoryDoubleChest((InventoryLargeChest) largeChest.left); + } else { + left = new CraftInventory(largeChest.left); + } + if (largeChest.right instanceof InventoryLargeChest) { + right = new CraftInventoryDoubleChest((InventoryLargeChest) largeChest.right); + } else { + right = new CraftInventory(largeChest.right); + } + } + + public Inventory getLeftSide() { + return left; + } + + public Inventory getRightSide() { + return right; + } + + @Override + public void setContents(ItemStack[] items) { + if (getInventory().getContents().length < items.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + getInventory().getContents().length + " or less"); + } + ItemStack[] leftItems = new ItemStack[left.getSize()], rightItems = new ItemStack[right.getSize()]; + System.arraycopy(items, 0, leftItems, 0, Math.min(left.getSize(),items.length)); + left.setContents(leftItems); + if (items.length >= left.getSize()) { + System.arraycopy(items, left.getSize(), rightItems, 0, Math.min(right.getSize(), items.length - left.getSize())); + right.setContents(rightItems); + } + } + + @Override + public DoubleChest getHolder() { + return new DoubleChest(this); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryEnchanting.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryEnchanting.java new file mode 100644 index 0000000..fdc58f1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryEnchanting.java @@ -0,0 +1,25 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.inventory.EnchantingInventory; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.ContainerEnchantTableInventory; + +public class CraftInventoryEnchanting extends CraftInventory implements EnchantingInventory { + public CraftInventoryEnchanting(ContainerEnchantTableInventory inventory) { + super(inventory); + } + + public void setItem(ItemStack item) { + setItem(0,item); + } + + public ItemStack getItem() { + return getItem(0); + } + + @Override + public ContainerEnchantTableInventory getInventory() { + return (ContainerEnchantTableInventory)inventory; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java new file mode 100644 index 0000000..37a631e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java @@ -0,0 +1,42 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.block.Furnace; +import org.bukkit.inventory.FurnaceInventory; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.TileEntityFurnace; + +public class CraftInventoryFurnace extends CraftInventory implements FurnaceInventory { + public CraftInventoryFurnace(TileEntityFurnace inventory) { + super(inventory); + } + + public ItemStack getResult() { + return getItem(2); + } + + public ItemStack getFuel() { + return getItem(1); + } + + public ItemStack getSmelting() { + return getItem(0); + } + + public void setFuel(ItemStack stack) { + setItem(1,stack); + } + + public void setResult(ItemStack stack) { + setItem(2,stack); + } + + public void setSmelting(ItemStack stack) { + setItem(0,stack); + } + + @Override + public Furnace getHolder() { + return (Furnace) inventory.getOwner(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java new file mode 100644 index 0000000..5adbd74 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IInventory; +import org.bukkit.inventory.HorseInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryHorse extends CraftInventory implements HorseInventory { + + public CraftInventoryHorse(IInventory inventory) { + super(inventory); + } + + public ItemStack getSaddle() { + return getItem(0); + } + + public ItemStack getArmor() { + return getItem(1); + } + + public void setSaddle(ItemStack stack) { + setItem(0, stack); + } + + public void setArmor(ItemStack stack) { + setItem(1, stack); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryMerchant.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryMerchant.java new file mode 100644 index 0000000..7f89c2a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryMerchant.java @@ -0,0 +1,10 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.InventoryMerchant; +import org.bukkit.inventory.MerchantInventory; + +public class CraftInventoryMerchant extends CraftInventory implements MerchantInventory { + public CraftInventoryMerchant(InventoryMerchant merchant) { + super(merchant); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java new file mode 100644 index 0000000..31ce4b7 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java @@ -0,0 +1,216 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.HashMap; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.PacketPlayOutHeldItemSlot; +import net.minecraft.server.PlayerInventory; + +public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.inventory.PlayerInventory, EntityEquipment { + + public CraftInventoryPlayer(net.minecraft.server.PlayerInventory inventory) { + super(inventory); + } + + @Override + public PlayerInventory getInventory() { + return (PlayerInventory) inventory; + } + + @Override + public int getSize() { + return super.getSize() - 4; + } + + public ItemStack getItemInHand() { + return CraftItemStack.asCraftMirror(getInventory().getItemInHand()); + } + + public void setItemInHand(ItemStack stack) { + setItem(getHeldItemSlot(), stack); + } + + public int getHeldItemSlot() { + return getInventory().itemInHandIndex; + } + + public void setHeldItemSlot(int slot) { + Validate.isTrue(slot >= 0 && slot < PlayerInventory.getHotbarSize(), "Slot is not between 0 and 8 inclusive"); + this.getInventory().itemInHandIndex = slot; + ((CraftPlayer) this.getHolder()).getHandle().playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(slot)); + } + + public ItemStack getHelmet() { + return getItem(getSize() + 3); + } + + public ItemStack getChestplate() { + return getItem(getSize() + 2); + } + + public ItemStack getLeggings() { + return getItem(getSize() + 1); + } + + public ItemStack getBoots() { + return getItem(getSize() + 0); + } + + public void setHelmet(ItemStack helmet) { + setItem(getSize() + 3, helmet); + } + + public void setChestplate(ItemStack chestplate) { + setItem(getSize() + 2, chestplate); + } + + public void setLeggings(ItemStack leggings) { + setItem(getSize() + 1, leggings); + } + + public void setBoots(ItemStack boots) { + setItem(getSize() + 0, boots); + } + + public ItemStack[] getArmorContents() { + net.minecraft.server.ItemStack[] mcItems = getInventory().getArmorContents(); + ItemStack[] ret = new ItemStack[mcItems.length]; + + for (int i = 0; i < mcItems.length; i++) { + ret[i] = CraftItemStack.asCraftMirror(mcItems[i]); + } + return ret; + } + + public void setArmorContents(ItemStack[] items) { + int cnt = getSize(); + + if (items == null) { + items = new ItemStack[4]; + } + for (ItemStack item : items) { + if (item == null || item.getTypeId() == 0) { + clear(cnt++); + } else { + setItem(cnt++, item); + } + } + } + + public int clear(int id, int data) { + int count = 0; + ItemStack[] items = getContents(); + ItemStack[] armor = getArmorContents(); + int armorSlot = getSize(); + + for (int i = 0; i < items.length; i++) { + ItemStack item = items[i]; + if (item == null) continue; + if (id > -1 && item.getTypeId() != id) continue; + if (data > -1 && item.getData().getData() != data) continue; + + count += item.getAmount(); + setItem(i, null); + } + + for (ItemStack item : armor) { + if (item == null) continue; + if (id > -1 && item.getTypeId() != id) continue; + if (data > -1 && item.getData().getData() != data) continue; + + count += item.getAmount(); + setItem(armorSlot++, null); + } + return count; + } + + @Override + public HumanEntity getHolder() { + return (HumanEntity) inventory.getOwner(); + } + + public float getItemInHandDropChance() { + return 1; + } + + public void setItemInHandDropChance(float chance) { + throw new UnsupportedOperationException(); + } + + public float getHelmetDropChance() { + return 1; + } + + public void setHelmetDropChance(float chance) { + throw new UnsupportedOperationException(); + } + + public float getChestplateDropChance() { + return 1; + } + + public void setChestplateDropChance(float chance) { + throw new UnsupportedOperationException(); + } + + public float getLeggingsDropChance() { + return 1; + } + + public void setLeggingsDropChance(float chance) { + throw new UnsupportedOperationException(); + } + + public float getBootsDropChance() { + return 1; + } + + public void setBootsDropChance(float chance) { + throw new UnsupportedOperationException(); + } + + // MineHQ start + @Override + public HashMap addItem(ItemStack... items) { + HashMap leftover = super.addItem(items); + this.updatePlayerInventory(); + return leftover; + } + + @Override + public void remove(Material material) { + super.remove(material); + this.updatePlayerInventory(); + } + + @Override + public HashMap removeItem(ItemStack... items) { + HashMap leftover = super.removeItem(items); + this.updatePlayerInventory(); + return leftover; + } + + @Override + public void remove(ItemStack item) { + super.remove(item); + this.updatePlayerInventory(); + } + + private void updatePlayerInventory() { + EntityHuman human = this.getInventory().player; + if (human instanceof EntityPlayer) { + EntityPlayer player = (EntityPlayer) human; + player.updateInventory(player.defaultContainer); + } + } + // MineHQ end +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java new file mode 100644 index 0000000..ae47a0e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java @@ -0,0 +1,142 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.GameMode; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.Container; + +public class CraftInventoryView extends InventoryView { + private final Container container; + private final CraftHumanEntity player; + private final CraftInventory viewing; + + public CraftInventoryView(HumanEntity player, Inventory viewing, Container container) { + // TODO: Should we make sure it really IS a CraftHumanEntity first? And a CraftInventory? + this.player = (CraftHumanEntity) player; + this.viewing = (CraftInventory) viewing; + this.container = container; + } + + @Override + public Inventory getTopInventory() { + return viewing; + } + + @Override + public Inventory getBottomInventory() { + return player.getInventory(); + } + + @Override + public HumanEntity getPlayer() { + return player; + } + + @Override + public InventoryType getType() { + InventoryType type = viewing.getType(); + if (type == InventoryType.CRAFTING && player.getGameMode() == GameMode.CREATIVE) { + return InventoryType.CREATIVE; + } + return type; + } + + @Override + public void setItem(int slot, ItemStack item) { + net.minecraft.server.ItemStack stack = CraftItemStack.asNMSCopy(item); + if (slot != -999) { + container.getSlot(slot).set(stack); + } else { + player.getHandle().drop(stack, false); + } + } + + @Override + public ItemStack getItem(int slot) { + if (slot == -999) { + return null; + } + return CraftItemStack.asCraftMirror(container.getSlot(slot).getItem()); + } + + public boolean isInTop(int rawSlot) { + return rawSlot < viewing.getSize(); + } + + public Container getHandle() { + return container; + } + + public static SlotType getSlotType(InventoryView inventory, int slot) { + SlotType type = SlotType.CONTAINER; + if (slot >= 0 && slot < inventory.getTopInventory().getSize()) { + switch(inventory.getType()) { + case FURNACE: + if (slot == 2) { + type = SlotType.RESULT; + } else if(slot == 1) { + type = SlotType.FUEL; + } else { + type = SlotType.CRAFTING; + } + break; + case BREWING: + if (slot == 3) { + type = SlotType.FUEL; + } else { + type = SlotType.CRAFTING; + } + break; + case ENCHANTING: + type = SlotType.CRAFTING; + break; + case WORKBENCH: + case CRAFTING: + if (slot == 0) { + type = SlotType.RESULT; + } else { + type = SlotType.CRAFTING; + } + break; + case MERCHANT: + if (slot == 2) { + type = SlotType.RESULT; + } else { + type = SlotType.CRAFTING; + } + break; + case BEACON: + type = SlotType.CRAFTING; + break; + case ANVIL: + if (slot == 2) { + type = SlotType.RESULT; + } else { + type = SlotType.CRAFTING; + } + break; + default: + // Nothing to do, it's a CONTAINER slot + } + } else { + if (slot == -999) { + type = SlotType.OUTSIDE; + } else if (inventory.getType() == InventoryType.CRAFTING) { + if (slot < 9) { + type = SlotType.ARMOR; + } else if (slot > 35) { + type = SlotType.QUICKBAR; + } + } else if (slot >= (inventory.countSlots() - 9)) { + type = SlotType.QUICKBAR; + } + } + return type; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java new file mode 100644 index 0000000..1b2394d --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java @@ -0,0 +1,142 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Collection; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import com.google.common.collect.ImmutableSet; + +public final class CraftItemFactory implements ItemFactory { + static final Color DEFAULT_LEATHER_COLOR = Color.fromRGB(0xA06540); + static final Collection KNOWN_NBT_ATTRIBUTE_NAMES; + private static final CraftItemFactory instance; + + static { + instance = new CraftItemFactory(); + ConfigurationSerialization.registerClass(CraftMetaItem.SerializableMeta.class); + KNOWN_NBT_ATTRIBUTE_NAMES = ImmutableSet.builder() + .add("generic.attackDamage") + .add("generic.followRange") + .add("generic.knockbackResistance") + .add("generic.maxHealth") + .add("generic.movementSpeed") + .add("horse.jumpStrength") + .add("zombie.spawnReinforcements") + .build(); + } + + private CraftItemFactory() { + } + + public boolean isApplicable(ItemMeta meta, ItemStack itemstack) { + if (itemstack == null) { + return false; + } + return isApplicable(meta, itemstack.getType()); + } + + public boolean isApplicable(ItemMeta meta, Material type) { + if (type == null || meta == null) { + return false; + } + if (!(meta instanceof CraftMetaItem)) { + throw new IllegalArgumentException("Meta of " + meta.getClass().toString() + " not created by " + CraftItemFactory.class.getName()); + } + + return ((CraftMetaItem) meta).applicableTo(type); + } + + public ItemMeta getItemMeta(Material material) { + Validate.notNull(material, "Material cannot be null"); + return getItemMeta(material, null); + } + + private ItemMeta getItemMeta(Material material, CraftMetaItem meta) { + switch (material) { + case AIR: + return null; + case WRITTEN_BOOK: + case BOOK_AND_QUILL: + return meta instanceof CraftMetaBook ? meta : new CraftMetaBook(meta); + case SKULL_ITEM: + return meta instanceof CraftMetaSkull ? meta : new CraftMetaSkull(meta); + case LEATHER_HELMET: + case LEATHER_CHESTPLATE: + case LEATHER_LEGGINGS: + case LEATHER_BOOTS: + return meta instanceof CraftMetaLeatherArmor ? meta : new CraftMetaLeatherArmor(meta); + case POTION: + return meta instanceof CraftMetaPotion ? meta : new CraftMetaPotion(meta); + case MAP: + return meta instanceof CraftMetaMap ? meta : new CraftMetaMap(meta); + case FIREWORK: + return meta instanceof CraftMetaFirework ? meta : new CraftMetaFirework(meta); + case FIREWORK_CHARGE: + return meta instanceof CraftMetaCharge ? meta : new CraftMetaCharge(meta); + case ENCHANTED_BOOK: + return meta instanceof CraftMetaEnchantedBook ? meta : new CraftMetaEnchantedBook(meta); + default: + return new CraftMetaItem(meta); + } + } + + public boolean equals(ItemMeta meta1, ItemMeta meta2) { + if (meta1 == meta2) { + return true; + } + if (meta1 != null && !(meta1 instanceof CraftMetaItem)) { + throw new IllegalArgumentException("First meta of " + meta1.getClass().getName() + " does not belong to " + CraftItemFactory.class.getName()); + } + if (meta2 != null && !(meta2 instanceof CraftMetaItem)) { + throw new IllegalArgumentException("Second meta " + meta2.getClass().getName() + " does not belong to " + CraftItemFactory.class.getName()); + } + if (meta1 == null) { + return ((CraftMetaItem) meta2).isEmpty(); + } + if (meta2 == null) { + return ((CraftMetaItem) meta1).isEmpty(); + } + + return equals((CraftMetaItem) meta1, (CraftMetaItem) meta2); + } + + boolean equals(CraftMetaItem meta1, CraftMetaItem meta2) { + /* + * This couldn't be done inside of the objects themselves, else force recursion. + * This is a fairly clean way of implementing it, by dividing the methods into purposes and letting each method perform its own function. + * + * The common and uncommon were split, as both could have variables not applicable to the other, like a skull and book. + * Each object needs its chance to say "hey wait a minute, we're not equal," but without the redundancy of using the 1.equals(2) && 2.equals(1) checking the 'commons' twice. + * + * Doing it this way fills all conditions of the .equals() method. + */ + return meta1.equalsCommon(meta2) && meta1.notUncommon(meta2) && meta2.notUncommon(meta1); + } + + public static CraftItemFactory instance() { + return instance; + } + + public ItemMeta asMetaFor(ItemMeta meta, ItemStack stack) { + Validate.notNull(stack, "Stack cannot be null"); + return asMetaFor(meta, stack.getType()); + } + + public ItemMeta asMetaFor(ItemMeta meta, Material material) { + Validate.notNull(material, "Material cannot be null"); + if (!(meta instanceof CraftMetaItem)) { + throw new IllegalArgumentException("Meta of " + (meta != null ? meta.getClass().toString() : "null") + " not created by " + CraftItemFactory.class.getName()); + } + return getItemMeta(material, (CraftMetaItem) meta); + } + + public Color getDefaultLeatherColor() { + return DEFAULT_LEATHER_COLOR; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java new file mode 100644 index 0000000..704be69 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -0,0 +1,411 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS; +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS_ID; +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS_LVL; + +import java.util.Map; + +import net.minecraft.server.EnchantmentManager; +import net.minecraft.server.Item; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import com.google.common.collect.ImmutableMap; + +@DelegateDeserialization(ItemStack.class) +public final class CraftItemStack extends ItemStack { + + public static net.minecraft.server.ItemStack asNMSCopy(ItemStack original) { + if (original instanceof CraftItemStack) { + CraftItemStack stack = (CraftItemStack) original; + return stack.handle == null ? null : stack.handle.cloneItemStack(); + } + if (original == null || original.getTypeId() <= 0) { + return null; + } + + Item item = CraftMagicNumbers.getItem(original.getType()); + + if (item == null) { + return null; + } + + net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(item, original.getAmount(), original.getDurability()); + if (original.hasItemMeta()) { + setItemMeta(stack, original.getItemMeta()); + } + return stack; + } + + public static net.minecraft.server.ItemStack copyNMSStack(net.minecraft.server.ItemStack original, int amount) { + net.minecraft.server.ItemStack stack = original.cloneItemStack(); + stack.count = amount; + return stack; + } + + /** + * Copies the NMS stack to return as a strictly-Bukkit stack + */ + public static ItemStack asBukkitCopy(net.minecraft.server.ItemStack original) { + if (original == null) { + return new ItemStack(Material.AIR); + } + ItemStack stack = new ItemStack(CraftMagicNumbers.getMaterial(original.getItem()), original.count, (short) original.getData()); + if (hasItemMeta(original)) { + stack.setItemMeta(getItemMeta(original)); + } + return stack; + } + + public static CraftItemStack asCraftMirror(net.minecraft.server.ItemStack original) { + return new CraftItemStack(original); + } + + public static CraftItemStack asCraftCopy(ItemStack original) { + if (original instanceof CraftItemStack) { + CraftItemStack stack = (CraftItemStack) original; + return new CraftItemStack(stack.handle == null ? null : stack.handle.cloneItemStack()); + } + return new CraftItemStack(original); + } + + public static CraftItemStack asNewCraftStack(Item item) { + return asNewCraftStack(item, 1); + } + + public static CraftItemStack asNewCraftStack(Item item, int amount) { + return new CraftItemStack(CraftMagicNumbers.getMaterial(item), amount, (short) 0, null); + } + + net.minecraft.server.ItemStack handle; + + /** + * Mirror + */ + private CraftItemStack(net.minecraft.server.ItemStack item) { + this.handle = item; + } + + private CraftItemStack(ItemStack item) { + this(item.getTypeId(), item.getAmount(), item.getDurability(), item.hasItemMeta() ? item.getItemMeta() : null); + } + + private CraftItemStack(Material type, int amount, short durability, ItemMeta itemMeta) { + setType(type); + setAmount(amount); + setDurability(durability); + setItemMeta(itemMeta); + } + + private CraftItemStack(int typeId, int amount, short durability, ItemMeta itemMeta) { + this(Material.getMaterial(typeId), amount, durability, itemMeta); + + } + + @Override + public int getTypeId() { + return handle != null ? CraftMagicNumbers.getId(handle.getItem()) : 0; + } + + @Override + public void setTypeId(int type) { + if (getTypeId() == type) { + return; + } else if (type == 0) { + handle = null; + } else if (CraftMagicNumbers.getItem(type) == null) { // :( + handle = null; + } else if (handle == null) { + handle = new net.minecraft.server.ItemStack(CraftMagicNumbers.getItem(type), 1, 0); + } else { + handle.setItem(CraftMagicNumbers.getItem(type)); + if (hasItemMeta()) { + // This will create the appropriate item meta, which will contain all the data we intend to keep + setItemMeta(handle, getItemMeta(handle)); + } + } + setData(null); + } + + @Override + public int getAmount() { + return handle != null ? handle.count : 0; + } + + @Override + public void setAmount(int amount) { + if (handle == null) { + return; + } + if (amount == 0) { + handle = null; + } else { + handle.count = amount; + } + } + + @Override + public void setDurability(final short durability) { + // Ignore damage if item is null + if (handle != null) { + handle.setData(durability); + } + } + + @Override + public short getDurability() { + if (handle != null) { + return (short) handle.getData(); + } else { + return -1; + } + } + + @Override + public int getMaxStackSize() { + return (handle == null) ? Material.AIR.getMaxStackSize() : handle.getItem().getMaxStackSize(); + } + + @Override + public void addUnsafeEnchantment(Enchantment ench, int level) { + Validate.notNull(ench, "Cannot add null enchantment"); + + if (!makeTag(handle)) { + return; + } + NBTTagList list = getEnchantmentList(handle); + if (list == null) { + list = new NBTTagList(); + handle.tag.set(ENCHANTMENTS.NBT, list); + } + int size = list.size(); + + for (int i = 0; i < size; i++) { + NBTTagCompound tag = (NBTTagCompound) list.get(i); + short id = tag.getShort(ENCHANTMENTS_ID.NBT); + if (id == ench.getId()) { + tag.setShort(ENCHANTMENTS_LVL.NBT, (short) level); + return; + } + } + NBTTagCompound tag = new NBTTagCompound(); + tag.setShort(ENCHANTMENTS_ID.NBT, (short) ench.getId()); + tag.setShort(ENCHANTMENTS_LVL.NBT, (short) level); + list.add(tag); + } + + static boolean makeTag(net.minecraft.server.ItemStack item) { + if (item == null) { + return false; + } + + if (item.tag == null) { + item.setTag(new NBTTagCompound()); + } + + return true; + } + + @Override + public boolean containsEnchantment(Enchantment ench) { + return getEnchantmentLevel(ench) > 0; + } + + @Override + public int getEnchantmentLevel(Enchantment ench) { + Validate.notNull(ench, "Cannot find null enchantment"); + if (handle == null) { + return 0; + } + return EnchantmentManager.getEnchantmentLevel(ench.getId(), handle); + } + + @Override + public int removeEnchantment(Enchantment ench) { + Validate.notNull(ench, "Cannot remove null enchantment"); + + NBTTagList list = getEnchantmentList(handle), listCopy; + if (list == null) { + return 0; + } + int index = Integer.MIN_VALUE; + int level = Integer.MIN_VALUE; + int size = list.size(); + + for (int i = 0; i < size; i++) { + NBTTagCompound enchantment = (NBTTagCompound) list.get(i); + int id = 0xffff & enchantment.getShort(ENCHANTMENTS_ID.NBT); + if (id == ench.getId()) { + index = i; + level = 0xffff & enchantment.getShort(ENCHANTMENTS_LVL.NBT); + break; + } + } + + if (index == Integer.MIN_VALUE) { + return 0; + } + if (size == 1) { + handle.tag.remove(ENCHANTMENTS.NBT); + if (handle.tag.isEmpty()) { + handle.tag = null; + } + return level; + } + + // This is workaround for not having an index removal + listCopy = new NBTTagList(); + for (int i = 0; i < size; i++) { + if (i != index) { + listCopy.add(list.get(i)); + } + } + handle.tag.set(ENCHANTMENTS.NBT, listCopy); + + return level; + } + + @Override + public Map getEnchantments() { + return getEnchantments(handle); + } + + static Map getEnchantments(net.minecraft.server.ItemStack item) { + NBTTagList list = (item != null && item.hasEnchantments()) ? item.getEnchantments() : null; + + if (list == null || list.size() == 0) { + return ImmutableMap.of(); + } + + ImmutableMap.Builder result = ImmutableMap.builder(); + + for (int i = 0; i < list.size(); i++) { + int id = 0xffff & ((NBTTagCompound) list.get(i)).getShort(ENCHANTMENTS_ID.NBT); + int level = 0xffff & ((NBTTagCompound) list.get(i)).getShort(ENCHANTMENTS_LVL.NBT); + + result.put(Enchantment.getById(id), level); + } + + return result.build(); + } + + static NBTTagList getEnchantmentList(net.minecraft.server.ItemStack item) { + return (item != null && item.hasEnchantments()) ? item.getEnchantments() : null; + } + + @Override + public CraftItemStack clone() { + CraftItemStack itemStack = (CraftItemStack) super.clone(); + if (this.handle != null) { + itemStack.handle = this.handle.cloneItemStack(); + } + return itemStack; + } + + @Override + public ItemMeta getItemMeta() { + return getItemMeta(handle); + } + + public static ItemMeta getItemMeta(net.minecraft.server.ItemStack item) { + if (!hasItemMeta(item)) { + return CraftItemFactory.instance().getItemMeta(getType(item)); + } + switch (getType(item)) { + case WRITTEN_BOOK: + case BOOK_AND_QUILL: + return new CraftMetaBook(item.tag); + case SKULL_ITEM: + return new CraftMetaSkull(item.tag); + case LEATHER_HELMET: + case LEATHER_CHESTPLATE: + case LEATHER_LEGGINGS: + case LEATHER_BOOTS: + return new CraftMetaLeatherArmor(item.tag); + case POTION: + return new CraftMetaPotion(item.tag); + case MAP: + return new CraftMetaMap(item.tag); + case FIREWORK: + return new CraftMetaFirework(item.tag); + case FIREWORK_CHARGE: + return new CraftMetaCharge(item.tag); + case ENCHANTED_BOOK: + return new CraftMetaEnchantedBook(item.tag); + default: + return new CraftMetaItem(item.tag); + } + } + + static Material getType(net.minecraft.server.ItemStack item) { + Material material = Material.getMaterial(item == null ? 0 : CraftMagicNumbers.getId(item.getItem())); + return material == null ? Material.AIR : material; + } + + @Override + public boolean setItemMeta(ItemMeta itemMeta) { + return setItemMeta(handle, itemMeta); + } + + public static boolean setItemMeta(net.minecraft.server.ItemStack item, ItemMeta itemMeta) { + if (item == null) { + return false; + } + if (CraftItemFactory.instance().equals(itemMeta, null)) { + item.tag = null; + return true; + } + if (!CraftItemFactory.instance().isApplicable(itemMeta, getType(item))) { + return false; + } + + NBTTagCompound tag = new NBTTagCompound(); + item.setTag(tag); + + ((CraftMetaItem) itemMeta).applyToItem(tag); + return true; + } + + @Override + public boolean isSimilar(ItemStack stack) { + if (stack == null) { + return false; + } + if (stack == this) { + return true; + } + if (!(stack instanceof CraftItemStack)) { + return stack.getClass() == ItemStack.class && stack.isSimilar(this); + } + + CraftItemStack that = (CraftItemStack) stack; + if (handle == that.handle) { + return true; + } + if (handle == null || that.handle == null) { + return false; + } + if (!(that.getTypeId() == getTypeId() && getDurability() == that.getDurability())) { + return false; + } + return hasItemMeta() ? that.hasItemMeta() && handle.tag.equals(that.handle.tag) : !that.hasItemMeta(); + } + + @Override + public boolean hasItemMeta() { + return hasItemMeta(handle); + } + + static boolean hasItemMeta(net.minecraft.server.ItemStack item) { + return !(item == null || item.tag == null || item.tag.isEmpty()); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java new file mode 100644 index 0000000..bdc6364 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java @@ -0,0 +1,265 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.BookMeta; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap.Builder; + +// Spigot start +import static org.spigotmc.ValidateUtils.*; +// Spigot end + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaBook extends CraftMetaItem implements BookMeta { + static final ItemMetaKey BOOK_TITLE = new ItemMetaKey("title"); + static final ItemMetaKey BOOK_AUTHOR = new ItemMetaKey("author"); + static final ItemMetaKey BOOK_PAGES = new ItemMetaKey("pages"); + static final int MAX_PAGE_LENGTH = 256; + static final int MAX_TITLE_LENGTH = 0xffff; + + private String title; + private String author; + private List pages = new ArrayList(); + + CraftMetaBook(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaBook)) { + return; + } + CraftMetaBook bookMeta = (CraftMetaBook) meta; + this.title = bookMeta.title; + this.author = bookMeta.author; + pages.addAll(bookMeta.pages); + } + + CraftMetaBook(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(BOOK_TITLE.NBT)) { + this.title = limit( tag.getString(BOOK_TITLE.NBT), 1024 ); // Spigot + } + + if (tag.hasKey(BOOK_AUTHOR.NBT)) { + this.author = limit( tag.getString(BOOK_AUTHOR.NBT), 1024 ); // Spigot + } + + if (tag.hasKey(BOOK_PAGES.NBT)) { + NBTTagList pages = tag.getList(BOOK_PAGES.NBT, 8); + String[] pageArray = new String[pages.size()]; + + for (int i = 0; i < pages.size(); i++) { + String page = limit( pages.getString(i), 2048 ); // Spigot + pageArray[i] = page; + } + + addPage(pageArray); + } + } + + CraftMetaBook(Map map) { + super(map); + + setAuthor(SerializableMeta.getString(map, BOOK_AUTHOR.BUKKIT, true)); + + setTitle(SerializableMeta.getString(map, BOOK_TITLE.BUKKIT, true)); + + Iterable pages = SerializableMeta.getObject(Iterable.class, map, BOOK_PAGES.BUKKIT, true); + CraftMetaItem.safelyAdd(pages, this.pages, MAX_PAGE_LENGTH); + } + + @Override + void applyToItem(NBTTagCompound itemData) { + super.applyToItem(itemData); + + if (hasTitle()) { + itemData.setString(BOOK_TITLE.NBT, this.title); + } + + if (hasAuthor()) { + itemData.setString(BOOK_AUTHOR.NBT, this.author); + } + + if (hasPages()) { + itemData.set(BOOK_PAGES.NBT, createStringList(pages)); + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isBookEmpty(); + } + + boolean isBookEmpty() { + return !(hasPages() || hasAuthor() || hasTitle()); + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case WRITTEN_BOOK: + case BOOK_AND_QUILL: + return true; + default: + return false; + } + } + + public boolean hasAuthor() { + return !Strings.isNullOrEmpty(author); + } + + public boolean hasTitle() { + return !Strings.isNullOrEmpty(title); + } + + public boolean hasPages() { + return !pages.isEmpty(); + } + + public String getTitle() { + return this.title; + } + + public boolean setTitle(final String title) { + if (title == null) { + this.title = null; + return true; + } else if (title.length() > MAX_TITLE_LENGTH) { + return false; + } + + this.title = title; + return true; + } + + public String getAuthor() { + return this.author; + } + + public void setAuthor(final String author) { + this.author = author; + } + + public String getPage(final int page) { + Validate.isTrue(isValidPage(page), "Invalid page number"); + return pages.get(page - 1); + } + + public void setPage(final int page, final String text) { + if (!isValidPage(page)) { + throw new IllegalArgumentException("Invalid page number " + page + "/" + pages.size()); + } + + pages.set(page - 1, text == null ? "" : text.length() > MAX_PAGE_LENGTH ? text.substring(0, MAX_PAGE_LENGTH) : text); + } + + public void setPages(final String... pages) { + this.pages.clear(); + + addPage(pages); + } + + public void addPage(final String... pages) { + for (String page : pages) { + if (page == null) { + page = ""; + } else if (page.length() > MAX_PAGE_LENGTH) { + page = page.substring(0, MAX_PAGE_LENGTH); + } + + this.pages.add(page); + } + } + + public int getPageCount() { + return pages.size(); + } + + public List getPages() { + return ImmutableList.copyOf(pages); + } + + public void setPages(List pages) { + this.pages.clear(); + CraftMetaItem.safelyAdd(pages, this.pages, MAX_PAGE_LENGTH); + } + + private boolean isValidPage(int page) { + return page > 0 && page <= pages.size(); + } + + @Override + public CraftMetaBook clone() { + CraftMetaBook meta = (CraftMetaBook) super.clone(); + meta.pages = new ArrayList(pages); + return meta; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasTitle()) { + hash = 61 * hash + this.title.hashCode(); + } + if (hasAuthor()) { + hash = 61 * hash + 13 * this.author.hashCode(); + } + if (hasPages()) { + hash = 61 * hash + 17 * this.pages.hashCode(); + } + return original != hash ? CraftMetaBook.class.hashCode() ^ hash : hash; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaBook) { + CraftMetaBook that = (CraftMetaBook) meta; + + return (hasTitle() ? that.hasTitle() && this.title.equals(that.title) : !that.hasTitle()) + && (hasAuthor() ? that.hasAuthor() && this.author.equals(that.author) : !that.hasAuthor()) + && (hasPages() ? that.hasPages() && this.pages.equals(that.pages) : !that.hasPages()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaBook || isBookEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasTitle()) { + builder.put(BOOK_TITLE.BUKKIT, title); + } + + if (hasAuthor()) { + builder.put(BOOK_AUTHOR.BUKKIT, author); + } + + if (hasPages()) { + builder.put(BOOK_PAGES.BUKKIT, pages); + } + + return builder; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java new file mode 100644 index 0000000..bff3be9 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java @@ -0,0 +1,128 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.FireworkEffect; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.FireworkEffectMeta; + +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaCharge extends CraftMetaItem implements FireworkEffectMeta { + static final ItemMetaKey EXPLOSION = new ItemMetaKey("Explosion", "firework-effect"); + + private FireworkEffect effect; + + CraftMetaCharge(CraftMetaItem meta) { + super(meta); + + if (meta instanceof CraftMetaCharge) { + effect = ((CraftMetaCharge) meta).effect; + } + } + + CraftMetaCharge(Map map) { + super(map); + + setEffect(SerializableMeta.getObject(FireworkEffect.class, map, EXPLOSION.BUKKIT, true)); + } + + CraftMetaCharge(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(EXPLOSION.NBT)) { + effect = CraftMetaFirework.getEffect(tag.getCompound(EXPLOSION.NBT)); + } + } + + public void setEffect(FireworkEffect effect) { + this.effect = effect; + } + + public boolean hasEffect() { + return effect != null; + } + + public FireworkEffect getEffect() { + return effect; + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + if (hasEffect()) { + itemTag.set(EXPLOSION.NBT, CraftMetaFirework.getExplosion(effect)); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case FIREWORK_CHARGE: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && !hasChargeMeta(); + } + + boolean hasChargeMeta() { + return hasEffect(); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaCharge) { + CraftMetaCharge that = (CraftMetaCharge) meta; + + return (hasEffect() ? that.hasEffect() && this.effect.equals(that.effect) : !that.hasEffect()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaCharge || !hasChargeMeta()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasEffect()) { + hash = 61 * hash + effect.hashCode(); + } + + return hash != original ? CraftMetaCharge.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaCharge clone() { + return (CraftMetaCharge) super.clone(); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasEffect()) { + builder.put(EXPLOSION.BUKKIT, effect); + } + + return builder; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java new file mode 100644 index 0000000..8d44e55 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java @@ -0,0 +1,168 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorageMeta { + static final ItemMetaKey STORED_ENCHANTMENTS = new ItemMetaKey("StoredEnchantments", "stored-enchants"); + + private Map enchantments; + + CraftMetaEnchantedBook(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaEnchantedBook)) { + return; + } + + CraftMetaEnchantedBook that = (CraftMetaEnchantedBook) meta; + + if (that.hasEnchants()) { + this.enchantments = new HashMap(that.enchantments); + } + } + + CraftMetaEnchantedBook(NBTTagCompound tag) { + super(tag); + + if (!tag.hasKey(STORED_ENCHANTMENTS.NBT)) { + return; + } + + enchantments = buildEnchantments(tag, STORED_ENCHANTMENTS); + } + + CraftMetaEnchantedBook(Map map) { + super(map); + + enchantments = buildEnchantments(map, STORED_ENCHANTMENTS); + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + applyEnchantments(enchantments, itemTag, STORED_ENCHANTMENTS); + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case ENCHANTED_BOOK: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isEnchantedEmpty(); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaEnchantedBook) { + CraftMetaEnchantedBook that = (CraftMetaEnchantedBook) meta; + + return (hasStoredEnchants() ? that.hasStoredEnchants() && this.enchantments.equals(that.enchantments) : !that.hasStoredEnchants()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaEnchantedBook || isEnchantedEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasStoredEnchants()) { + hash = 61 * hash + enchantments.hashCode(); + } + + return original != hash ? CraftMetaEnchantedBook.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaEnchantedBook clone() { + CraftMetaEnchantedBook meta = (CraftMetaEnchantedBook) super.clone(); + + if (this.enchantments != null) { + meta.enchantments = new HashMap(this.enchantments); + } + + return meta; + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + serializeEnchantments(enchantments, builder, STORED_ENCHANTMENTS); + + return builder; + } + + boolean isEnchantedEmpty() { + return !hasStoredEnchants(); + } + + public boolean hasStoredEnchant(Enchantment ench) { + return hasStoredEnchants() && enchantments.containsKey(ench); + } + + public int getStoredEnchantLevel(Enchantment ench) { + Integer level = hasStoredEnchants() ? enchantments.get(ench) : null; + if (level == null) { + return 0; + } + return level; + } + + public Map getStoredEnchants() { + return hasStoredEnchants() ? ImmutableMap.copyOf(enchantments) : ImmutableMap.of(); + } + + public boolean addStoredEnchant(Enchantment ench, int level, boolean ignoreRestrictions) { + if (enchantments == null) { + enchantments = new HashMap(4); + } + + if (ignoreRestrictions || level >= ench.getStartLevel() && level <= ench.getMaxLevel()) { + Integer old = enchantments.put(ench, level); + return old == null || old != level; + } + return false; + } + + public boolean removeStoredEnchant(Enchantment ench) { + return hasStoredEnchants() && enchantments.remove(ench) != null; + } + + public boolean hasStoredEnchants() { + return !(enchantments == null || enchantments.isEmpty()); + } + + public boolean hasConflictingStoredEnchant(Enchantment ench) { + return checkConflictingEnchants(enchantments, ench); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java new file mode 100644 index 0000000..5a409ae --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java @@ -0,0 +1,386 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific.To; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.FireworkMeta; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaFirework extends CraftMetaItem implements FireworkMeta { + /* + "Fireworks", "Explosion", "Explosions", "Flight", "Type", "Trail", "Flicker", "Colors", "FadeColors"; + + Fireworks + - Compound: Fireworks + -- Byte: Flight + -- List: Explosions + --- Compound: Explosion + ---- IntArray: Colors + ---- Byte: Type + ---- Boolean: Trail + ---- Boolean: Flicker + ---- IntArray: FadeColors + */ + + @Specific(To.NBT) + static final ItemMetaKey FIREWORKS = new ItemMetaKey("Fireworks"); + static final ItemMetaKey FLIGHT = new ItemMetaKey("Flight", "power"); + static final ItemMetaKey EXPLOSIONS = new ItemMetaKey("Explosions", "firework-effects"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_COLORS = new ItemMetaKey("Colors"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_TYPE = new ItemMetaKey("Type"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_TRAIL = new ItemMetaKey("Trail"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_FLICKER = new ItemMetaKey("Flicker"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_FADE = new ItemMetaKey("FadeColors"); + + private List effects; + private int power; + + CraftMetaFirework(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaFirework)) { + return; + } + + CraftMetaFirework that = (CraftMetaFirework) meta; + + this.power = that.power; + + if (that.hasEffects()) { + this.effects = new ArrayList(that.effects); + } + } + + CraftMetaFirework(NBTTagCompound tag) { + super(tag); + + if (!tag.hasKey(FIREWORKS.NBT)) { + return; + } + + NBTTagCompound fireworks = tag.getCompound(FIREWORKS.NBT); + + power = 0xff & fireworks.getByte(FLIGHT.NBT); + + if (!fireworks.hasKey(EXPLOSIONS.NBT)) { + return; + } + + NBTTagList fireworkEffects = fireworks.getList(EXPLOSIONS.NBT, 10); + List effects = this.effects = new ArrayList(fireworkEffects.size()); + + for (int i = 0; i < fireworkEffects.size(); i++) { + effects.add(getEffect((NBTTagCompound) fireworkEffects.get(i))); + } + } + + static FireworkEffect getEffect(NBTTagCompound explosion) { + FireworkEffect.Builder effect = FireworkEffect.builder() + .flicker(explosion.getBoolean(EXPLOSION_FLICKER.NBT)) + .trail(explosion.getBoolean(EXPLOSION_TRAIL.NBT)) + .with(getEffectType(0xff & explosion.getByte(EXPLOSION_TYPE.NBT))); + + for (int color : explosion.getIntArray(EXPLOSION_COLORS.NBT)) { + effect.withColor(Color.fromRGB(color)); + } + + for (int color : explosion.getIntArray(EXPLOSION_FADE.NBT)) { + effect.withFade(Color.fromRGB(color)); + } + + return effect.build(); + } + + static NBTTagCompound getExplosion(FireworkEffect effect) { + NBTTagCompound explosion = new NBTTagCompound(); + + if (effect.hasFlicker()) { + explosion.setBoolean(EXPLOSION_FLICKER.NBT, true); + } + + if (effect.hasTrail()) { + explosion.setBoolean(EXPLOSION_TRAIL.NBT, true); + } + + addColors(explosion, EXPLOSION_COLORS, effect.getColors()); + addColors(explosion, EXPLOSION_FADE, effect.getFadeColors()); + + explosion.setByte(EXPLOSION_TYPE.NBT, (byte) getNBT(effect.getType())); + + return explosion; + } + + static int getNBT(Type type) { + switch (type) { + case BALL: + return 0; + case BALL_LARGE: + return 1; + case STAR: + return 2; + case CREEPER: + return 3; + case BURST: + return 4; + default: + throw new IllegalStateException(type.toString()); // Spigot + } + } + + static Type getEffectType(int nbt) { + switch (nbt) { + case 0: + return Type.BALL; + case 1: + return Type.BALL_LARGE; + case 2: + return Type.STAR; + case 3: + return Type.CREEPER; + case 4: + return Type.BURST; + default: + throw new IllegalStateException(Integer.toString(nbt)); // Spigot + } + } + + CraftMetaFirework(Map map) { + super(map); + + Integer power = SerializableMeta.getObject(Integer.class, map, FLIGHT.BUKKIT, true); + if (power != null) { + setPower(power); + } + + Iterable effects = SerializableMeta.getObject(Iterable.class, map, EXPLOSIONS.BUKKIT, true); + safelyAddEffects(effects); + } + + public boolean hasEffects() { + return !(effects == null || effects.isEmpty()); + } + + void safelyAddEffects(Iterable collection) { + if (collection == null || (collection instanceof Collection && ((Collection) collection).isEmpty())) { + return; + } + + List effects = this.effects; + if (effects == null) { + effects = this.effects = new ArrayList(); + } + + for (Object obj : collection) { + if (obj instanceof FireworkEffect) { + effects.add((FireworkEffect) obj); + } else { + throw new IllegalArgumentException(obj + " in " + collection + " is not a FireworkEffect"); + } + } + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + if (isFireworkEmpty()) { + return; + } + + NBTTagCompound fireworks = itemTag.getCompound(FIREWORKS.NBT); + itemTag.set(FIREWORKS.NBT, fireworks); + + if (hasEffects()) { + NBTTagList effects = new NBTTagList(); + for (FireworkEffect effect : this.effects) { + effects.add(getExplosion(effect)); + } + + if (effects.size() > 0) { + fireworks.set(EXPLOSIONS.NBT, effects); + } + } + + if (hasPower()) { + fireworks.setByte(FLIGHT.NBT, (byte) power); + } + } + + static void addColors(NBTTagCompound compound, ItemMetaKey key, List colors) { + if (colors.isEmpty()) { + return; + } + + final int[] colorArray = new int[colors.size()]; + int i = 0; + for (Color color : colors) { + colorArray[i++] = color.asRGB(); + } + + compound.setIntArray(key.NBT, colorArray); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case FIREWORK: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isFireworkEmpty(); + } + + boolean isFireworkEmpty() { + return !(hasEffects() || hasPower()); + } + + boolean hasPower() { + return power != 0; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + + if (meta instanceof CraftMetaFirework) { + CraftMetaFirework that = (CraftMetaFirework) meta; + + return (hasPower() ? that.hasPower() && this.power == that.power : !that.hasPower()) + && (hasEffects() ? that.hasEffects() && this.effects.equals(that.effects) : !that.hasEffects()); + } + + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaFirework || isFireworkEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasPower()) { + hash = 61 * hash + power; + } + if (hasEffects()) { + hash = 61 * hash + 13 * effects.hashCode(); + } + return hash != original ? CraftMetaFirework.class.hashCode() ^ hash : hash; + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasEffects()) { + builder.put(EXPLOSIONS.BUKKIT, ImmutableList.copyOf(effects)); + } + + if (hasPower()) { + builder.put(FLIGHT.BUKKIT, power); + } + + return builder; + } + + @Override + public CraftMetaFirework clone() { + CraftMetaFirework meta = (CraftMetaFirework) super.clone(); + + if (this.effects != null) { + meta.effects = new ArrayList(this.effects); + } + + return meta; + } + + public void addEffect(FireworkEffect effect) { + Validate.notNull(effect, "Effect cannot be null"); + if (this.effects == null) { + this.effects = new ArrayList(); + } + this.effects.add(effect); + } + + public void addEffects(FireworkEffect...effects) { + Validate.notNull(effects, "Effects cannot be null"); + if (effects.length == 0) { + return; + } + + List list = this.effects; + if (list == null) { + list = this.effects = new ArrayList(); + } + + for (FireworkEffect effect : effects) { + Validate.notNull(effect, "Effect cannot be null"); + list.add(effect); + } + } + + public void addEffects(Iterable effects) { + Validate.notNull(effects, "Effects cannot be null"); + safelyAddEffects(effects); + } + + public List getEffects() { + return this.effects == null ? ImmutableList.of() : ImmutableList.copyOf(this.effects); + } + + public int getEffectsSize() { + return this.effects == null ? 0 : this.effects.size(); + } + + public void removeEffect(int index) { + if (this.effects == null) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: 0"); + } else { + this.effects.remove(index); + } + } + + public void clearEffects() { + this.effects = null; + } + + public int getPower() { + return this.power; + } + + public void setPower(int power) { + Validate.isTrue(power >= 0, "Power cannot be less than zero: ", power); + Validate.isTrue(power < 0x80, "Power cannot be more than 127: ", power); + this.power = power; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java new file mode 100644 index 0000000..772160e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -0,0 +1,873 @@ +package org.bukkit.craftbukkit.inventory; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagDouble; +import net.minecraft.server.NBTTagInt; +import net.minecraft.server.NBTTagList; +import net.minecraft.server.NBTTagLong; +import net.minecraft.server.NBTTagString; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.craftbukkit.Overridden; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Repairable; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +// Spigot start +import static org.spigotmc.ValidateUtils.*; +import net.minecraft.server.GenericAttributes; +import net.minecraft.server.IAttribute; +// Spigot end + +/** + * Children must include the following: + * + *
          • Constructor(CraftMetaItem meta) + *
          • Constructor(NBTTagCompound tag) + *
          • Constructor(Map map) + *

            + *
          • void applyToItem(NBTTagCompound tag) + *
          • boolean applicableTo(Material type) + *

            + *
          • boolean equalsCommon(CraftMetaItem meta) + *
          • boolean notUncommon(CraftMetaItem meta) + *

            + *
          • boolean isEmpty() + *
          • boolean is{Type}Empty() + *

            + *
          • int applyHash() + *
          • public Class clone() + *

            + *
          • Builder serialize(Builder builder) + *
          • SerializableMeta.Deserializers deserializer() + */ +@DelegateDeserialization(CraftMetaItem.SerializableMeta.class) +class CraftMetaItem implements ItemMeta, Repairable { + + static class ItemMetaKey { + + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.FIELD) + @interface Specific { + enum To { + BUKKIT, + NBT, + ; + } + To value(); + } + + final String BUKKIT; + final String NBT; + + ItemMetaKey(final String both) { + this(both, both); + } + + ItemMetaKey(final String nbt, final String bukkit) { + this.NBT = nbt; + this.BUKKIT = bukkit; + } + } + + @SerializableAs("ItemMeta") + public static class SerializableMeta implements ConfigurationSerializable { + static final String TYPE_FIELD = "meta-type"; + + static final ImmutableMap, String> classMap; + static final ImmutableMap> constructorMap; + + static { + classMap = ImmutableMap., String>builder() + .put(CraftMetaBook.class, "BOOK") + .put(CraftMetaSkull.class, "SKULL") + .put(CraftMetaLeatherArmor.class, "LEATHER_ARMOR") + .put(CraftMetaMap.class, "MAP") + .put(CraftMetaPotion.class, "POTION") + .put(CraftMetaEnchantedBook.class, "ENCHANTED") + .put(CraftMetaFirework.class, "FIREWORK") + .put(CraftMetaCharge.class, "FIREWORK_EFFECT") + .put(CraftMetaItem.class, "UNSPECIFIC") + .build(); + + final ImmutableMap.Builder> classConstructorBuilder = ImmutableMap.builder(); + for (Map.Entry, String> mapping : classMap.entrySet()) { + try { + classConstructorBuilder.put(mapping.getValue(), mapping.getKey().getDeclaredConstructor(Map.class)); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + constructorMap = classConstructorBuilder.build(); + } + + private SerializableMeta() { + } + + public static ItemMeta deserialize(Map map) throws Throwable { + Validate.notNull(map, "Cannot deserialize null map"); + + String type = getString(map, TYPE_FIELD, false); + Constructor constructor = constructorMap.get(type); + + if (constructor == null) { + throw new IllegalArgumentException(type + " is not a valid " + TYPE_FIELD); + } + + try { + return constructor.newInstance(map); + } catch (final InstantiationException e) { + throw new AssertionError(e); + } catch (final IllegalAccessException e) { + throw new AssertionError(e); + } catch (final InvocationTargetException e) { + throw e.getCause(); + } + } + + public Map serialize() { + throw new AssertionError(); + } + + static String getString(Map map, Object field, boolean nullable) { + return getObject(String.class, map, field, nullable); + } + + static boolean getBoolean(Map map, Object field) { + Boolean value = getObject(Boolean.class, map, field, true); + return value != null && value; + } + + static T getObject(Class clazz, Map map, Object field, boolean nullable) { + final Object object = map.get(field); + + if (clazz.isInstance(object)) { + return clazz.cast(object); + } + if (object == null) { + if (!nullable) { + throw new NoSuchElementException(map + " does not contain " + field); + } + return null; + } + throw new IllegalArgumentException(field + "(" + object + ") is not a valid " + clazz); + } + } + + static final ItemMetaKey NAME = new ItemMetaKey("Name", "display-name"); + @Specific(Specific.To.NBT) + static final ItemMetaKey DISPLAY = new ItemMetaKey("display"); + static final ItemMetaKey LORE = new ItemMetaKey("Lore", "lore"); + static final ItemMetaKey ENCHANTMENTS = new ItemMetaKey("ench", "enchants"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ENCHANTMENTS_ID = new ItemMetaKey("id"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ENCHANTMENTS_LVL = new ItemMetaKey("lvl"); + static final ItemMetaKey REPAIR = new ItemMetaKey("RepairCost", "repair-cost"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES = new ItemMetaKey("AttributeModifiers"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_IDENTIFIER = new ItemMetaKey("AttributeName"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_NAME = new ItemMetaKey("Name"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_VALUE = new ItemMetaKey("Amount"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_TYPE = new ItemMetaKey("Operation"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_UUID_HIGH = new ItemMetaKey("UUIDMost"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_UUID_LOW = new ItemMetaKey("UUIDLeast"); + static final ItemMetaKey UNBREAKABLE = new ItemMetaKey("Unbreakable"); // Spigot + + private String displayName; + private List lore; + private Map enchantments; + private int repairCost; + private final NBTTagList attributes; + + CraftMetaItem(CraftMetaItem meta) { + if (meta == null) { + attributes = null; + return; + } + + this.displayName = meta.displayName; + + if (meta.hasLore()) { + this.lore = new ArrayList(meta.lore); + } + + if (meta.hasEnchants()) { + this.enchantments = new HashMap(meta.enchantments); + } + + this.repairCost = meta.repairCost; + this.attributes = meta.attributes; + spigot.setUnbreakable( meta.spigot.isUnbreakable() ); // Spigot + } + + CraftMetaItem(NBTTagCompound tag) { + if (tag.hasKey(DISPLAY.NBT)) { + NBTTagCompound display = tag.getCompound(DISPLAY.NBT); + + if (display.hasKey(NAME.NBT)) { + displayName = limit( display.getString(NAME.NBT), 1024 ); // Spigot + } + + if (display.hasKey(LORE.NBT)) { + NBTTagList list = display.getList(LORE.NBT, 8); + lore = new ArrayList(list.size()); + + for (int index = 0; index < list.size(); index++) { + String line = limit( list.getString(index), 1024 ); // Spigot + lore.add(line); + } + } + } + + this.enchantments = buildEnchantments(tag, ENCHANTMENTS); + + if (tag.hasKey(REPAIR.NBT)) { + repairCost = tag.getInt(REPAIR.NBT); + } + + + if (tag.get(ATTRIBUTES.NBT) instanceof NBTTagList) { + NBTTagList save = null; + NBTTagList nbttaglist = tag.getList(ATTRIBUTES.NBT, 10); + + // Spigot start + net.minecraft.util.gnu.trove.map.hash.TObjectDoubleHashMap attributeTracker = new net.minecraft.util.gnu.trove.map.hash.TObjectDoubleHashMap(); + net.minecraft.util.gnu.trove.map.hash.TObjectDoubleHashMap attributeTrackerX = new net.minecraft.util.gnu.trove.map.hash.TObjectDoubleHashMap(); + Map attributesByName = new HashMap(); + attributeTracker.put( "generic.maxHealth", 20.0 ); + attributesByName.put( "generic.maxHealth", GenericAttributes.maxHealth ); + attributeTracker.put( "generic.followRange", 32.0 ); + attributesByName.put( "generic.followRange", GenericAttributes.b ); + attributeTracker.put( "generic.knockbackResistance", 0.0 ); + attributesByName.put( "generic.knockbackResistance", GenericAttributes.c ); + attributeTracker.put( "generic.movementSpeed", 0.7 ); + attributesByName.put( "generic.movementSpeed", GenericAttributes.d ); + attributeTracker.put( "generic.attackDamage", 1.0 ); + attributesByName.put( "generic.attackDamage", GenericAttributes.e ); + NBTTagList oldList = nbttaglist; + nbttaglist = new NBTTagList(); + + List op0 = new ArrayList(); + List op1 = new ArrayList(); + List op2 = new ArrayList(); + + for ( int i = 0; i < oldList.size(); ++i ) + { + NBTTagCompound nbttagcompound = oldList.get( i ); + if ( nbttagcompound == null ) continue; + + if ( !( nbttagcompound.get( ATTRIBUTES_UUID_HIGH.NBT ) instanceof NBTTagLong ) ) + { + continue; + } + if ( !( nbttagcompound.get( ATTRIBUTES_UUID_LOW.NBT ) instanceof NBTTagLong ) ) + { + continue; + } + if ( !( nbttagcompound.get( ATTRIBUTES_IDENTIFIER.NBT ) instanceof NBTTagString ) || !CraftItemFactory.KNOWN_NBT_ATTRIBUTE_NAMES.contains( nbttagcompound.getString( ATTRIBUTES_IDENTIFIER.NBT ) ) ) + { + continue; + } + if ( !( nbttagcompound.get( ATTRIBUTES_NAME.NBT ) instanceof NBTTagString ) || nbttagcompound.getString( ATTRIBUTES_NAME.NBT ).isEmpty() ) + { + continue; + } + if ( !( nbttagcompound.get( ATTRIBUTES_VALUE.NBT ) instanceof NBTTagDouble ) ) + { + continue; + } + if ( !( nbttagcompound.get( ATTRIBUTES_TYPE.NBT ) instanceof NBTTagInt ) || nbttagcompound.getInt( ATTRIBUTES_TYPE.NBT ) < 0 || nbttagcompound.getInt( ATTRIBUTES_TYPE.NBT ) > 2 ) + { + continue; + } + + switch ( nbttagcompound.getInt( ATTRIBUTES_TYPE.NBT ) ) + { + case 0: + op0.add( nbttagcompound ); + break; + case 1: + op1.add( nbttagcompound ); + break; + case 2: + op2.add( nbttagcompound ); + break; + } + } + for ( NBTTagCompound nbtTagCompound : op0 ) + { + String name = nbtTagCompound.getString( ATTRIBUTES_IDENTIFIER.NBT ); + if ( attributeTracker.containsKey( name ) ) + { + double val = attributeTracker.get( name ); + val += nbtTagCompound.getDouble( ATTRIBUTES_VALUE.NBT ); + if ( val != attributesByName.get( name ).a( val ) ) + { + continue; + } + attributeTracker.put( name, val ); + } + nbttaglist.add( nbtTagCompound ); + } + for ( String name : attributeTracker.keySet() ) + { + attributeTrackerX.put( name, attributeTracker.get( name ) ); + } + for ( NBTTagCompound nbtTagCompound : op1 ) + { + String name = nbtTagCompound.getString( ATTRIBUTES_IDENTIFIER.NBT ); + if ( attributeTracker.containsKey( name ) ) + { + double val = attributeTracker.get( name ); + double valX = attributeTrackerX.get( name ); + val += valX * nbtTagCompound.getDouble( ATTRIBUTES_VALUE.NBT ); + if ( val != attributesByName.get( name ).a( val ) ) + { + continue; + } + attributeTracker.put( name, val ); + } + nbttaglist.add( nbtTagCompound ); + } + for ( NBTTagCompound nbtTagCompound : op2 ) + { + String name = nbtTagCompound.getString( ATTRIBUTES_IDENTIFIER.NBT ); + if ( attributeTracker.containsKey( name ) ) + { + double val = attributeTracker.get( name ); + val += val * nbtTagCompound.getDouble( ATTRIBUTES_VALUE.NBT ); + if ( val != attributesByName.get( name ).a( val ) ) + { + continue; + } + attributeTracker.put( name, val ); + } + nbttaglist.add( nbtTagCompound ); + } + + // Spigot end + + for (int i = 0; i < nbttaglist.size(); ++i) { + if (!(nbttaglist.get(i) instanceof NBTTagCompound)) { + continue; + } + NBTTagCompound nbttagcompound = (NBTTagCompound) nbttaglist.get(i); + + if (!(nbttagcompound.get(ATTRIBUTES_UUID_HIGH.NBT) instanceof NBTTagLong)) { + continue; + } + if (!(nbttagcompound.get(ATTRIBUTES_UUID_LOW.NBT) instanceof NBTTagLong)) { + continue; + } + if (!(nbttagcompound.get(ATTRIBUTES_IDENTIFIER.NBT) instanceof NBTTagString) || !CraftItemFactory.KNOWN_NBT_ATTRIBUTE_NAMES.contains(nbttagcompound.getString(ATTRIBUTES_IDENTIFIER.NBT))) { + continue; + } + if (!(nbttagcompound.get(ATTRIBUTES_NAME.NBT) instanceof NBTTagString) || nbttagcompound.getString(ATTRIBUTES_NAME.NBT).isEmpty()) { + continue; + } + if (!(nbttagcompound.get(ATTRIBUTES_VALUE.NBT) instanceof NBTTagDouble)) { + continue; + } + if (!(nbttagcompound.get(ATTRIBUTES_TYPE.NBT) instanceof NBTTagInt) || nbttagcompound.getInt(ATTRIBUTES_TYPE.NBT) < 0 || nbttagcompound.getInt(ATTRIBUTES_TYPE.NBT) > 2) { + continue; + } + + if (save == null) { + save = new NBTTagList(); + } + + NBTTagCompound entry = new NBTTagCompound(); + entry.set(ATTRIBUTES_UUID_HIGH.NBT, nbttagcompound.get(ATTRIBUTES_UUID_HIGH.NBT)); + entry.set(ATTRIBUTES_UUID_LOW.NBT, nbttagcompound.get(ATTRIBUTES_UUID_LOW.NBT)); + entry.set(ATTRIBUTES_IDENTIFIER.NBT, nbttagcompound.get(ATTRIBUTES_IDENTIFIER.NBT)); + entry.set(ATTRIBUTES_NAME.NBT, nbttagcompound.get(ATTRIBUTES_NAME.NBT)); + entry.set(ATTRIBUTES_VALUE.NBT, nbttagcompound.get(ATTRIBUTES_VALUE.NBT)); + entry.set(ATTRIBUTES_TYPE.NBT, nbttagcompound.get(ATTRIBUTES_TYPE.NBT)); + save.add(entry); + } + + attributes = save; + } else { + attributes = null; + } + // Spigot start + if ( tag.hasKey( UNBREAKABLE.NBT ) ) + { + spigot.setUnbreakable( tag.getBoolean( UNBREAKABLE.NBT ) ); + } + // Spigot end + } + + static Map buildEnchantments(NBTTagCompound tag, ItemMetaKey key) { + if (!tag.hasKey(key.NBT)) { + return null; + } + + NBTTagList ench = tag.getList(key.NBT, 10); + Map enchantments = new HashMap(ench.size()); + + for (int i = 0; i < ench.size(); i++) { + int id = 0xffff & ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_ID.NBT); + int level = 0xffff & ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_LVL.NBT); + + // Spigot start - skip invalid enchantments + Enchantment e = Enchantment.getById(id); + if (e == null) continue; + // Spigot end + enchantments.put(e, level); + } + + return enchantments; + } + + CraftMetaItem(Map map) { + setDisplayName(SerializableMeta.getString(map, NAME.BUKKIT, true)); + + Iterable lore = SerializableMeta.getObject(Iterable.class, map, LORE.BUKKIT, true); + if (lore != null) { + safelyAdd(lore, this.lore = new ArrayList(), Integer.MAX_VALUE); + } + + enchantments = buildEnchantments(map, ENCHANTMENTS); + + Integer repairCost = SerializableMeta.getObject(Integer.class, map, REPAIR.BUKKIT, true); + if (repairCost != null) { + setRepairCost(repairCost); + } + + attributes = null; + // Spigot start + Boolean unbreakable = SerializableMeta.getObject( Boolean.class, map, UNBREAKABLE.BUKKIT, true ); + if ( unbreakable != null ) + { + spigot.setUnbreakable( unbreakable ); + } + // Spigot end + } + + static Map buildEnchantments(Map map, ItemMetaKey key) { + Map ench = SerializableMeta.getObject(Map.class, map, key.BUKKIT, true); + if (ench == null) { + return null; + } + + Map enchantments = new HashMap(ench.size()); + for (Map.Entry entry : ench.entrySet()) { + Enchantment enchantment = Enchantment.getByName(entry.getKey().toString()); + + if ((enchantment != null) && (entry.getValue() instanceof Integer)) { + enchantments.put(enchantment, (Integer) entry.getValue()); + } + } + + return enchantments; + } + + @Overridden + void applyToItem(NBTTagCompound itemTag) { + if (hasDisplayName()) { + setDisplayTag(itemTag, NAME.NBT, new NBTTagString(displayName)); + } + + if (hasLore()) { + setDisplayTag(itemTag, LORE.NBT, createStringList(lore)); + } + + applyEnchantments(enchantments, itemTag, ENCHANTMENTS); + + // Spigot start + if ( spigot.isUnbreakable() ) + { + itemTag.setBoolean( UNBREAKABLE.NBT, true ); + } + // Spigot end + + if (hasRepairCost()) { + itemTag.setInt(REPAIR.NBT, repairCost); + } + + if (attributes != null) { + itemTag.set(ATTRIBUTES.NBT, attributes); + } + } + + static NBTTagList createStringList(List list) { + if (list == null || list.isEmpty()) { + return null; + } + + NBTTagList tagList = new NBTTagList(); + for (String value : list) { + tagList.add(new NBTTagString(value)); + } + + return tagList; + } + + static void applyEnchantments(Map enchantments, NBTTagCompound tag, ItemMetaKey key) { + if (enchantments == null /*|| enchantments.size() == 0*/) { // Spigot - remove size check + return; + } + + NBTTagList list = new NBTTagList(); + + for (Map.Entry entry : enchantments.entrySet()) { + NBTTagCompound subtag = new NBTTagCompound(); + + subtag.setShort(ENCHANTMENTS_ID.NBT, (short) entry.getKey().getId()); + subtag.setShort(ENCHANTMENTS_LVL.NBT, entry.getValue().shortValue()); + + list.add(subtag); + } + + tag.set(key.NBT, list); + } + + void setDisplayTag(NBTTagCompound tag, String key, NBTBase value) { + final NBTTagCompound display = tag.getCompound(DISPLAY.NBT); + + if (!tag.hasKey(DISPLAY.NBT)) { + tag.set(DISPLAY.NBT, display); + } + + display.set(key, value); + } + + @Overridden + boolean applicableTo(Material type) { + return type != Material.AIR; + } + + @Overridden + boolean isEmpty() { + return !(hasDisplayName() || hasEnchants() || hasLore() || hasAttributes() || hasRepairCost() || spigot.isUnbreakable()); // Spigot + } + + public String getDisplayName() { + return displayName; + } + + public final void setDisplayName(String name) { + this.displayName = name; + } + + public boolean hasDisplayName() { + return !Strings.isNullOrEmpty(displayName); + } + + public boolean hasLore() { + return this.lore != null && !this.lore.isEmpty(); + } + + public boolean hasAttributes() { + return this.attributes != null; + } + + public boolean hasRepairCost() { + return repairCost > 0; + } + + public boolean hasEnchant(Enchantment ench) { + return hasEnchants() && enchantments.containsKey(ench); + } + + public int getEnchantLevel(Enchantment ench) { + Integer level = hasEnchants() ? enchantments.get(ench) : null; + if (level == null) { + return 0; + } + return level; + } + + public Map getEnchants() { + return hasEnchants() ? ImmutableMap.copyOf(enchantments) : ImmutableMap.of(); + } + + public boolean addEnchant(Enchantment ench, int level, boolean ignoreRestrictions) { + if (enchantments == null) { + enchantments = new HashMap(4); + } + + if (ignoreRestrictions || level >= ench.getStartLevel() && level <= ench.getMaxLevel()) { + Integer old = enchantments.put(ench, level); + return old == null || old != level; + } + return false; + } + + public boolean removeEnchant(Enchantment ench) { + // Spigot start + boolean b = hasEnchants() && enchantments.remove( ench ) != null; + if ( enchantments != null && enchantments.isEmpty() ) + { + this.enchantments = null; + } + return b; + // Spigot end + } + + public boolean hasEnchants() { + return !(enchantments == null || enchantments.isEmpty()); + } + + public boolean hasConflictingEnchant(Enchantment ench) { + return checkConflictingEnchants(enchantments, ench); + } + + @Override + public void addItemFlags(ItemFlag... itemFlags) { + + } + + @Override + public void removeItemFlags(ItemFlag... itemFlags) { + + } + + @Override + public Set getItemFlags() { + return Collections.emptySet(); + } + + @Override + public boolean hasItemFlag(ItemFlag flag) { + return false; + } + + public List getLore() { + return this.lore == null ? null : new ArrayList(this.lore); + } + + public void setLore(List lore) { // too tired to think if .clone is better + if (lore == null) { + this.lore = null; + } else { + if (this.lore == null) { + safelyAdd(lore, this.lore = new ArrayList(lore.size()), Integer.MAX_VALUE); + } else { + this.lore.clear(); + safelyAdd(lore, this.lore, Integer.MAX_VALUE); + } + } + } + + public int getRepairCost() { + return repairCost; + } + + public void setRepairCost(int cost) { // TODO: Does this have limits? + repairCost = cost; + } + + @Override + public final boolean equals(Object object) { + if (object == null) { + return false; + } + if (object == this) { + return true; + } + if (!(object instanceof CraftMetaItem)) { + return false; + } + return CraftItemFactory.instance().equals(this, (ItemMeta) object); + } + + /** + * This method is almost as weird as notUncommon. + * Only return false if your common internals are unequal. + * Checking your own internals is redundant if you are not common, as notUncommon is meant for checking those 'not common' variables. + */ + @Overridden + boolean equalsCommon(CraftMetaItem that) { + return ((this.hasDisplayName() ? that.hasDisplayName() && this.displayName.equals(that.displayName) : !that.hasDisplayName())) + && (this.hasEnchants() ? that.hasEnchants() && this.enchantments.equals(that.enchantments) : !that.hasEnchants()) + && (this.hasLore() ? that.hasLore() && this.lore.equals(that.lore) : !that.hasLore()) + && (this.hasAttributes() ? that.hasAttributes() && this.attributes.equals(that.attributes) : !that.hasAttributes()) + && (this.hasRepairCost() ? that.hasRepairCost() && this.repairCost == that.repairCost : !that.hasRepairCost()) && this.spigot.isUnbreakable() == that.spigot.isUnbreakable(); // Spigot + } + + /** + * This method is a bit weird... + * Return true if you are a common class OR your uncommon parts are empty. + * Empty uncommon parts implies the NBT data would be equivalent if both were applied to an item + */ + @Overridden + boolean notUncommon(CraftMetaItem meta) { + return true; + } + + @Override + public final int hashCode() { + return applyHash(); + } + + @Overridden + int applyHash() { + int hash = 3; + hash = 61 * hash + (hasDisplayName() ? this.displayName.hashCode() : 0); + hash = 61 * hash + (hasLore() ? this.lore.hashCode() : 0); + hash = 61 * hash + (hasEnchants() ? this.enchantments.hashCode() : 0); + hash = 61 * hash + (hasAttributes() ? this.attributes.hashCode() : 0); + hash = 61 * hash + (hasRepairCost() ? this.repairCost : 0); + hash = 61 * hash + (spigot.isUnbreakable() ? 1231 : 1237); // Spigot + return hash; + } + + @Overridden + @Override + public CraftMetaItem clone() { + try { + CraftMetaItem clone = (CraftMetaItem) super.clone(); + if (this.lore != null) { + clone.lore = new ArrayList(this.lore); + } + if (this.enchantments != null) { + clone.enchantments = new HashMap(this.enchantments); + } + return clone; + } catch (CloneNotSupportedException e) { + throw new Error(e); + } + } + + public final Map serialize() { + ImmutableMap.Builder map = ImmutableMap.builder(); + map.put(SerializableMeta.TYPE_FIELD, SerializableMeta.classMap.get(getClass())); + serialize(map); + return map.build(); + } + + @Overridden + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + if (hasDisplayName()) { + builder.put(NAME.BUKKIT, displayName); + } + + if (hasLore()) { + builder.put(LORE.BUKKIT, ImmutableList.copyOf(lore)); + } + + serializeEnchantments(enchantments, builder, ENCHANTMENTS); + + if (hasRepairCost()) { + builder.put(REPAIR.BUKKIT, repairCost); + } + + // Spigot start + if ( spigot.isUnbreakable() ) + { + builder.put( UNBREAKABLE.BUKKIT, true ); + } + // Spigot end + + return builder; + } + + static void serializeEnchantments(Map enchantments, ImmutableMap.Builder builder, ItemMetaKey key) { + if (enchantments == null || enchantments.isEmpty()) { + return; + } + + ImmutableMap.Builder enchants = ImmutableMap.builder(); + for (Map.Entry enchant : enchantments.entrySet()) { + enchants.put(enchant.getKey().getName(), enchant.getValue()); + } + + builder.put(key.BUKKIT, enchants.build()); + } + + static void safelyAdd(Iterable addFrom, Collection addTo, int maxItemLength) { + if (addFrom == null) { + return; + } + + for (Object object : addFrom) { + if (!(object instanceof String)) { + if (object != null) { + throw new IllegalArgumentException(addFrom + " cannot contain non-string " + object.getClass().getName()); + } + + addTo.add(""); + } else { + String page = object.toString(); + + if (page.length() > maxItemLength) { + page = page.substring(0, maxItemLength); + } + + addTo.add(page); + } + } + } + + static boolean checkConflictingEnchants(Map enchantments, Enchantment ench) { + if (enchantments == null || enchantments.isEmpty()) { + return false; + } + + for (Enchantment enchant : enchantments.keySet()) { + if (enchant.conflictsWith(ench)) { + return true; + } + } + + return false; + } + + @Override + public final String toString() { + return SerializableMeta.classMap.get(getClass()) + "_META:" + serialize(); // TODO: cry + } + + // Spigot start + private final Spigot spigot = new Spigot() + { + private boolean unbreakable; + + @Override + public void setUnbreakable(boolean setUnbreakable) + { + unbreakable = setUnbreakable; + } + + @Override + public boolean isUnbreakable() + { + return unbreakable; + } + }; + + @Override + public Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java new file mode 100644 index 0000000..5478e9b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java @@ -0,0 +1,135 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.bukkit.craftbukkit.inventory.CraftItemFactory.DEFAULT_LEATHER_COLOR; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagInt; + +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; + +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta { + static final ItemMetaKey COLOR = new ItemMetaKey("color"); + + private Color color = DEFAULT_LEATHER_COLOR; + + CraftMetaLeatherArmor(CraftMetaItem meta) { + super(meta); + if (!(meta instanceof CraftMetaLeatherArmor)) { + return; + } + + CraftMetaLeatherArmor armorMeta = (CraftMetaLeatherArmor) meta; + this.color = armorMeta.color; + } + + CraftMetaLeatherArmor(NBTTagCompound tag) { + super(tag); + if (tag.hasKey(DISPLAY.NBT)) { + NBTTagCompound display = tag.getCompound(DISPLAY.NBT); + if (display.hasKey(COLOR.NBT)) { + color = Color.fromRGB(display.getInt(COLOR.NBT)); + } + } + } + + CraftMetaLeatherArmor(Map map) { + super(map); + setColor(SerializableMeta.getObject(Color.class, map, COLOR.BUKKIT, true)); + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + if (hasColor()) { + setDisplayTag(itemTag, COLOR.NBT, new NBTTagInt(color.asRGB())); + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isLeatherArmorEmpty(); + } + + boolean isLeatherArmorEmpty() { + return !(hasColor()); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case LEATHER_HELMET: + case LEATHER_CHESTPLATE: + case LEATHER_LEGGINGS: + case LEATHER_BOOTS: + return true; + default: + return false; + } + } + + @Override + public CraftMetaLeatherArmor clone() { + return (CraftMetaLeatherArmor) super.clone(); + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color == null ? DEFAULT_LEATHER_COLOR : color; + } + + boolean hasColor() { + return !DEFAULT_LEATHER_COLOR.equals(color); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasColor()) { + builder.put(COLOR.BUKKIT, color); + } + + return builder; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaLeatherArmor) { + CraftMetaLeatherArmor that = (CraftMetaLeatherArmor) meta; + + return color.equals(that.color); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaLeatherArmor || isLeatherArmorEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasColor()) { + hash ^= color.hashCode(); + } + return original != hash ? CraftMetaSkull.class.hashCode() ^ hash : hash; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java new file mode 100644 index 0000000..d8af390 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java @@ -0,0 +1,135 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.MapMeta; + +import com.google.common.collect.ImmutableMap; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaMap extends CraftMetaItem implements MapMeta { + static final ItemMetaKey MAP_SCALING = new ItemMetaKey("map_is_scaling", "scaling"); + static final byte SCALING_EMPTY = (byte) 0; + static final byte SCALING_TRUE = (byte) 1; + static final byte SCALING_FALSE = (byte) 2; + + private byte scaling = SCALING_EMPTY; + + CraftMetaMap(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaMap)) { + return; + } + + CraftMetaMap map = (CraftMetaMap) meta; + this.scaling = map.scaling; + } + + CraftMetaMap(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(MAP_SCALING.NBT)) { + this.scaling = tag.getBoolean(MAP_SCALING.NBT) ? SCALING_TRUE : SCALING_FALSE; + } + } + + CraftMetaMap(Map map) { + super(map); + + Boolean scaling = SerializableMeta.getObject(Boolean.class, map, MAP_SCALING.BUKKIT, true); + if (scaling != null) { + setScaling(scaling); + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + if (hasScaling()) { + tag.setBoolean(MAP_SCALING.NBT, isScaling()); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case MAP: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isMapEmpty(); + } + + boolean isMapEmpty() { + return !hasScaling(); + } + + boolean hasScaling() { + return scaling != SCALING_EMPTY; + } + + public boolean isScaling() { + return scaling == SCALING_TRUE; + } + + public void setScaling(boolean scaling) { + this.scaling = scaling ? SCALING_TRUE : SCALING_FALSE; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaMap) { + CraftMetaMap that = (CraftMetaMap) meta; + + return (this.scaling == that.scaling); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaMap || isMapEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasScaling()) { + hash ^= 0x22222222 << (isScaling() ? 1 : -1); + } + + return original != hash ? CraftMetaMap.class.hashCode() ^ hash : hash; + } + + public CraftMetaMap clone() { + return (CraftMetaMap) super.clone(); + } + + @Override + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + super.serialize(builder); + + if (hasScaling()) { + builder.put(MAP_SCALING.BUKKIT, isScaling()); + } + + return builder; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java new file mode 100644 index 0000000..cebb941 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java @@ -0,0 +1,255 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaPotion extends CraftMetaItem implements PotionMeta { + static final ItemMetaKey AMPLIFIER = new ItemMetaKey("Amplifier", "amplifier"); + static final ItemMetaKey AMBIENT = new ItemMetaKey("Ambient", "ambient"); + static final ItemMetaKey DURATION = new ItemMetaKey("Duration", "duration"); + static final ItemMetaKey POTION_EFFECTS = new ItemMetaKey("CustomPotionEffects", "custom-effects"); + static final ItemMetaKey ID = new ItemMetaKey("Id", "potion-id"); + + private List customEffects; + + CraftMetaPotion(CraftMetaItem meta) { + super(meta); + if (!(meta instanceof CraftMetaPotion)) { + return; + } + CraftMetaPotion potionMeta = (CraftMetaPotion) meta; + if (potionMeta.hasCustomEffects()) { + this.customEffects = new ArrayList(potionMeta.customEffects); + } + } + + CraftMetaPotion(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(POTION_EFFECTS.NBT)) { + NBTTagList list = tag.getList(POTION_EFFECTS.NBT, 10); + int length = list.size(); + if (length > 0) { + customEffects = new ArrayList(length); + + for (int i = 0; i < length; i++) { + NBTTagCompound effect = list.get(i); + PotionEffectType type = PotionEffectType.getById(effect.getByte(ID.NBT)); + int amp = effect.getByte(AMPLIFIER.NBT); + int duration = effect.getInt(DURATION.NBT); + boolean ambient = effect.getBoolean(AMBIENT.NBT); + customEffects.add(new PotionEffect(type, duration, amp, ambient)); + } + } + } + } + + CraftMetaPotion(Map map) { + super(map); + + Iterable rawEffectList = SerializableMeta.getObject(Iterable.class, map, POTION_EFFECTS.BUKKIT, true); + if (rawEffectList == null) { + return; + } + + for (Object obj : rawEffectList) { + if (!(obj instanceof PotionEffect)) { + throw new IllegalArgumentException("Object in effect list is not valid. " + obj.getClass()); + } + addCustomEffect((PotionEffect) obj, true); + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + if (hasCustomEffects()) { + NBTTagList effectList = new NBTTagList(); + tag.set(POTION_EFFECTS.NBT, effectList); + + for (PotionEffect effect : customEffects) { + NBTTagCompound effectData = new NBTTagCompound(); + effectData.setByte(ID.NBT, (byte) effect.getType().getId()); + effectData.setByte(AMPLIFIER.NBT, (byte) effect.getAmplifier()); + effectData.setInt(DURATION.NBT, effect.getDuration()); + effectData.setBoolean(AMBIENT.NBT, effect.isAmbient()); + effectList.add(effectData); + } + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isPotionEmpty(); + } + + boolean isPotionEmpty() { + return !(hasCustomEffects()); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case POTION: + return true; + default: + return false; + } + } + + @Override + public CraftMetaPotion clone() { + CraftMetaPotion clone = (CraftMetaPotion) super.clone(); + if (this.customEffects != null) { + clone.customEffects = new ArrayList(this.customEffects); + } + return clone; + } + + public boolean hasCustomEffects() { + return !(customEffects == null || customEffects.isEmpty()); + } + + public List getCustomEffects() { + if (hasCustomEffects()) { + return ImmutableList.copyOf(customEffects); + } + return ImmutableList.of(); + } + + public boolean addCustomEffect(PotionEffect effect, boolean overwrite) { + Validate.notNull(effect, "Potion effect must not be null"); + + int index = indexOfEffect(effect.getType()); + if (index != -1) { + if (overwrite) { + PotionEffect old = customEffects.get(index); + if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient()) { + return false; + } + customEffects.set(index, effect); + return true; + } else { + return false; + } + } else { + if (customEffects == null) { + customEffects = new ArrayList(); + } + customEffects.add(effect); + return true; + } + } + + public boolean removeCustomEffect(PotionEffectType type) { + Validate.notNull(type, "Potion effect type must not be null"); + + if (!hasCustomEffects()) { + return false; + } + + boolean changed = false; + Iterator iterator = customEffects.iterator(); + while (iterator.hasNext()) { + PotionEffect effect = iterator.next(); + if (effect.getType() == type) { + iterator.remove(); + changed = true; + } + } + return changed; + } + + public boolean hasCustomEffect(PotionEffectType type) { + Validate.notNull(type, "Potion effect type must not be null"); + return indexOfEffect(type) != -1; + } + + public boolean setMainEffect(PotionEffectType type) { + Validate.notNull(type, "Potion effect type must not be null"); + int index = indexOfEffect(type); + if (index == -1 || index == 0) { + return false; + } + + PotionEffect old = customEffects.get(0); + customEffects.set(0, customEffects.get(index)); + customEffects.set(index, old); + return true; + } + + private int indexOfEffect(PotionEffectType type) { + if (!hasCustomEffects()) { + return -1; + } + + for (int i = 0; i < customEffects.size(); i++) { + if (customEffects.get(i).getType().equals(type)) { + return i; + } + } + return -1; + } + + public boolean clearCustomEffects() { + boolean changed = hasCustomEffects(); + customEffects = null; + return changed; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasCustomEffects()) { + hash = 73 * hash + customEffects.hashCode(); + } + return original != hash ? CraftMetaPotion.class.hashCode() ^ hash : hash; + } + + @Override + public boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaPotion) { + CraftMetaPotion that = (CraftMetaPotion) meta; + + return (this.hasCustomEffects() ? that.hasCustomEffects() && this.customEffects.equals(that.customEffects) : !that.hasCustomEffects()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaPotion || isPotionEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasCustomEffects()) { + builder.put(POTION_EFFECTS.BUKKIT, ImmutableList.copyOf(this.customEffects)); + } + + return builder; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java new file mode 100644 index 0000000..b460fd9 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java @@ -0,0 +1,169 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.GameProfileSerializer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import com.google.common.collect.ImmutableMap.Builder; + +import net.valorhcf.ThreadingManager; // Poweruser + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaSkull extends CraftMetaItem implements SkullMeta { + static final ItemMetaKey SKULL_OWNER = new ItemMetaKey("SkullOwner", "skull-owner"); + static final int MAX_OWNER_LENGTH = 16; + + private GameProfile profile; + + CraftMetaSkull(CraftMetaItem meta) { + super(meta); + if (!(meta instanceof CraftMetaSkull)) { + return; + } + CraftMetaSkull skullMeta = (CraftMetaSkull) meta; + this.profile = skullMeta.profile; + } + + CraftMetaSkull(NBTTagCompound tag) { + super(tag); + + if (tag.hasKeyOfType(SKULL_OWNER.NBT, 10)) { + profile = GameProfileSerializer.deserialize(tag.getCompound(SKULL_OWNER.NBT)); + } else if (tag.hasKeyOfType(SKULL_OWNER.NBT, 8)) { + profile = new GameProfile(null, tag.getString(SKULL_OWNER.NBT)); + } + } + + CraftMetaSkull(Map map) { + super(map); + setOwner(SerializableMeta.getString(map, SKULL_OWNER.BUKKIT, true)); + } + + @Override + void applyToItem(final NBTTagCompound tag) { // Spigot - make final + super.applyToItem(tag); + + if (hasOwner()) { + NBTTagCompound owner = new NBTTagCompound(); + GameProfileSerializer.serialize(owner, profile); + tag.set( SKULL_OWNER.NBT, owner ); + // Spigot start - do an async lookup of the profile. + // Unfortunately there is not way to refresh the holding + // inventory, so that responsibility is left to the user. + ThreadingManager.queueHeadConversion(new Runnable() // Poweruser + { + @Override + public void run() + { + + final GameProfile profile = net.minecraft.server.TileEntitySkull.skinCache.getUnchecked( CraftMetaSkull.this.profile.getName().toLowerCase() ); + if ( profile != null ) + { + MinecraftServer.getServer().processQueue.add( new Runnable() + { + @Override + public void run() + { + NBTTagCompound owner = new NBTTagCompound(); + GameProfileSerializer.serialize( owner, profile ); + tag.set( SKULL_OWNER.NBT, owner ); + } + } ); + } + } + } ); + // Spigot end + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isSkullEmpty(); + } + + boolean isSkullEmpty() { + return !(hasOwner()); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case SKULL_ITEM: + return true; + default: + return false; + } + } + + @Override + public CraftMetaSkull clone() { + return (CraftMetaSkull) super.clone(); + } + + public boolean hasOwner() { + return profile != null; + } + + public String getOwner() { + return hasOwner() ? profile.getName() : null; + } + + public boolean setOwner(String name) { + if (name != null && name.length() > MAX_OWNER_LENGTH) { + return false; + } + + if (name == null) { + profile = null; + } else { + profile = new GameProfile(null, name); + } + + return true; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasOwner()) { + hash = 61 * hash + profile.hashCode(); + } + return original != hash ? CraftMetaSkull.class.hashCode() ^ hash : hash; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaSkull) { + CraftMetaSkull that = (CraftMetaSkull) meta; + + return (this.hasOwner() ? that.hasOwner() && this.profile.equals(that.profile) : !that.hasOwner()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaSkull || isSkullEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + if (hasOwner()) { + return builder.put(SKULL_OWNER.BUKKIT, this.profile.getName()); + } + return builder; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java new file mode 100644 index 0000000..d3e03e2 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java @@ -0,0 +1,7 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.inventory.Recipe; + +public interface CraftRecipe extends Recipe { + void addToCraftingManager(); +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java new file mode 100644 index 0000000..baea759 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java @@ -0,0 +1,65 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.CraftingManager; +import net.minecraft.server.ShapedRecipes; + +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; + +public class CraftShapedRecipe extends ShapedRecipe implements CraftRecipe { + // TODO: Could eventually use this to add a matches() method or some such + private ShapedRecipes recipe; + + public CraftShapedRecipe(ItemStack result) { + super(result); + } + + public CraftShapedRecipe(ItemStack result, ShapedRecipes recipe) { + this(result); + this.recipe = recipe; + } + + public static CraftShapedRecipe fromBukkitRecipe(ShapedRecipe recipe) { + if (recipe instanceof CraftShapedRecipe) { + return (CraftShapedRecipe) recipe; + } + CraftShapedRecipe ret = new CraftShapedRecipe(recipe.getResult()); + String[] shape = recipe.getShape(); + ret.shape(shape); + Map ingredientMap = recipe.getIngredientMap(); + for (char c : ingredientMap.keySet()) { + ItemStack stack = ingredientMap.get(c); + if (stack != null) { + ret.setIngredient(c, stack.getType(), stack.getDurability()); + } + } + return ret; + } + + public void addToCraftingManager() { + Object[] data; + String[] shape = this.getShape(); + Map ingred = this.getIngredientMap(); + int datalen = shape.length; + datalen += ingred.size() * 2; + int i = 0; + data = new Object[datalen]; + for (; i < shape.length; i++) { + data[i] = shape[i]; + } + for (char c : ingred.keySet()) { + ItemStack mdata = ingred.get(c); + if (mdata == null) continue; + data[i] = c; + i++; + int id = mdata.getTypeId(); + short dmg = mdata.getDurability(); + data[i] = new net.minecraft.server.ItemStack(CraftMagicNumbers.getItem(id), 1, dmg); + i++; + } + CraftingManager.getInstance().registerShapedRecipe(CraftItemStack.asNMSCopy(this.getResult()), data); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java new file mode 100644 index 0000000..53479c7 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.List; + +import net.minecraft.server.CraftingManager; +import net.minecraft.server.ShapelessRecipes; + +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapelessRecipe; + +public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe { + // TODO: Could eventually use this to add a matches() method or some such + private ShapelessRecipes recipe; + + public CraftShapelessRecipe(ItemStack result) { + super(result); + } + + public CraftShapelessRecipe(ItemStack result, ShapelessRecipes recipe) { + this(result); + this.recipe = recipe; + } + + public static CraftShapelessRecipe fromBukkitRecipe(ShapelessRecipe recipe) { + if (recipe instanceof CraftShapelessRecipe) { + return (CraftShapelessRecipe) recipe; + } + CraftShapelessRecipe ret = new CraftShapelessRecipe(recipe.getResult()); + for (ItemStack ingred : recipe.getIngredientList()) { + ret.addIngredient(ingred.getType(), ingred.getDurability()); + } + return ret; + } + + public void addToCraftingManager() { + List ingred = this.getIngredientList(); + Object[] data = new Object[ingred.size()]; + int i = 0; + for (ItemStack mdata : ingred) { + int id = mdata.getTypeId(); + short dmg = mdata.getDurability(); + data[i] = new net.minecraft.server.ItemStack(CraftMagicNumbers.getItem(id), 1, dmg); + i++; + } + CraftingManager.getInstance().registerShapelessRecipe(CraftItemStack.asNMSCopy(this.getResult()), data); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/InventoryIterator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/InventoryIterator.java new file mode 100644 index 0000000..e3b5f42 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/InventoryIterator.java @@ -0,0 +1,64 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ListIterator; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class InventoryIterator implements ListIterator { + private final Inventory inventory; + private int nextIndex; + private Boolean lastDirection; // true = forward, false = backward, null = haven't moved yet + + InventoryIterator(Inventory craftInventory) { + this.inventory = craftInventory; + this.nextIndex = 0; + } + + InventoryIterator(Inventory craftInventory, int index) { + this.inventory = craftInventory; + this.nextIndex = index; + } + + public boolean hasNext() { + return nextIndex < inventory.getSize(); + } + + public ItemStack next() { + lastDirection = true; + return inventory.getItem(nextIndex++); + } + + public int nextIndex() { + return nextIndex; + } + + public boolean hasPrevious() { + return nextIndex > 0; + } + + public ItemStack previous() { + lastDirection = false; + return inventory.getItem(--nextIndex); + } + + public int previousIndex() { + return nextIndex - 1; + } + + public void set(ItemStack item) { + if (lastDirection == null) { + throw new IllegalStateException("No current item!"); + } + int i = lastDirection ? nextIndex - 1 : nextIndex; + inventory.setItem(i, item); + } + + public void add(ItemStack item) { + throw new UnsupportedOperationException("Can't change the size of an inventory!"); + } + + public void remove() { + throw new UnsupportedOperationException("Can't change the size of an inventory!"); + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java new file mode 100644 index 0000000..53b53b7 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java @@ -0,0 +1,53 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Iterator; + +import org.bukkit.inventory.Recipe; + +import net.minecraft.server.CraftingManager; +import net.minecraft.server.IRecipe; +import net.minecraft.server.RecipesFurnace; + +public class RecipeIterator implements Iterator { + private final Iterator recipes; + private final Iterator smeltingCustom; + private final Iterator smeltingVanilla; + private Iterator removeFrom = null; + + public RecipeIterator() { + this.recipes = CraftingManager.getInstance().getRecipes().iterator(); + this.smeltingCustom = RecipesFurnace.getInstance().customRecipes.keySet().iterator(); + this.smeltingVanilla = RecipesFurnace.getInstance().recipes.keySet().iterator(); + } + + public boolean hasNext() { + return recipes.hasNext() || smeltingCustom.hasNext() || smeltingVanilla.hasNext(); + } + + public Recipe next() { + if (recipes.hasNext()) { + removeFrom = recipes; + return recipes.next().toBukkitRecipe(); + } else { + net.minecraft.server.ItemStack item; + if (smeltingCustom.hasNext()) { + removeFrom = smeltingCustom; + item = smeltingCustom.next(); + } else { + removeFrom = smeltingVanilla; + item = smeltingVanilla.next(); + } + + CraftItemStack stack = CraftItemStack.asCraftMirror(RecipesFurnace.getInstance().getResult(item)); + + return new CraftFurnaceRecipe(stack, CraftItemStack.asCraftMirror(item)); + } + } + + public void remove() { + if (removeFrom == null) { + throw new IllegalStateException(); + } + removeFrom.remove(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java new file mode 100644 index 0000000..b5e9e31 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java @@ -0,0 +1,110 @@ +package org.bukkit.craftbukkit.map; + +import java.awt.Image; +import java.util.Arrays; +import org.bukkit.map.MapCanvas; +import org.bukkit.map.MapCursorCollection; +import org.bukkit.map.MapFont; +import org.bukkit.map.MapFont.CharacterSprite; +import org.bukkit.map.MapPalette; + +public class CraftMapCanvas implements MapCanvas { + + private final byte[] buffer = new byte[128 * 128]; + private final CraftMapView mapView; + private byte[] base; + private MapCursorCollection cursors = new MapCursorCollection(); + + protected CraftMapCanvas(CraftMapView mapView) { + this.mapView = mapView; + Arrays.fill(buffer, (byte) -1); + } + + public CraftMapView getMapView() { + return mapView; + } + + public MapCursorCollection getCursors() { + return cursors; + } + + public void setCursors(MapCursorCollection cursors) { + this.cursors = cursors; + } + + public void setPixel(int x, int y, byte color) { + if (x < 0 || y < 0 || x >= 128 || y >= 128) + return; + if (buffer[y * 128 + x] != color) { + buffer[y * 128 + x] = color; + mapView.worldMap.flagDirty(x, y, y); + } + } + + public byte getPixel(int x, int y) { + if (x < 0 || y < 0 || x >= 128 || y >= 128) + return 0; + return buffer[y * 128 + x]; + } + + public byte getBasePixel(int x, int y) { + if (x < 0 || y < 0 || x >= 128 || y >= 128) + return 0; + return base[y * 128 + x]; + } + + protected void setBase(byte[] base) { + this.base = base; + } + + protected byte[] getBuffer() { + return buffer; + } + + public void drawImage(int x, int y, Image image) { + byte[] bytes = MapPalette.imageToBytes(image); + for (int x2 = 0; x2 < image.getWidth(null); ++x2) { + for (int y2 = 0; y2 < image.getHeight(null); ++y2) { + setPixel(x + x2, y + y2, bytes[y2 * image.getWidth(null) + x2]); + } + } + } + + public void drawText(int x, int y, MapFont font, String text) { + int xStart = x; + byte color = MapPalette.DARK_GRAY; + if (!font.isValid(text)) { + throw new IllegalArgumentException("text contains invalid characters"); + } + + for (int i = 0; i < text.length(); ++i) { + char ch = text.charAt(i); + if (ch == '\n') { + x = xStart; + y += font.getHeight() + 1; + continue; + } else if (ch == '\u00A7') { + int j = text.indexOf(';', i); + if (j >= 0) { + try { + color = Byte.parseByte(text.substring(i + 1, j)); + i = j; + continue; + } + catch (NumberFormatException ex) {} + } + } + + CharacterSprite sprite = font.getChar(text.charAt(i)); + for (int r = 0; r < font.getHeight(); ++r) { + for (int c = 0; c < sprite.getWidth(); ++c) { + if (sprite.get(r, c)) { + setPixel(x + c, y + r, color); + } + } + } + x += sprite.getWidth() + 1; + } + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java new file mode 100644 index 0000000..392dba4 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java @@ -0,0 +1,49 @@ +package org.bukkit.craftbukkit.map; + +import net.minecraft.server.WorldMap; +import net.minecraft.server.WorldMapDecoration; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.map.MapCanvas; +import org.bukkit.map.MapCursorCollection; +import org.bukkit.map.MapRenderer; +import org.bukkit.map.MapView; + +public class CraftMapRenderer extends MapRenderer { + + private final WorldMap worldMap; + + public CraftMapRenderer(CraftMapView mapView, WorldMap worldMap) { + super(false); + this.worldMap = worldMap; + } + + @Override + public void render(MapView map, MapCanvas canvas, Player player) { + // Map + for (int x = 0; x < 128; ++x) { + for (int y = 0; y < 128; ++y) { + canvas.setPixel(x, y, worldMap.colors[y * 128 + x]); + } + } + + // Cursors + MapCursorCollection cursors = canvas.getCursors(); + while (cursors.size() > 0) { + cursors.removeCursor(cursors.getCursor(0)); + } + + for (Object key : worldMap.decorations.keySet()) { + // If this cursor is for a player check visibility with vanish system + Player other = Bukkit.getPlayerExact((String) key); + if (other != null && !player.canSee(other)) { + continue; + } + + WorldMapDecoration decoration = (WorldMapDecoration) worldMap.decorations.get(key); + cursors.addCursor(decoration.locX, decoration.locY, (byte) (decoration.rotation & 15), decoration.type); + } + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapView.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapView.java new file mode 100644 index 0000000..c9f0027 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapView.java @@ -0,0 +1,163 @@ +package org.bukkit.craftbukkit.map; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.WorldMap; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.map.MapRenderer; +import org.bukkit.map.MapView; + +public final class CraftMapView implements MapView { + + private final Map renderCache = new HashMap(); + public final List renderers = new ArrayList(); // Spigot + private final Map> canvases = new HashMap>(); + protected final WorldMap worldMap; + + public CraftMapView(WorldMap worldMap) { + this.worldMap = worldMap; + addRenderer(new CraftMapRenderer(this, worldMap)); + } + + public short getId() { + String text = worldMap.id; + if (text.startsWith("map_")) { + try { + return Short.parseShort(text.substring("map_".length())); + } + catch (NumberFormatException ex) { + throw new IllegalStateException("Map has non-numeric ID"); + } + } else { + throw new IllegalStateException("Map has invalid ID"); + } + } + + public boolean isVirtual() { + return renderers.size() > 0 && !(renderers.get(0) instanceof CraftMapRenderer); + } + + public Scale getScale() { + return Scale.valueOf(worldMap.scale); + } + + public void setScale(Scale scale) { + worldMap.scale = scale.getValue(); + } + + public World getWorld() { + byte dimension = worldMap.map; + for (World world : Bukkit.getServer().getWorlds()) { + if (((CraftWorld) world).getHandle().dimension == dimension) { + return world; + } + } + return null; + } + + public void setWorld(World world) { + worldMap.map = (byte) ((CraftWorld) world).getHandle().dimension; + } + + public int getCenterX() { + return worldMap.centerX; + } + + public int getCenterZ() { + return worldMap.centerZ; + } + + public void setCenterX(int x) { + worldMap.centerX = x; + } + + public void setCenterZ(int z) { + worldMap.centerZ = z; + } + + public List getRenderers() { + return new ArrayList(renderers); + } + + public void addRenderer(MapRenderer renderer) { + if (!renderers.contains(renderer)) { + renderers.add(renderer); + canvases.put(renderer, new HashMap()); + renderer.initialize(this); + } + } + + public boolean removeRenderer(MapRenderer renderer) { + if (renderers.contains(renderer)) { + renderers.remove(renderer); + for (Map.Entry entry : canvases.get(renderer).entrySet()) { + for (int x = 0; x < 128; ++x) { + for (int y = 0; y < 128; ++y) { + entry.getValue().setPixel(x, y, (byte) -1); + } + } + } + canvases.remove(renderer); + return true; + } else { + return false; + } + } + + private boolean isContextual() { + for (MapRenderer renderer : renderers) { + if (renderer.isContextual()) return true; + } + return false; + } + + public RenderData render(CraftPlayer player) { + boolean context = isContextual(); + RenderData render = renderCache.get(context ? player : null); + + if (render == null) { + render = new RenderData(); + renderCache.put(context ? player : null, render); + } + + if (context && renderCache.containsKey(null)) { + renderCache.remove(null); + } + + Arrays.fill(render.buffer, (byte) 0); + render.cursors.clear(); + + for (MapRenderer renderer : renderers) { + CraftMapCanvas canvas = canvases.get(renderer).get(renderer.isContextual() ? player : null); + if (canvas == null) { + canvas = new CraftMapCanvas(this); + canvases.get(renderer).put(renderer.isContextual() ? player : null, canvas); + } + + canvas.setBase(render.buffer); + renderer.render(this, canvas, player); + + byte[] buf = canvas.getBuffer(); + for (int i = 0; i < buf.length; ++i) { + byte color = buf[i]; + // There are 143 valid color id's, 0 -> 127 and -128 -> -113 + if (color >= 0 || color <= -113) render.buffer[i] = color; + } + + for (int i = 0; i < canvas.getCursors().size(); ++i) { + render.cursors.add(canvas.getCursors().getCursor(i)); + } + } + + return render; + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/RenderData.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/RenderData.java new file mode 100644 index 0000000..256a131 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/map/RenderData.java @@ -0,0 +1,16 @@ +package org.bukkit.craftbukkit.map; + +import java.util.ArrayList; +import org.bukkit.map.MapCursor; + +public class RenderData { + + public final byte[] buffer; + public final ArrayList cursors; + + public RenderData() { + this.buffer = new byte[128 * 128]; + this.cursors = new ArrayList(); + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/BlockMetadataStore.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/BlockMetadataStore.java new file mode 100644 index 0000000..6f7102f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/BlockMetadataStore.java @@ -0,0 +1,94 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; + +import java.util.List; + +/** + * A BlockMetadataStore stores metadata values for {@link Block} objects. + */ +public class BlockMetadataStore extends MetadataStoreBase implements MetadataStore { + + private final World owningWorld; + + /** + * Initializes a BlockMetadataStore. + * @param owningWorld The world to which this BlockMetadataStore belongs. + */ + public BlockMetadataStore(World owningWorld) { + this.owningWorld = owningWorld; + } + + /** + * Generates a unique metadata key for a {@link Block} object based on its coordinates in the world. + * @see MetadataStoreBase#disambiguate(Object, String) + * @param block the block + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(Block block, String metadataKey) { + return Integer.toString(block.getX()) + ":" + Integer.toString(block.getY()) + ":" + Integer.toString(block.getZ()) + ":" + metadataKey; + } + + /** + * Retrieves the metadata for a {@link Block}, ensuring the block being asked for actually belongs to this BlockMetadataStore's + * owning world. + * @see MetadataStoreBase#getMetadata(Object, String) + */ + @Override + public List getMetadata(Block block, String metadataKey) { + if(block.getWorld() == owningWorld) { + return super.getMetadata(block, metadataKey); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } + + /** + * Tests to see if a metadata value has been added to a {@link Block}, ensuring the block being interrogated belongs + * to this BlockMetadataStore's owning world. + * @see MetadataStoreBase#hasMetadata(Object, String) + */ + @Override + public boolean hasMetadata(Block block, String metadataKey) { + if(block.getWorld() == owningWorld) { + return super.hasMetadata(block, metadataKey); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } + + /** + * Removes metadata from from a {@link Block} belonging to a given {@link Plugin}, ensuring the block being deleted from belongs + * to this BlockMetadataStore's owning world. + * @see MetadataStoreBase#removeMetadata(Object, String, org.bukkit.plugin.Plugin) + */ + @Override + public void removeMetadata(Block block, String metadataKey, Plugin owningPlugin) { + if(block.getWorld() == owningWorld) { + super.removeMetadata(block, metadataKey, owningPlugin); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } + + /** + * Sets or overwrites a metadata value on a {@link Block} from a given {@link Plugin}, ensuring the target block belongs + * to this BlockMetadataStore's owning world. + * @see MetadataStoreBase#setMetadata(Object, String, org.bukkit.metadata.MetadataValue) + */ + @Override + public void setMetadata(Block block, String metadataKey, MetadataValue newMetadataValue) { + if(block.getWorld() == owningWorld) { + super.setMetadata(block, metadataKey, newMetadataValue); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/EntityMetadataStore.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/EntityMetadataStore.java new file mode 100644 index 0000000..35c484f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/EntityMetadataStore.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.entity.Entity; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; + +/** + * An EntityMetadataStore stores metadata values for all {@link Entity} classes an their descendants. + */ +public class EntityMetadataStore extends MetadataStoreBase implements MetadataStore { + /** + * Generates a unique metadata key for an {@link Entity} UUID. + * + * @see MetadataStoreBase#disambiguate(Object, String) + * @param entity the entity + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(Entity entity, String metadataKey) { + return entity.getUniqueId().toString() + ":" + metadataKey; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/PlayerMetadataStore.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/PlayerMetadataStore.java new file mode 100644 index 0000000..d4d7f38 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/PlayerMetadataStore.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.OfflinePlayer; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; + +/** + * A PlayerMetadataStore stores metadata for {@link org.bukkit.entity.Player} and {@link OfflinePlayer} objects. + */ +public class PlayerMetadataStore extends MetadataStoreBase implements MetadataStore { + /** + * Generates a unique metadata key for {@link org.bukkit.entity.Player} and {@link OfflinePlayer} using the player + * name. + * @see MetadataStoreBase#disambiguate(Object, String) + * @param player the player + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(OfflinePlayer player, String metadataKey) { + return player.getName().toLowerCase() + ":" + metadataKey; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/WorldMetadataStore.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/WorldMetadataStore.java new file mode 100644 index 0000000..dd37ed2 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/metadata/WorldMetadataStore.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.World; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; + +/** + * An WorldMetadataStore stores metadata values for {@link World} objects. + */ +public class WorldMetadataStore extends MetadataStoreBase implements MetadataStore { + /** + * Generates a unique metadata key for a {@link World} object based on the world UID. + * @see WorldMetadataStore#disambiguate(Object, String) + * @param world the world + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(World world, String metadataKey) { + return world.getUID().toString() + ":" + metadataKey; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java new file mode 100644 index 0000000..bb16958 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java @@ -0,0 +1,47 @@ +package org.bukkit.craftbukkit.potion; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.MobEffect; + +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionBrewer; +import org.bukkit.potion.PotionEffect; + +import com.google.common.collect.Maps; + +public class CraftPotionBrewer implements PotionBrewer { + private static final Map> cache = Maps.newHashMap(); + + public Collection getEffectsFromDamage(int damage) { + if (cache.containsKey(damage)) + return cache.get(damage); + + List mcEffects = net.minecraft.server.PotionBrewer.getEffects(damage, false); + List effects = new ArrayList(); + if (mcEffects == null) + return effects; + + for (Object raw : mcEffects) { + if (raw == null || !(raw instanceof MobEffect)) + continue; + MobEffect mcEffect = (MobEffect) raw; + PotionEffect effect = new PotionEffect(PotionEffectType.getById(mcEffect.getEffectId()), + mcEffect.getDuration(), mcEffect.getAmplifier()); + // Minecraft PotionBrewer applies duration modifiers automatically. + effects.add(effect); + } + + cache.put(damage, effects); + + return effects; + } + + public PotionEffect createEffect(PotionEffectType potion, int duration, int amplifier) { + return new PotionEffect(potion, potion.isInstant() ? 1 : (int) (duration * potion.getDurationModifier()), + amplifier); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java new file mode 100644 index 0000000..b59d142 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java @@ -0,0 +1,82 @@ +package org.bukkit.craftbukkit.potion; + +import net.minecraft.server.MobEffectList; + +import org.bukkit.potion.PotionEffectType; + +public class CraftPotionEffectType extends PotionEffectType { + private final MobEffectList handle; + + public CraftPotionEffectType(MobEffectList handle) { + super(handle.id); + this.handle = handle; + } + + @Override + public double getDurationModifier() { + return handle.getDurationModifier(); + } + + public MobEffectList getHandle() { + return handle; + } + + @Override + public String getName() { + switch (handle.id) { + case 1: + return "SPEED"; + case 2: + return "SLOW"; + case 3: + return "FAST_DIGGING"; + case 4: + return "SLOW_DIGGING"; + case 5: + return "INCREASE_DAMAGE"; + case 6: + return "HEAL"; + case 7: + return "HARM"; + case 8: + return "JUMP"; + case 9: + return "CONFUSION"; + case 10: + return "REGENERATION"; + case 11: + return "DAMAGE_RESISTANCE"; + case 12: + return "FIRE_RESISTANCE"; + case 13: + return "WATER_BREATHING"; + case 14: + return "INVISIBILITY"; + case 15: + return "BLINDNESS"; + case 16: + return "NIGHT_VISION"; + case 17: + return "HUNGER"; + case 18: + return "WEAKNESS"; + case 19: + return "POISON"; + case 20: + return "WITHER"; + case 21: + return "HEALTH_BOOST"; + case 22: + return "ABSORPTION"; + case 23: + return "SATURATION"; + default: + return "UNKNOWN_EFFECT_TYPE_" + handle.id; + } + } + + @Override + public boolean isInstant() { + return handle.isInstant(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java new file mode 100644 index 0000000..0959a09 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java @@ -0,0 +1,142 @@ +package org.bukkit.craftbukkit.projectiles; + +import java.util.Random; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Egg; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.SmallFireball; +import org.bukkit.entity.Snowball; +import org.bukkit.entity.ThrownExpBottle; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.WitherSkull; +import org.bukkit.inventory.ItemStack; +import org.bukkit.projectiles.BlockProjectileSource; +import org.bukkit.util.Vector; + +import net.minecraft.server.BlockDispenser; +import net.minecraft.server.EntityArrow; +import net.minecraft.server.EntityEgg; +import net.minecraft.server.EntityEnderPearl; +import net.minecraft.server.EntityFireball; +import net.minecraft.server.EntityLargeFireball; +import net.minecraft.server.EntityPotion; +import net.minecraft.server.EntityProjectile; +import net.minecraft.server.EntitySmallFireball; +import net.minecraft.server.EntitySnowball; +import net.minecraft.server.EntityThrownExpBottle; +import net.minecraft.server.EntityWitherSkull; +import net.minecraft.server.EnumFacing; +import net.minecraft.server.IPosition; +import net.minecraft.server.IProjectile; +import net.minecraft.server.MathHelper; +import net.minecraft.server.SourceBlock; +import net.minecraft.server.TileEntityDispenser; + +public class CraftBlockProjectileSource implements BlockProjectileSource { + private final TileEntityDispenser dispenserBlock; + + public CraftBlockProjectileSource(TileEntityDispenser dispenserBlock) { + this.dispenserBlock = dispenserBlock; + } + + @Override + public Block getBlock() { + return dispenserBlock.getWorld().getWorld().getBlockAt(dispenserBlock.x, dispenserBlock.y, dispenserBlock.z); + } + + @Override + public T launchProjectile(Class projectile) { + return launchProjectile(projectile, null); + } + + @Override + public T launchProjectile(Class projectile, Vector velocity) { + Validate.isTrue(getBlock().getType() == Material.DISPENSER, "Block is no longer dispenser"); + // Copied from BlockDispenser.dispense() + SourceBlock isourceblock = new SourceBlock(dispenserBlock.getWorld(), dispenserBlock.x, dispenserBlock.y, dispenserBlock.z); + // Copied from DispenseBehaviorProjectile + IPosition iposition = BlockDispenser.a(isourceblock); + EnumFacing enumfacing = BlockDispenser.b(isourceblock.h()); + net.minecraft.server.World world = dispenserBlock.getWorld(); + net.minecraft.server.Entity launch = null; + + if (Snowball.class.isAssignableFrom(projectile)) { + launch = new EntitySnowball(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (Egg.class.isAssignableFrom(projectile)) { + launch = new EntityEgg(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (EnderPearl.class.isAssignableFrom(projectile)) { + launch = new EntityEnderPearl(world); + launch.setPosition(iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { + launch = new EntityThrownExpBottle(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (ThrownPotion.class.isAssignableFrom(projectile)) { + launch = new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), CraftItemStack.asNMSCopy(new ItemStack(Material.POTION, 1))); + } else if (Arrow.class.isAssignableFrom(projectile)) { + launch = new EntityArrow(world, iposition.getX(), iposition.getY(), iposition.getZ()); + ((EntityArrow) launch).fromPlayer = 1; + ((EntityArrow) launch).projectileSource = this; + } else if (Fireball.class.isAssignableFrom(projectile)) { + double d0 = iposition.getX() + (double) ((float) enumfacing.getAdjacentX() * 0.3F); + double d1 = iposition.getY() + (double) ((float) enumfacing.getAdjacentY() * 0.3F); + double d2 = iposition.getZ() + (double) ((float) enumfacing.getAdjacentZ() * 0.3F); + Random random = world.random; + double d3 = random.nextGaussian() * 0.05D + (double) enumfacing.getAdjacentX(); + double d4 = random.nextGaussian() * 0.05D + (double) enumfacing.getAdjacentY(); + double d5 = random.nextGaussian() * 0.05D + (double) enumfacing.getAdjacentZ(); + + if (SmallFireball.class.isAssignableFrom(projectile)) { + launch = new EntitySmallFireball(world, d0, d1, d2, d3, d4, d5); + } else if (WitherSkull.class.isAssignableFrom(projectile)) { + launch = new EntityWitherSkull(world); + launch.setPosition(d0, d1, d2); + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + ((EntityFireball) launch).dirX = d3 / d6 * 0.1D; + ((EntityFireball) launch).dirY = d4 / d6 * 0.1D; + ((EntityFireball) launch).dirZ = d5 / d6 * 0.1D; + } else { + launch = new EntityLargeFireball(world); + launch.setPosition(d0, d1, d2); + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + ((EntityFireball) launch).dirX = d3 / d6 * 0.1D; + ((EntityFireball) launch).dirY = d4 / d6 * 0.1D; + ((EntityFireball) launch).dirZ = d5 / d6 * 0.1D; + } + + ((EntityFireball) launch).projectileSource = this; + } + + Validate.notNull(launch, "Projectile not supported"); + + if (launch instanceof IProjectile) { + if (launch instanceof EntityProjectile) { + ((EntityProjectile) launch).projectileSource = this; + } + // Values from DispenseBehaviorProjectile + float a = 6.0F; + float b = 1.1F; + if (launch instanceof EntityPotion || launch instanceof ThrownExpBottle) { + // Values from respective DispenseBehavior classes + a *= 0.5F; + b *= 1.25F; + } + // Copied from DispenseBehaviorProjectile + ((IProjectile) launch).shoot((double) enumfacing.getAdjacentX(), (double) ((float) enumfacing.getAdjacentY() + 0.1F), (double) enumfacing.getAdjacentZ(), b, a); + } + + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } + + world.addEntity(launch); + return (T) launch.getBukkitEntity(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncDebugger.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncDebugger.java new file mode 100644 index 0000000..d80ae50 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncDebugger.java @@ -0,0 +1,37 @@ +package org.bukkit.craftbukkit.scheduler; + +import org.bukkit.plugin.Plugin; + + +class CraftAsyncDebugger { + private CraftAsyncDebugger next = null; + private final int expiry; + private final Plugin plugin; + private final Class clazz; + + CraftAsyncDebugger(final int expiry, final Plugin plugin, final Class clazz) { + this.expiry = expiry; + this.plugin = plugin; + this.clazz = clazz; + + } + + final CraftAsyncDebugger getNextHead(final int time) { + CraftAsyncDebugger next, current = this; + while (time > current.expiry && (next = current.next) != null) { + current = next; + } + return current; + } + + final CraftAsyncDebugger setNext(final CraftAsyncDebugger next) { + return this.next = next; + } + + StringBuilder debugTo(final StringBuilder string) { + for (CraftAsyncDebugger next = this; next != null; next = next.next) { + string.append(next.plugin.getDescription().getName()).append(':').append(next.clazz.getName()).append('@').append(next.expiry).append(','); + } + return string; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java new file mode 100644 index 0000000..f3da84a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java @@ -0,0 +1,109 @@ +package org.bukkit.craftbukkit.scheduler; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; + +import org.apache.commons.lang.UnhandledException; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitWorker; + + +class CraftAsyncTask extends CraftTask { + + private final LinkedList workers = new LinkedList(); + private final Map runners; + + CraftAsyncTask(final Map runners, final Plugin plugin, final Runnable task, final int id, final long delay) { + super(plugin, task, id, delay); + this.runners = runners; + } + + @Override + public boolean isSync() { + return false; + } + + @Override + public void run() { + final Thread thread = Thread.currentThread(); + synchronized(workers) { + if (getPeriod() == -2) { + // Never continue running after cancelled. + // Checking this with the lock is important! + return; + } + workers.add( + new BukkitWorker() { + public Thread getThread() { + return thread; + } + + public int getTaskId() { + return CraftAsyncTask.this.getTaskId(); + } + + public Plugin getOwner() { + return CraftAsyncTask.this.getOwner(); + } + }); + } + Throwable thrown = null; + try { + super.run(); + } catch (final Throwable t) { + thrown = t; + throw new UnhandledException( + String.format( + "Plugin %s generated an exception while executing task %s", + getOwner().getDescription().getFullName(), + getTaskId()), + thrown); + } finally { + // Cleanup is important for any async task, otherwise ghost tasks are everywhere + synchronized(workers) { + try { + final Iterator workers = this.workers.iterator(); + boolean removed = false; + while (workers.hasNext()) { + if (workers.next().getThread() == thread) { + workers.remove(); + removed = true; // Don't throw exception + break; + } + } + if (!removed) { + throw new IllegalStateException( + String.format( + "Unable to remove worker %s on task %s for %s", + thread.getName(), + getTaskId(), + getOwner().getDescription().getFullName()), + thrown); // We don't want to lose the original exception, if any + } + } finally { + if (getPeriod() < 0 && workers.isEmpty()) { + // At this spot, we know we are the final async task being executed! + // Because we have the lock, nothing else is running or will run because delay < 0 + runners.remove(getTaskId()); + } + } + } + } + } + + LinkedList getWorkers() { + return workers; + } + + boolean cancel0() { + synchronized (workers) { + // Synchronizing here prevents race condition for a completing task + setPeriod(-2l); + if (workers.isEmpty()) { + runners.remove(getTaskId()); + } + } + return true; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java new file mode 100644 index 0000000..1baec56 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java @@ -0,0 +1,108 @@ +package org.bukkit.craftbukkit.scheduler; + +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.bukkit.plugin.Plugin; + +class CraftFuture extends CraftTask implements Future { + + private final Callable callable; + private T value; + private Exception exception = null; + + CraftFuture(final Callable callable, final Plugin plugin, final int id) { + super(plugin, null, id, -1l); + this.callable = callable; + } + + public synchronized boolean cancel(final boolean mayInterruptIfRunning) { + if (getPeriod() != -1l) { + return false; + } + setPeriod(-2l); + return true; + } + + public boolean isCancelled() { + return getPeriod() == -2l; + } + + public boolean isDone() { + final long period = this.getPeriod(); + return period != -1l && period != -3l; + } + + public T get() throws CancellationException, InterruptedException, ExecutionException { + try { + return get(0, TimeUnit.MILLISECONDS); + } catch (final TimeoutException e) { + throw new Error(e); + } + } + + public synchronized T get(long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + timeout = unit.toMillis(timeout); + long period = this.getPeriod(); + long timestamp = timeout > 0 ? System.currentTimeMillis() : 0l; + while (true) { + if (period == -1l || period == -3l) { + this.wait(timeout); + period = this.getPeriod(); + if (period == -1l || period == -3l) { + if (timeout == 0l) { + continue; + } + timeout += timestamp - (timestamp = System.currentTimeMillis()); + if (timeout > 0) { + continue; + } + throw new TimeoutException(); + } + } + if (period == -2l) { + throw new CancellationException(); + } + if (period == -4l) { + if (exception == null) { + return value; + } + throw new ExecutionException(exception); + } + throw new IllegalStateException("Expected " + -1l + " to " + -4l + ", got " + period); + } + } + + @Override + public void run() { + synchronized (this) { + if (getPeriod() == -2l) { + return; + } + setPeriod(-3l); + } + try { + value = callable.call(); + } catch (final Exception e) { + exception = e; + } finally { + synchronized (this) { + setPeriod(-4l); + this.notifyAll(); + } + } + } + + synchronized boolean cancel0() { + if (getPeriod() != -1l) { + return false; + } + setPeriod(-2l); + notifyAll(); + return true; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java new file mode 100644 index 0000000..cdff9f1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java @@ -0,0 +1,502 @@ +package org.bukkit.craftbukkit.scheduler; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.PriorityQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; + +import net.valorhcf.ThreadingManager; // Poweruser + +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.IllegalPluginAccessException; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.scheduler.BukkitWorker; + +/** + * The fundamental concepts for this implementation: + *
          • Main thread owns {@link #head} and {@link #currentTick}, but it may be read from any thread
          • + *
          • Main thread exclusively controls {@link #temp} and {@link #pending}. + * They are never to be accessed outside of the main thread; alternatives exist to prevent locking.
          • + *
          • {@link #head} to {@link #tail} act as a linked list/queue, with 1 consumer and infinite producers. + * Adding to the tail is atomic and very efficient; utility method is {@link #handle(CraftTask, long)} or {@link #addTask(CraftTask)}.
          • + *
          • Changing the period on a task is delicate. + * Any future task needs to notify waiting threads. + * Async tasks must be synchronized to make sure that any thread that's finishing will remove itself from {@link #runners}. + * Another utility method is provided for this, {@link #cancelTask(CraftTask)}
          • + *
          • {@link #runners} provides a moderately up-to-date view of active tasks. + * If the linked head to tail set is read, all remaining tasks that were active at the time execution started will be located in runners.
          • + *
          • Async tasks are responsible for removing themselves from runners
          • + *
          • Sync tasks are only to be removed from runners on the main thread when coupled with a removal from pending and temp.
          • + *
          • Most of the design in this scheduler relies on queuing special tasks to perform any data changes on the main thread. + * When executed from inside a synchronous method, the scheduler will be updated before next execution by virtue of the frequent {@link #parsePending()} calls.
          • + */ +public class CraftScheduler implements BukkitScheduler { + + /** + * Counter for IDs. Order doesn't matter, only uniqueness. + */ + private final AtomicInteger ids = new AtomicInteger(1); + /** + * Current head of linked-list. This reference is always stale, {@link CraftTask#next} is the live reference. + */ + private volatile CraftTask head = new CraftTask(); + /** + * Tail of a linked-list. AtomicReference only matters when adding to queue + */ + private final AtomicReference tail = new AtomicReference(head); + /** + * Main thread logic only + */ + private final PriorityQueue pending = new PriorityQueue(10, + new Comparator() { + public int compare(final CraftTask o1, final CraftTask o2) { + return (int) (o1.getNextRun() - o2.getNextRun()); + } + }); + /** + * Main thread logic only + */ + private final List temp = new ArrayList(); + /** + * These are tasks that are currently active. It's provided for 'viewing' the current state. + */ + private final ConcurrentHashMap runners = new ConcurrentHashMap(); + private volatile int currentTick = -1; + private final Executor executor = ThreadingManager.getCommonThreadPool(); // Poweruser + // MineHQ start - nope + private CraftAsyncDebugger debugHead = new CraftAsyncDebugger(-1, null, null) {@Override StringBuilder debugTo(StringBuilder string) {return string;}}; + private CraftAsyncDebugger debugTail = debugHead; + private static final int RECENT_TICKS; + // MineHQ end + + static { + RECENT_TICKS = 30; + } + + public int scheduleSyncDelayedTask(final Plugin plugin, final Runnable task) { + return this.scheduleSyncDelayedTask(plugin, task, 0l); + } + + public BukkitTask runTask(Plugin plugin, Runnable runnable) { + return runTaskLater(plugin, runnable, 0l); + } + + @Deprecated + public int scheduleAsyncDelayedTask(final Plugin plugin, final Runnable task) { + return this.scheduleAsyncDelayedTask(plugin, task, 0l); + } + + public BukkitTask runTaskAsynchronously(Plugin plugin, Runnable runnable) { + return runTaskLaterAsynchronously(plugin, runnable, 0l); + } + + public int scheduleSyncDelayedTask(final Plugin plugin, final Runnable task, final long delay) { + return this.scheduleSyncRepeatingTask(plugin, task, delay, -1l); + } + + public BukkitTask runTaskLater(Plugin plugin, Runnable runnable, long delay) { + return runTaskTimer(plugin, runnable, delay, -1l); + } + + @Deprecated + public int scheduleAsyncDelayedTask(final Plugin plugin, final Runnable task, final long delay) { + return this.scheduleAsyncRepeatingTask(plugin, task, delay, -1l); + } + + public BukkitTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) { + return runTaskTimerAsynchronously(plugin, runnable, delay, -1l); + } + + public int scheduleSyncRepeatingTask(final Plugin plugin, final Runnable runnable, long delay, long period) { + return runTaskTimer(plugin, runnable, delay, period).getTaskId(); + } + + public BukkitTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) { + validate(plugin, runnable); + if (delay < 0l) { + delay = 0; + } + if (period == 0l) { + period = 1l; + } else if (period < -1l) { + period = -1l; + } + return handle(new CraftTask(plugin, runnable, nextId(), period), delay); + } + + @Deprecated + public int scheduleAsyncRepeatingTask(final Plugin plugin, final Runnable runnable, long delay, long period) { + return runTaskTimerAsynchronously(plugin, runnable, delay, period).getTaskId(); + } + + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) { + validate(plugin, runnable); + if (delay < 0l) { + delay = 0; + } + if (period == 0l) { + period = 1l; + } else if (period < -1l) { + period = -1l; + } + return handle(new CraftAsyncTask(runners, plugin, runnable, nextId(), period), delay); + } + + public Future callSyncMethod(final Plugin plugin, final Callable task) { + validate(plugin, task); + final CraftFuture future = new CraftFuture(task, plugin, nextId()); + handle(future, 0l); + return future; + } + + public void cancelTask(final int taskId) { + if (taskId <= 0) { + return; + } + CraftTask task = runners.get(taskId); + if (task != null) { + task.cancel0(); + } + task = new CraftTask( + new Runnable() { + public void run() { + if (!check(CraftScheduler.this.temp)) { + check(CraftScheduler.this.pending); + } + } + private boolean check(final Iterable collection) { + final Iterator tasks = collection.iterator(); + while (tasks.hasNext()) { + final CraftTask task = tasks.next(); + if (task.getTaskId() == taskId) { + task.cancel0(); + tasks.remove(); + if (task.isSync()) { + runners.remove(taskId); + } + return true; + } + } + return false; + }}); + handle(task, 0l); + for (CraftTask taskPending = head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { + if (taskPending == task) { + return; + } + if (taskPending.getTaskId() == taskId) { + taskPending.cancel0(); + } + } + } + + public void cancelTasks(final Plugin plugin) { + Validate.notNull(plugin, "Cannot cancel tasks of null plugin"); + final CraftTask task = new CraftTask( + new Runnable() { + public void run() { + check(CraftScheduler.this.pending); + check(CraftScheduler.this.temp); + } + void check(final Iterable collection) { + final Iterator tasks = collection.iterator(); + while (tasks.hasNext()) { + final CraftTask task = tasks.next(); + if (task.getOwner().equals(plugin)) { + task.cancel0(); + tasks.remove(); + if (task.isSync()) { + runners.remove(task.getTaskId()); + } + } + } + } + }); + handle(task, 0l); + for (CraftTask taskPending = head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { + if (taskPending == task) { + return; + } + if (taskPending.getTaskId() != -1 && taskPending.getOwner().equals(plugin)) { + taskPending.cancel0(); + } + } + for (CraftTask runner : runners.values()) { + if (runner.getOwner().equals(plugin)) { + runner.cancel0(); + } + } + } + + public void cancelAllTasks() { + final CraftTask task = new CraftTask( + new Runnable() { + public void run() { + Iterator it = CraftScheduler.this.runners.values().iterator(); + while (it.hasNext()) { + CraftTask task = it.next(); + task.cancel0(); + if (task.isSync()) { + it.remove(); + } + } + CraftScheduler.this.pending.clear(); + CraftScheduler.this.temp.clear(); + } + }); + handle(task, 0l); + for (CraftTask taskPending = head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { + if (taskPending == task) { + break; + } + taskPending.cancel0(); + } + for (CraftTask runner : runners.values()) { + runner.cancel0(); + } + } + + public boolean isCurrentlyRunning(final int taskId) { + final CraftTask task = runners.get(taskId); + if (task == null || task.isSync()) { + return false; + } + final CraftAsyncTask asyncTask = (CraftAsyncTask) task; + synchronized (asyncTask.getWorkers()) { + return asyncTask.getWorkers().isEmpty(); + } + } + + public boolean isQueued(final int taskId) { + if (taskId <= 0) { + return false; + } + for (CraftTask task = head.getNext(); task != null; task = task.getNext()) { + if (task.getTaskId() == taskId) { + return task.getPeriod() >= -1l; // The task will run + } + } + CraftTask task = runners.get(taskId); + return task != null && task.getPeriod() >= -1l; + } + + public List getActiveWorkers() { + final ArrayList workers = new ArrayList(); + for (final CraftTask taskObj : runners.values()) { + // Iterator will be a best-effort (may fail to grab very new values) if called from an async thread + if (taskObj.isSync()) { + continue; + } + final CraftAsyncTask task = (CraftAsyncTask) taskObj; + synchronized (task.getWorkers()) { + // This will never have an issue with stale threads; it's state-safe + workers.addAll(task.getWorkers()); + } + } + return workers; + } + + public List getPendingTasks() { + final ArrayList truePending = new ArrayList(); + for (CraftTask task = head.getNext(); task != null; task = task.getNext()) { + if (task.getTaskId() != -1) { + // -1 is special code + truePending.add(task); + } + } + + final ArrayList pending = new ArrayList(); + for (CraftTask task : runners.values()) { + if (task.getPeriod() >= -1l) { + pending.add(task); + } + } + + for (final CraftTask task : truePending) { + if (task.getPeriod() >= -1l && !pending.contains(task)) { + pending.add(task); + } + } + return pending; + } + + /** + * This method is designed to never block or wait for locks; an immediate execution of all current tasks. + */ + public void mainThreadHeartbeat(final int currentTick) { + this.currentTick = currentTick; + final List temp = this.temp; + parsePending(); + while (isReady(currentTick)) { + final CraftTask task = pending.remove(); + if (task.getPeriod() < -1l) { + if (task.isSync()) { + runners.remove(task.getTaskId(), task); + } + parsePending(); + continue; + } + if (task.isSync()) { + try { + task.timings.startTiming(); // Spigot + task.run(); + task.timings.stopTiming(); // Spigot + } catch (final Throwable throwable) { + task.getOwner().getLogger().log( + Level.WARNING, + String.format( + "Task #%s for %s generated an exception", + task.getTaskId(), + task.getOwner().getDescription().getFullName()), + throwable); + } + parsePending(); + } else { + // debugTail = debugTail.setNext(new CraftAsyncDebugger(currentTick + RECENT_TICKS, task.getOwner(), task.getTaskClass())); // MineHQ - nope + executor.execute(task); + // We don't need to parse pending + // (async tasks must live with race-conditions if they attempt to cancel between these few lines of code) + } + final long period = task.getPeriod(); // State consistency + if (period > 0) { + task.setNextRun(currentTick + period); + temp.add(task); + } else if (task.isSync()) { + runners.remove(task.getTaskId()); + } + } + pending.addAll(temp); + temp.clear(); + // debugHead = debugHead.getNextHead(currentTick); // MineHQ - nope + } + + private void addTask(final CraftTask task) { + final AtomicReference tail = this.tail; + CraftTask tailTask = tail.get(); + while (!tail.compareAndSet(tailTask, task)) { + tailTask = tail.get(); + } + tailTask.setNext(task); + } + + private CraftTask handle(final CraftTask task, final long delay) { + task.setNextRun(currentTick + delay); + addTask(task); + return task; + } + + private static void validate(final Plugin plugin, final Object task) { + Validate.notNull(plugin, "Plugin cannot be null"); + Validate.notNull(task, "Task cannot be null"); + if (!plugin.isEnabled()) { + throw new IllegalPluginAccessException("Plugin attempted to register task while disabled"); + } + } + + private int nextId() { + return ids.incrementAndGet(); + } + + private void parsePending() { + CraftTask head = this.head; + CraftTask task = head.getNext(); + CraftTask lastTask = head; + for (; task != null; task = (lastTask = task).getNext()) { + if (task.getTaskId() == -1) { + task.run(); + } else if (task.getPeriod() >= -1l) { + pending.add(task); + runners.put(task.getTaskId(), task); + } + } + // We split this because of the way things are ordered for all of the async calls in CraftScheduler + // (it prevents race-conditions) + for (task = head; task != lastTask; task = head) { + head = task.getNext(); + task.setNext(null); + } + this.head = lastTask; + } + + private boolean isReady(final int currentTick) { + return !pending.isEmpty() && pending.peek().getNextRun() <= currentTick; + } + + @Override + public String toString() { + // MineHQ start + return ""; + /* + int debugTick = currentTick; + StringBuilder string = new StringBuilder("Recent tasks from ").append(debugTick - RECENT_TICKS).append('-').append(debugTick).append('{'); + debugHead.debugTo(string); + return string.append('}').toString(); + */ + // MineHQ end + } + + @Deprecated + @Override + public int scheduleSyncDelayedTask(Plugin plugin, BukkitRunnable task, long delay) { + return scheduleSyncDelayedTask(plugin, (Runnable) task, delay); + } + + @Deprecated + @Override + public int scheduleSyncDelayedTask(Plugin plugin, BukkitRunnable task) { + return scheduleSyncDelayedTask(plugin, (Runnable) task); + } + + @Deprecated + @Override + public int scheduleSyncRepeatingTask(Plugin plugin, BukkitRunnable task, long delay, long period) { + return scheduleSyncRepeatingTask(plugin, (Runnable) task, delay, period); + } + + @Deprecated + @Override + public BukkitTask runTask(Plugin plugin, BukkitRunnable task) throws IllegalArgumentException { + return runTask(plugin, (Runnable) task); + } + + @Deprecated + @Override + public BukkitTask runTaskAsynchronously(Plugin plugin, BukkitRunnable task) throws IllegalArgumentException { + return runTaskAsynchronously(plugin, (Runnable) task); + } + + @Deprecated + @Override + public BukkitTask runTaskLater(Plugin plugin, BukkitRunnable task, long delay) throws IllegalArgumentException { + return runTaskLater(plugin, (Runnable) task, delay); + } + + @Deprecated + @Override + public BukkitTask runTaskLaterAsynchronously(Plugin plugin, BukkitRunnable task, long delay) throws IllegalArgumentException { + return runTaskLaterAsynchronously(plugin, (Runnable) task, delay); + } + + @Deprecated + @Override + public BukkitTask runTaskTimer(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException { + return runTaskTimer(plugin, (Runnable) task, delay, period); + } + + @Deprecated + @Override + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException { + return runTaskTimerAsynchronously(plugin, (Runnable) task, delay, period); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java new file mode 100644 index 0000000..220e39a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java @@ -0,0 +1,124 @@ +package org.bukkit.craftbukkit.scheduler; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.SpigotTimings; // Spigot +import org.spigotmc.CustomTimingsHandler; // Spigot +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + + +public class CraftTask implements BukkitTask, Runnable { // Spigot + + private volatile CraftTask next = null; + /** + * -1 means no repeating
            + * -2 means cancel
            + * -3 means processing for Future
            + * -4 means done for Future
            + * Never 0
            + * >0 means number of ticks to wait between each execution + */ + private volatile long period; + private long nextRun; + private final Runnable task; + private final Plugin plugin; + private final int id; + + final CustomTimingsHandler timings; // Spigot + CraftTask() { + this(null, null, -1, -1); + } + + CraftTask(final Runnable task) { + this(null, task, -1, -1); + } + + // Spigot start + public String timingName = null; + CraftTask(String timingName) { + this(timingName, null, null, -1, -1); + } + CraftTask(String timingName, final Runnable task) { + this(timingName, null, task, -1, -1); + } + CraftTask(String timingName, final Plugin plugin, final Runnable task, final int id, final long period) { + this.plugin = plugin; + this.task = task; + this.id = id; + this.period = period; + this.timingName = timingName == null && task == null ? "Unknown" : timingName; + timings = this.isSync() ? SpigotTimings.getPluginTaskTimings(this, period) : null; + } + + CraftTask(final Plugin plugin, final Runnable task, final int id, final long period) { + this(null, plugin, task, id, period); + // Spigot end + } + + public final int getTaskId() { + return id; + } + + public final Plugin getOwner() { + return plugin; + } + + public boolean isSync() { + return true; + } + + public void run() { + task.run(); + } + + long getPeriod() { + return period; + } + + void setPeriod(long period) { + this.period = period; + } + + long getNextRun() { + return nextRun; + } + + void setNextRun(long nextRun) { + this.nextRun = nextRun; + } + + CraftTask getNext() { + return next; + } + + void setNext(CraftTask next) { + this.next = next; + } + + Class getTaskClass() { + return task.getClass(); + } + + public void cancel() { + Bukkit.getScheduler().cancelTask(id); + } + + /** + * This method properly sets the status to cancelled, synchronizing when required. + * + * @return false if it is a craft future task that has already begun execution, true otherwise + */ + boolean cancel0() { + setPeriod(-2l); + return true; + } + + // Spigot start + public String getTaskName() { + if (timingName != null) { + return timingName; + } + return task.getClass().getName(); + } + // Spigot end +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java new file mode 100644 index 0000000..612a524 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java @@ -0,0 +1,68 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Map; + +import net.minecraft.server.IScoreboardCriteria; +import net.minecraft.server.ScoreboardObjective; + +import com.google.common.collect.ImmutableMap; + +final class CraftCriteria { + static final Map DEFAULTS; + static final CraftCriteria DUMMY; + + static { + ImmutableMap.Builder defaults = ImmutableMap.builder(); + + for (Map.Entry entry : ((Map ) IScoreboardCriteria.criteria).entrySet()) { + String name = entry.getKey().toString(); + IScoreboardCriteria criteria = (IScoreboardCriteria) entry.getValue(); + if (!criteria.getName().equals(name)) { + throw new AssertionError("Unexpected entry " + name + " to criteria " + criteria + "(" + criteria.getName() + ")"); + } + + defaults.put(name, new CraftCriteria(criteria)); + } + + DEFAULTS = defaults.build(); + DUMMY = DEFAULTS.get("dummy"); + } + + final IScoreboardCriteria criteria; + final String bukkitName; + + private CraftCriteria(String bukkitName) { + this.bukkitName = bukkitName; + this.criteria = DUMMY.criteria; + } + + private CraftCriteria(IScoreboardCriteria criteria) { + this.criteria = criteria; + this.bukkitName = criteria.getName(); + } + + static CraftCriteria getFromNMS(ScoreboardObjective objective) { + return DEFAULTS.get(objective.getCriteria().getName()); + } + + static CraftCriteria getFromBukkit(String name) { + final CraftCriteria criteria = DEFAULTS.get(name); + if (criteria != null) { + return criteria; + } + return new CraftCriteria(name); + } + + @Override + public boolean equals(Object that) { + if (!(that instanceof CraftCriteria)) { + return false; + } + return ((CraftCriteria) that).bukkitName.equals(this.bukkitName); + } + + @Override + public int hashCode() { + return this.bukkitName.hashCode() ^ CraftCriteria.class.hashCode(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java new file mode 100644 index 0000000..0ee1147 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java @@ -0,0 +1,112 @@ +package org.bukkit.craftbukkit.scoreboard; + +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardObjective; + +import org.apache.commons.lang.Validate; +import org.bukkit.OfflinePlayer; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Score; + +final class CraftObjective extends CraftScoreboardComponent implements Objective { + private final ScoreboardObjective objective; + private final CraftCriteria criteria; + + CraftObjective(CraftScoreboard scoreboard, ScoreboardObjective objective) { + super(scoreboard); + this.objective = objective; + this.criteria = CraftCriteria.getFromNMS(objective); + + scoreboard.objectives.put(objective.getName(), this); + } + + ScoreboardObjective getHandle() { + return objective; + } + + public String getName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return objective.getName(); + } + + public String getDisplayName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return objective.getDisplayName(); + } + + public void setDisplayName(String displayName) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(displayName, "Display name cannot be null"); + Validate.isTrue(displayName.length() <= 32, "Display name '" + displayName + "' is longer than the limit of 32 characters"); + CraftScoreboard scoreboard = checkState(); + + objective.setDisplayName(displayName); + } + + public String getCriteria() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return criteria.bukkitName; + } + + public boolean isModifiable() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return !criteria.criteria.isReadOnly(); + } + + public void setDisplaySlot(DisplaySlot slot) throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + Scoreboard board = scoreboard.board; + ScoreboardObjective objective = this.objective; + + for (int i = 0; i < CraftScoreboardTranslations.MAX_DISPLAY_SLOT; i++) { + if (board.getObjectiveForSlot(i) == objective) { + board.setDisplaySlot(i, null); + } + } + if (slot != null) { + int slotNumber = CraftScoreboardTranslations.fromBukkitSlot(slot); + board.setDisplaySlot(slotNumber, getHandle()); + } + } + + public DisplaySlot getDisplaySlot() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + Scoreboard board = scoreboard.board; + ScoreboardObjective objective = this.objective; + + for (int i = 0; i < CraftScoreboardTranslations.MAX_DISPLAY_SLOT; i++) { + if (board.getObjectiveForSlot(i) == objective) { + return CraftScoreboardTranslations.toBukkitSlot(i); + } + } + return null; + } + + public Score getScore(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException { + Validate.notNull(player, "Player cannot be null"); + CraftScoreboard scoreboard = checkState(); + + return new CraftScore(this, player.getName()); + } + + public Score getScore(String entry) throws IllegalArgumentException, IllegalStateException { + Validate.notNull(entry, "Entry cannot be null"); + if (entry.length() > 16) throw new IllegalArgumentException("Entry cannot be longer than 16 characters!"); // Spigot + CraftScoreboard scoreboard = checkState(); + + return new CraftScore(this, entry); + } + + @Override + public void unregister() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + scoreboard.objectives.remove(this.getName()); + scoreboard.board.unregisterObjective(objective); + setUnregistered(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java new file mode 100644 index 0000000..b265616 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java @@ -0,0 +1,70 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Map; + +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardScore; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Score; + +/** + * TL;DR: This class is special and lazily grabs a handle... + * ...because a handle is a full fledged (I think permanent) hashMap for the associated name. + *

            + * Also, as an added perk, a CraftScore will (intentionally) stay a valid reference so long as objective is valid. + */ +final class CraftScore implements Score { + private final String entry; + private final CraftObjective objective; + + CraftScore(CraftObjective objective, String entry) { + this.objective = objective; + this.entry = entry; + } + + public OfflinePlayer getPlayer() { + return Bukkit.getOfflinePlayer(entry); + } + + public String getEntry() { + return entry; + } + + public Objective getObjective() { + return objective; + } + + public int getScore() throws IllegalStateException { + Scoreboard board = objective.checkState().board; + + if (board.getPlayers().contains(entry)) { // Lazy + Map scores = board.getPlayerObjectives(entry); // Spigot + ScoreboardScore score = scores.get(objective.getHandle()); + if (score != null) { // Lazy + return score.getScore(); + } + } + + return 0; // Lazy + } + + public void setScore(int score) throws IllegalStateException { + objective.checkState().board.getPlayerScoreForObjective(entry, objective.getHandle()).setScore(score); + } + + // Spigot start + @Override + public boolean isScoreSet() throws IllegalStateException { + Scoreboard board = objective.checkState().board; + + return board.getPlayers().contains(entry) && board.getPlayerObjectives(entry).containsKey(objective.getHandle()); + } + // Spigot end + + public CraftScoreboard getScoreboard() { + return objective.getScoreboard(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java new file mode 100644 index 0000000..ad65b3f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java @@ -0,0 +1,159 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardObjective; +import net.minecraft.server.ScoreboardTeam; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Score; +import org.bukkit.scoreboard.Team; + +import com.google.common.collect.ImmutableSet; + +public final class CraftScoreboard implements org.bukkit.scoreboard.Scoreboard { + final Scoreboard board; + final Map objectives = new HashMap(); + final Map teams = new HashMap(); + + CraftScoreboard(Scoreboard board) { + this.board = board; + + for (ScoreboardObjective objective : (Iterable) board.getObjectives()) { + new CraftObjective(this, objective); // It adds itself to map + } + for (ScoreboardTeam team : (Iterable) board.getTeams()) { + new CraftTeam(this, team); // It adds itself to map + } + } + + public CraftObjective registerNewObjective(String name, String criteria) throws IllegalArgumentException { + Validate.notNull(name, "Objective name cannot be null"); + Validate.notNull(criteria, "Criteria cannot be null"); + Validate.isTrue(name.length() <= 16, "The name '" + name + "' is longer than the limit of 16 characters"); + Validate.isTrue(board.getObjective(name) == null, "An objective of name '" + name + "' already exists"); + + CraftCriteria craftCriteria = CraftCriteria.getFromBukkit(criteria); + ScoreboardObjective objective = board.registerObjective(name, craftCriteria.criteria); + return new CraftObjective(this, objective); + } + + public Objective getObjective(String name) throws IllegalArgumentException { + Validate.notNull(name, "Name cannot be null"); + return objectives.get(name); + } + + public ImmutableSet getObjectivesByCriteria(String criteria) throws IllegalArgumentException { + Validate.notNull(criteria, "Criteria cannot be null"); + + ImmutableSet.Builder objectives = ImmutableSet.builder(); + for (CraftObjective objective : this.objectives.values()) { + if (objective.getCriteria().equals(criteria)) { + objectives.add(objective); + } + } + return objectives.build(); + } + + public ImmutableSet getObjectives() { + return ImmutableSet.copyOf((Collection) objectives.values()); + } + + public Objective getObjective(DisplaySlot slot) throws IllegalArgumentException { + Validate.notNull(slot, "Display slot cannot be null"); + ScoreboardObjective objective = board.getObjectiveForSlot(CraftScoreboardTranslations.fromBukkitSlot(slot)); + if (objective == null) { + return null; + } + return this.objectives.get(objective.getName()); + } + + public ImmutableSet getScores(OfflinePlayer player) throws IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + + ImmutableSet.Builder scores = ImmutableSet.builder(); + for (CraftObjective objective : objectives.values()) { + scores.add(objective.getScore(player)); + } + return scores.build(); + } + + public ImmutableSet getScores(String entry) throws IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + + ImmutableSet.Builder scores = ImmutableSet.builder(); + for (CraftObjective objective : objectives.values()) { + scores.add(objective.getScore(entry)); + } + return scores.build(); + } + + public void resetScores(OfflinePlayer player) throws IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + + board.resetPlayerScores(player.getName()); + } + + public void resetScores(String entry) throws IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + + board.resetPlayerScores(entry); + } + + public Team getPlayerTeam(OfflinePlayer player) throws IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + + ScoreboardTeam team = board.getPlayerTeam(player.getName()); + return team == null ? null : teams.get(team.getName()); + } + + public Team getTeam(String teamName) throws IllegalArgumentException { + Validate.notNull(teamName, "Team name cannot be null"); + + return teams.get(teamName); + } + + public ImmutableSet getTeams() { + return ImmutableSet.copyOf((Collection) teams.values()); + } + + public Team registerNewTeam(String name) throws IllegalArgumentException { + Validate.notNull(name, "Team name cannot be null"); + Validate.isTrue(name.length() <= 16, "Team name '" + name + "' is longer than the limit of 16 characters"); + Validate.isTrue(board.getTeam(name) == null, "Team name '" + name + "' is already in use"); + + return new CraftTeam(this, board.createTeam(name)); + } + + public ImmutableSet getPlayers() { + ImmutableSet.Builder players = ImmutableSet.builder(); + for (Object playerName : board.getPlayers()) { + players.add(Bukkit.getOfflinePlayer(playerName.toString())); + } + return players.build(); + } + + public ImmutableSet getEntries() { + ImmutableSet.Builder entries = ImmutableSet.builder(); + for (Object entry : board.getPlayers()) { + entries.add(entry.toString()); + } + return entries.build(); + } + + public void clearSlot(DisplaySlot slot) throws IllegalArgumentException { + Validate.notNull(slot, "Slot cannot be null"); + board.setDisplaySlot(CraftScoreboardTranslations.fromBukkitSlot(slot), null); + } + + // CraftBukkit method + public Scoreboard getHandle() { + return board; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java new file mode 100644 index 0000000..3855a2b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.scoreboard; + +abstract class CraftScoreboardComponent { + private CraftScoreboard scoreboard; + + CraftScoreboardComponent(CraftScoreboard scoreboard) { + this.scoreboard = scoreboard; + } + + CraftScoreboard checkState() throws IllegalStateException { + CraftScoreboard scoreboard = this.scoreboard; + if (scoreboard == null) { + throw new IllegalStateException("Unregistered scoreboard component"); + } + return scoreboard; + } + + public CraftScoreboard getScoreboard() { + return scoreboard; + } + + abstract void unregister() throws IllegalStateException; + + final void setUnregistered() { + scoreboard = null; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java new file mode 100644 index 0000000..1b06dc8 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java @@ -0,0 +1,127 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.IScoreboardCriteria; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PacketPlayOutScoreboardObjective; +import net.minecraft.server.PacketPlayOutScoreboardTeam; +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardObjective; +import net.minecraft.server.ScoreboardScore; +import net.minecraft.server.ScoreboardServer; +import net.minecraft.server.ScoreboardTeam; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.util.WeakCollection; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.ScoreboardManager; + +public final class CraftScoreboardManager implements ScoreboardManager { + private final CraftScoreboard mainScoreboard; + private final MinecraftServer server; + private final Collection scoreboards = new WeakCollection(); + private final Map playerBoards = new HashMap(); + + public CraftScoreboardManager(MinecraftServer minecraftserver, net.minecraft.server.Scoreboard scoreboardServer) { + mainScoreboard = new CraftScoreboard(scoreboardServer); + server = minecraftserver; + scoreboards.add(mainScoreboard); + } + + public CraftScoreboard getMainScoreboard() { + return mainScoreboard; + } + + public CraftScoreboard getNewScoreboard() { + org.spigotmc.AsyncCatcher.catchOp( "scoreboard creation"); // Spigot + CraftScoreboard scoreboard = new CraftScoreboard(new ScoreboardServer(server)); + scoreboards.add(scoreboard); + return scoreboard; + } + + // CraftBukkit method + public CraftScoreboard getPlayerBoard(CraftPlayer player) { + CraftScoreboard board = playerBoards.get(player); + return (CraftScoreboard) (board == null ? getMainScoreboard() : board); + } + + // CraftBukkit method + public void setPlayerBoard(CraftPlayer player, org.bukkit.scoreboard.Scoreboard bukkitScoreboard) throws IllegalArgumentException { + Validate.isTrue(bukkitScoreboard instanceof CraftScoreboard, "Cannot set player scoreboard to an unregistered Scoreboard"); + + CraftScoreboard scoreboard = (CraftScoreboard) bukkitScoreboard; + net.minecraft.server.Scoreboard oldboard = getPlayerBoard(player).getHandle(); + net.minecraft.server.Scoreboard newboard = scoreboard.getHandle(); + EntityPlayer entityplayer = player.getHandle(); + + if (oldboard == newboard) { + return; + } + + if (oldboard != null) oldboard.removeViewer(entityplayer); // MineHQ + + if (scoreboard == mainScoreboard) { + playerBoards.remove(player); + } else { + playerBoards.put(player, (CraftScoreboard) scoreboard); + } + + // Old objective tracking + HashSet removed = new HashSet(); + for (int i = 0; i < 3; ++i) { + ScoreboardObjective scoreboardobjective = oldboard.getObjectiveForSlot(i); + if (scoreboardobjective != null && !removed.contains(scoreboardobjective)) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutScoreboardObjective(scoreboardobjective, 1)); + removed.add(scoreboardobjective); + } + } + + // Old team tracking + Iterator iterator = oldboard.getTeams().iterator(); + while (iterator.hasNext()) { + ScoreboardTeam scoreboardteam = (ScoreboardTeam) iterator.next(); + entityplayer.playerConnection.sendPacket(new PacketPlayOutScoreboardTeam(scoreboardteam, 1)); + } + + // The above is the reverse of the below method. + server.getPlayerList().sendScoreboard((ScoreboardServer) newboard, player.getHandle()); + } + + // CraftBukkit method + public void removePlayer(Player player) { + // MineHQ start + CraftPlayer craftPlayer = (CraftPlayer) player; + CraftScoreboard scoreboard = playerBoards.remove(craftPlayer); + if (scoreboard != null) { + scoreboard.getHandle().removeViewer(craftPlayer.getHandle()); + } + // MineHQ end + } + + // CraftBukkit method + public Collection getScoreboardScores(IScoreboardCriteria criteria, String name, Collection collection) { + for (CraftScoreboard scoreboard : scoreboards) { + Scoreboard board = scoreboard.board; + for (ScoreboardObjective objective : (Iterable) board.getObjectivesForCriteria(criteria)) { + collection.add(board.getPlayerScoreForObjective(name, objective)); + } + } + return collection; + } + + // CraftBukkit method + public void updateAllScoresForList(IScoreboardCriteria criteria, String name, List of) { + for (ScoreboardScore score : getScoreboardScores(criteria, name, new ArrayList())) { + score.updateForList(of); + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java new file mode 100644 index 0000000..d08e5a2 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.scoreboard; + +import net.minecraft.server.Scoreboard; + +import org.bukkit.scoreboard.DisplaySlot; + +import com.google.common.collect.ImmutableBiMap; + +class CraftScoreboardTranslations { + static final int MAX_DISPLAY_SLOT = 3; + static ImmutableBiMap SLOTS = ImmutableBiMap.of( + DisplaySlot.BELOW_NAME, "belowName", + DisplaySlot.PLAYER_LIST, "list", + DisplaySlot.SIDEBAR, "sidebar"); + + private CraftScoreboardTranslations() {} + + static DisplaySlot toBukkitSlot(int i) { + return SLOTS.inverse().get(Scoreboard.getSlotName(i)); + } + + static int fromBukkitSlot(DisplaySlot slot) { + return Scoreboard.getSlotForName(SLOTS.get(slot)); + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java new file mode 100644 index 0000000..ffd88a1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java @@ -0,0 +1,181 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.scoreboard.Team; + +import com.google.common.collect.ImmutableSet; + +import net.minecraft.server.ScoreboardTeam; + +final class CraftTeam extends CraftScoreboardComponent implements Team { + private final ScoreboardTeam team; + + CraftTeam(CraftScoreboard scoreboard, ScoreboardTeam team) { + super(scoreboard); + this.team = team; + scoreboard.teams.put(team.getName(), this); + } + + public String getName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.getName(); + } + + public String getDisplayName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.getDisplayName(); + } + + public void setDisplayName(String displayName) throws IllegalStateException { + Validate.notNull(displayName, "Display name cannot be null"); + Validate.isTrue(displayName.length() <= 32, "Display name '" + displayName + "' is longer than the limit of 32 characters"); + CraftScoreboard scoreboard = checkState(); + + team.setDisplayName(displayName); + } + + public String getPrefix() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.getPrefix(); + } + + public void setPrefix(String prefix) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(prefix, "Prefix cannot be null"); + Validate.isTrue(prefix.length() <= 32, "Prefix '" + prefix + "' is longer than the limit of 32 characters"); + CraftScoreboard scoreboard = checkState(); + + team.setPrefix(prefix); + } + + public String getSuffix() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.getSuffix(); + } + + public void setSuffix(String suffix) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(suffix, "Suffix cannot be null"); + Validate.isTrue(suffix.length() <= 32, "Suffix '" + suffix + "' is longer than the limit of 32 characters"); + CraftScoreboard scoreboard = checkState(); + + team.setSuffix(suffix); + } + + public boolean allowFriendlyFire() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.allowFriendlyFire(); + } + + public void setAllowFriendlyFire(boolean enabled) throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + team.setAllowFriendlyFire(enabled); + } + + public boolean canSeeFriendlyInvisibles() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.canSeeFriendlyInvisibles(); + } + + public void setCanSeeFriendlyInvisibles(boolean enabled) throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + team.setCanSeeFriendlyInvisibles(enabled); + } + + public Set getPlayers() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + ImmutableSet.Builder players = ImmutableSet.builder(); + for (Object o : team.getPlayerNameSet()) { + players.add(Bukkit.getOfflinePlayer(o.toString())); + } + return players.build(); + } + + // Spigot start + @Override + public Set getEntries() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + ImmutableSet.Builder entries = ImmutableSet.builder(); + for (Object o : team.getPlayerNameSet()){ + entries.add(o.toString()); + } + return entries.build(); + } + // Spigot end + + public int getSize() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.getPlayerNameSet().size(); + } + + public void addPlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + // Spigot Start + + addEntry(player.getName()); + } + + public void addEntry(String entry) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + CraftScoreboard scoreboard = checkState(); + + scoreboard.board.addPlayerToTeam(entry, team.getName()); + // Spigot end + } + + public boolean removePlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + // Spigot start + return removeEntry(player.getName()); + } + + public boolean removeEntry(String entry) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + CraftScoreboard scoreboard = checkState(); + + if (!team.getPlayerNameSet().contains(entry)) { + return false; + } + + scoreboard.board.removePlayerFromTeam(entry, team); + // Spigot end + return true; + } + + public boolean hasPlayer(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + // Spigot start + return hasEntry(player.getName()); + } + + public boolean hasEntry(String entry) throws IllegalArgumentException, IllegalStateException { + Validate.notNull("Entry cannot be null"); + + CraftScoreboard scoreboard = checkState(); + + return team.getPlayerNameSet().contains(entry); + // Spigot end + } + + @Override + public void unregister() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + scoreboard.board.removeTeam(team); + scoreboard.teams.remove(team.getName()); + setUnregistered(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/ArtifactDetails.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/ArtifactDetails.java new file mode 100644 index 0000000..a9c5eaf --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/ArtifactDetails.java @@ -0,0 +1,128 @@ +package org.bukkit.craftbukkit.updater; + +import java.util.Date; + +public class ArtifactDetails { + private String brokenReason; + private boolean isBroken; + private int buildNumber; + private String htmlUrl; + private String version; + private Date created; + private FileDetails file; + private ChannelDetails channel; + + public ChannelDetails getChannel() { + return channel; + } + + public void setChannel(ChannelDetails channel) { + this.channel = channel; + } + + public boolean isIsBroken() { + return isBroken; + } + + public void setIsBroken(boolean isBroken) { + this.isBroken = isBroken; + } + + public FileDetails getFile() { + return file; + } + + public void setFile(FileDetails file) { + this.file = file; + } + + public String getBrokenReason() { + return brokenReason; + } + + public void setBrokenReason(String brokenReason) { + this.brokenReason = brokenReason; + } + + public int getBuildNumber() { + return buildNumber; + } + + public void setBuildNumber(int buildNumber) { + this.buildNumber = buildNumber; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public String getHtmlUrl() { + return htmlUrl; + } + + public void setHtmlUrl(String htmlUrl) { + this.htmlUrl = htmlUrl; + } + + public boolean isBroken() { + return isBroken; + } + + public void setBroken(boolean isBroken) { + this.isBroken = isBroken; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public static class FileDetails { + private String url; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + } + + public static class ChannelDetails { + private String name; + private String slug; + private int priority; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + public String getSlug() { + return slug; + } + + public void setSlug(String slug) { + this.slug = slug; + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/AutoUpdater.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/AutoUpdater.java new file mode 100644 index 0000000..f21301c --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/AutoUpdater.java @@ -0,0 +1,127 @@ +package org.bukkit.craftbukkit.updater; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +public class AutoUpdater { + public static final String WARN_CONSOLE = "warn-console"; + public static final String WARN_OPERATORS = "warn-ops"; + + private final BukkitDLUpdaterService service; + private final List onUpdate = new ArrayList(); + private final List onBroken = new ArrayList(); + private final Logger log; + private final String channel; + private boolean enabled; + private ArtifactDetails current = null; + private ArtifactDetails latest = null; + private boolean suggestChannels = true; + + public AutoUpdater(BukkitDLUpdaterService service, Logger log, String channel) { + this.service = service; + this.log = log; + this.channel = channel; + } + + public String getChannel() { + return channel; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean isEnabled) { + this.enabled = isEnabled; + } + + public boolean shouldSuggestChannels() { + return suggestChannels; + } + + public void setSuggestChannels(boolean suggestChannels) { + this.suggestChannels = suggestChannels; + } + + public List getOnBroken() { + return onBroken; + } + + public List getOnUpdate() { + return onUpdate; + } + + public boolean isUpdateAvailable() { + if ((latest == null) || (current == null) || (!isEnabled())) { + return false; + } else { + return latest.getCreated().after(current.getCreated()); + } + } + + public ArtifactDetails getCurrent() { + return current; + } + + public ArtifactDetails getLatest() { + return latest; + } + + public void check(final String currentSlug) { + if (!isEnabled()) return; + + new Thread() { + @Override + public void run() { + current = service.getArtifact(currentSlug, "information about this CraftBukkit version; perhaps you are running a custom one?"); + latest = service.getArtifact("latest-" + channel, "latest artifact information"); + + if (isUpdateAvailable()) { + if ((current.isBroken()) && (onBroken.contains(WARN_CONSOLE))) { + log.severe("----- Bukkit Auto Updater -----"); + log.severe("Your version of CraftBukkit is known to be broken. It is strongly advised that you update to a more recent version ASAP."); + log.severe("Known issues with your version:"); + + for (String line : current.getBrokenReason().split("\n")) { + log.severe("> " + line); + } + + log.severe("Newer version " + latest.getVersion() + " (build #" + latest.getBuildNumber() + ") was released on " + latest.getCreated() + "."); + log.severe("Details: " + latest.getHtmlUrl()); + log.severe("Download: " + latest.getFile().getUrl()); + log.severe("----- ------------------- -----"); + } else if (onUpdate.contains(WARN_CONSOLE)) { + log.warning("----- Bukkit Auto Updater -----"); + log.warning("Your version of CraftBukkit is out of date. Version " + latest.getVersion() + " (build #" + latest.getBuildNumber() + ") was released on " + latest.getCreated() + "."); + log.warning("Details: " + latest.getHtmlUrl()); + log.warning("Download: " + latest.getFile().getUrl()); + log.warning("----- ------------------- -----"); + } + } else if ((current != null) && (current.isBroken()) && (onBroken.contains(WARN_CONSOLE))) { + log.severe("----- Bukkit Auto Updater -----"); + log.severe("Your version of CraftBukkit is known to be broken. It is strongly advised that you update to a more recent version ASAP."); + log.severe("Known issues with your version:"); + + for (String line : current.getBrokenReason().split("\n")) { + log.severe("> " + line); + } + + log.severe("Unfortunately, there is not yet a newer version suitable for your server. We would advise you wait an hour or two, or try out a dev build."); + log.severe("----- ------------------- -----"); + } else if ((current != null) && (shouldSuggestChannels())) { + ArtifactDetails.ChannelDetails prefChan = service.getChannel(channel, "preferred channel details"); + + if ((prefChan != null) && (current.getChannel().getPriority() < prefChan.getPriority())) { + log.info("----- Bukkit Auto Updater -----"); + log.info("It appears that you're running a " + current.getChannel().getName() + ", when you've specified in bukkit.yml that you prefer to run " + prefChan.getName() + "s."); + log.info("If you would like to be kept informed about new " + current.getChannel().getName() + " releases, it is recommended that you change 'preferred-channel' in your bukkit.yml to '" + current.getChannel().getSlug() + "'."); + log.info("With that set, you will be told whenever a new version is available for download, so that you can always keep up to date and secure with the latest fixes."); + log.info("If you would like to disable this warning, simply set 'suggest-channels' to false in bukkit.yml."); + log.info("----- ------------------- -----"); + } + } + } + }.start(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterService.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterService.java new file mode 100644 index 0000000..0145ac3 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterService.java @@ -0,0 +1,102 @@ +package org.bukkit.craftbukkit.updater; + +import com.google.gson.*; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URL; +import java.net.URLConnection; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; + +public class BukkitDLUpdaterService { + private static final String API_PREFIX_ARTIFACT = "/api/1.0/downloads/projects/craftbukkit/view/"; + private static final String API_PREFIX_CHANNEL = "/api/1.0/downloads/channels/"; + private static final DateDeserializer dateDeserializer = new DateDeserializer(); + private final String host; + + public BukkitDLUpdaterService(String host) { + this.host = host; + } + + public ArtifactDetails getArtifact(String slug, String name) { + try { + return fetchArtifact(slug); + } catch (UnsupportedEncodingException ex) { + Bukkit.getLogger().log(Level.WARNING, "Could not get " + name + ": " + ex.getClass().getSimpleName()); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.WARNING, "Could not get " + name + ": " + ex.getClass().getSimpleName()); + } + + return null; + } + + private String getUserAgent() { + return "CraftBukkit/" + BukkitDLUpdaterService.class.getPackage().getImplementationVersion() + "/" + System.getProperty("java.version"); + } + + public ArtifactDetails fetchArtifact(String slug) throws IOException { + URL url = new URL("http", host, API_PREFIX_ARTIFACT + slug + "/"); + InputStreamReader reader = null; + + try { + URLConnection connection = url.openConnection(); + connection.setRequestProperty("User-Agent", getUserAgent()); + reader = new InputStreamReader(connection.getInputStream()); + Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, dateDeserializer).setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + return gson.fromJson(reader, ArtifactDetails.class); + } finally { + if (reader != null) { + reader.close(); + } + } + } + + public ArtifactDetails.ChannelDetails getChannel(String slug, String name) { + try { + return fetchChannel(slug); + } catch (UnsupportedEncodingException ex) { + Bukkit.getLogger().log(Level.WARNING, "Could not get " + name + ": " + ex.getClass().getSimpleName()); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.WARNING, "Could not get " + name + ": " + ex.getClass().getSimpleName()); + } + + return null; + } + + public ArtifactDetails.ChannelDetails fetchChannel(String slug) throws IOException { + URL url = new URL("http", host, API_PREFIX_CHANNEL + slug + "/"); + InputStreamReader reader = null; + + try { + URLConnection connection = url.openConnection(); + connection.setRequestProperty("User-Agent", getUserAgent()); + reader = new InputStreamReader(connection.getInputStream()); + Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, dateDeserializer).setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + + return gson.fromJson(reader, ArtifactDetails.ChannelDetails.class); + } finally { + if (reader != null) { + reader.close(); + } + } + } + + static class DateDeserializer implements JsonDeserializer { + private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public Date deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException { + try { + return format.parse(je.getAsString()); + } catch (ParseException ex) { + throw new JsonParseException("Date is not formatted correctly", ex); + } + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java new file mode 100644 index 0000000..9c92ca3 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java @@ -0,0 +1,357 @@ +package org.bukkit.craftbukkit.util; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.Validate; + +/** + * Executes tasks using a multi-stage process executor. Synchronous executions are via {@link AsynchronousExecutor#finishActive()} or the {@link AsynchronousExecutor#get(Object)} methods. + *

          • Stage 1 creates the object from a parameter, and is usually called asynchronously. + *
          • Stage 2 takes the parameter and object from stage 1 and does any synchronous processing to prepare it. + *
          • Stage 3 takes the parameter and object from stage 1, as well as a callback that was registered, and performs any synchronous calculations. + * + * @param

            The type of parameter you provide to make the object that will be created. It should implement {@link Object#hashCode()} and {@link Object#equals(Object)} if you want to get the value early. + * @param The type of object you provide. This is created in stage 1, and passed to stage 2, 3, and returned if get() is called. + * @param The type of callback you provide. You may register many of these to be passed to the provider in stage 3, one at a time. + * @param A type of exception you may throw and expect to be handled by the main thread + * @author Wesley Wolfe (c) 2012, 2014 + */ +public final class AsynchronousExecutor { + + public static interface CallBackProvider extends ThreadFactory { + + /** + * Normally an asynchronous call, but can be synchronous + * + * @param parameter parameter object provided + * @return the created object + */ + T callStage1(P parameter) throws E; + + /** + * Synchronous call + * + * @param parameter parameter object provided + * @param object the previously created object + */ + void callStage2(P parameter, T object) throws E; + + /** + * Synchronous call, called multiple times, once per registered callback + * + * @param parameter parameter object provided + * @param object the previously created object + * @param callback the current callback to execute + */ + void callStage3(P parameter, T object, C callback) throws E; + } + + @SuppressWarnings("rawtypes") + static final AtomicIntegerFieldUpdater STATE_FIELD = AtomicIntegerFieldUpdater.newUpdater(AsynchronousExecutor.Task.class, "state"); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static boolean set(AsynchronousExecutor.Task $this, int expected, int value) { + return STATE_FIELD.compareAndSet($this, expected, value); + } + + class Task implements Runnable { + static final int PENDING = 0x0; + static final int STAGE_1_ASYNC = PENDING + 1; + static final int STAGE_1_SYNC = STAGE_1_ASYNC + 1; + static final int STAGE_1_COMPLETE = STAGE_1_SYNC + 1; + static final int FINISHED = STAGE_1_COMPLETE + 1; + + volatile int state = PENDING; + final P parameter; + T object; + final List callbacks = new LinkedList(); + E t = null; + + Task(final P parameter) { + this.parameter = parameter; + } + + public void run() { + if (initAsync()) { + finished.add(this); + } + } + + boolean initAsync() { + if (set(this, PENDING, STAGE_1_ASYNC)) { + boolean ret = true; + + try { + init(); + } finally { + if (set(this, STAGE_1_ASYNC, STAGE_1_COMPLETE)) { + // No one is/will be waiting + } else { + // We know that the sync thread will be waiting + synchronized (this) { + if (state != STAGE_1_SYNC) { + // They beat us to the synchronized block + this.notifyAll(); + } else { + // We beat them to the synchronized block + } + state = STAGE_1_COMPLETE; // They're already synchronized, atomic locks are not needed + } + // We want to return false, because we know a synchronous task already handled the finish() + ret = false; // Don't return inside finally; VERY bad practice. + } + } + + return ret; + } else { + return false; + } + } + + void initSync() { + if (set(this, PENDING, STAGE_1_COMPLETE)) { + // If we succeed that variable switch, good as done + init(); + } else if (set(this, STAGE_1_ASYNC, STAGE_1_SYNC)) { + // Async thread is running, but this shouldn't be likely; we need to sync to wait on them because of it. + synchronized (this) { + if (set(this, STAGE_1_SYNC, PENDING)) { // They might NOT synchronized yet, atomic lock IS needed + // We are the first into the lock + while (state != STAGE_1_COMPLETE) { + try { + this.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unable to handle interruption on " + parameter, e); + } + } + } else { + // They beat us to the synchronized block + } + } + } else { + // Async thread is not pending, the more likely situation for a task not pending + } + } + + @SuppressWarnings("unchecked") + void init() { + try { + object = provider.callStage1(parameter); + } catch (final Throwable t) { + this.t = (E) t; + } + } + + @SuppressWarnings("unchecked") + T get() throws E { + initSync(); + if (callbacks.isEmpty()) { + // 'this' is a placeholder to prevent callbacks from being empty during finish call + // See get method below + callbacks.add((C) this); + } + finish(); + return object; + } + + void finish() throws E { + switch (state) { + default: + case PENDING: + case STAGE_1_ASYNC: + case STAGE_1_SYNC: + throw new IllegalStateException("Attempting to finish unprepared(" + state + ") task(" + parameter + ")"); + case STAGE_1_COMPLETE: + try { + if (t != null) { + throw t; + } + if (callbacks.isEmpty()) { + return; + } + + final CallBackProvider provider = AsynchronousExecutor.this.provider; + final P parameter = this.parameter; + final T object = this.object; + + provider.callStage2(parameter, object); + for (C callback : callbacks) { + if (callback == this) { + // 'this' is a placeholder to prevent callbacks from being empty on a get() call + // See get method above + continue; + } + provider.callStage3(parameter, object, callback); + } + } finally { + tasks.remove(parameter); + state = FINISHED; + } + case FINISHED: + } + } + + boolean drop() { + if (set(this, PENDING, FINISHED)) { + // If we succeed that variable switch, good as forgotten + tasks.remove(parameter); + return true; + } else { + // We need the async thread to finish normally to properly dispose of the task + return false; + } + } + } + + final CallBackProvider provider; + final Queue finished = new ConcurrentLinkedQueue(); + final Map tasks = new HashMap(); + final ThreadPoolExecutor pool; + + /** + * Uses a thread pool to pass executions to the provider. + * @see AsynchronousExecutor + */ + public AsynchronousExecutor(final CallBackProvider provider, final int coreSize) { + Validate.notNull(provider, "Provider cannot be null"); + this.provider = provider; + + // We have an unbound queue size so do not need a max thread size + pool = new ThreadPoolExecutor(coreSize, Integer.MAX_VALUE, 60l, TimeUnit.SECONDS, new LinkedBlockingQueue(), provider); + } + + /** + * Adds a callback to the parameter provided, adding parameter to the queue if needed. + *

            + * This should always be synchronous. + */ + public void add(P parameter, C callback) { + Task task = tasks.get(parameter); + if (task == null) { + tasks.put(parameter, task = new Task(parameter)); + pool.execute(task); + } + task.callbacks.add(callback); + } + + /** + * This removes a particular callback from the specified parameter. + *

            + * If no callbacks remain for a given parameter, then the {@link CallBackProvider CallBackProvider's} stages may be omitted from execution. + * Stage 3 will have no callbacks, stage 2 will be skipped unless a {@link #get(Object)} is used, and stage 1 will be avoided on a best-effort basis. + *

            + * Subsequent calls to {@link #getSkipQueue(Object)} will always work. + *

            + * Subsequent calls to {@link #get(Object)} might work. + *

            + * This should always be synchronous + * @return true if no further execution for the parameter is possible, such that, no exceptions will be thrown in {@link #finishActive()} for the parameter, and {@link #get(Object)} will throw an {@link IllegalStateException}, false otherwise + * @throws IllegalStateException if parameter is not in the queue anymore + * @throws IllegalStateException if the callback was not specified for given parameter + */ + public boolean drop(P parameter, C callback) throws IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + //throw new IllegalStateException("Unknown " + parameter); + MinecraftServer.getLogger().error("Unknown " + parameter + " while running AsynchronousExecutor.drop()!"); + return false; + } + if (!task.callbacks.remove(callback)) { + throw new IllegalStateException("Unknown " + callback + " for " + parameter); + } + if (task.callbacks.isEmpty()) { + return task.drop(); + } + return false; + } + + /** + * This method attempts to skip the waiting period for said parameter. + *

            + * This should always be synchronous. + * @throws IllegalStateException if the parameter is not in the queue anymore, or sometimes if called from asynchronous thread + */ + public T get(P parameter) throws E, IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + throw new IllegalStateException("Unknown " + parameter); + } + return task.get(); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter) throws E { + return skipQueue(parameter); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C callback) throws E { + final T object = skipQueue(parameter); + provider.callStage3(parameter, object, callback); + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C...callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, Iterable callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + private T skipQueue(P parameter) throws E { + Task task = tasks.get(parameter); + if (task != null) { + return task.get(); + } + T object = provider.callStage1(parameter); + provider.callStage2(parameter, object); + return object; + } + + /** + * This is the 'heartbeat' that should be called synchronously to finish any pending tasks + */ + public void finishActive() throws E { + final Queue finished = this.finished; + while (!finished.isEmpty()) { + finished.poll().finish(); + } + } + + public void setActiveThreads(final int coreSize) { + pool.setMaximumPoolSize(coreSize); // Poweruser + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java new file mode 100644 index 0000000..30f2622 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java @@ -0,0 +1,59 @@ +package org.bukkit.craftbukkit.util; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.Block; + +import org.bukkit.World; +import org.bukkit.block.BlockState; + +public class BlockStateListPopulator { + private final World world; + private final List list; + + public BlockStateListPopulator(World world) { + this(world, new ArrayList()); + } + + public BlockStateListPopulator(World world, List list) { + this.world = world; + this.list = list; + } + + public void setTypeAndData(int x, int y, int z, Block block, int data, int light) { + BlockState state = world.getBlockAt(x, y, z).getState(); + state.setTypeId(Block.getId(block)); + state.setRawData((byte) data); + list.add(state); + } + public void setTypeId(int x, int y, int z, int type) { + BlockState state = world.getBlockAt(x, y, z).getState(); + state.setTypeId(type); + list.add(state); + } + + public void setTypeUpdate(int x, int y, int z, Block block) { + this.setType(x, y, z, block); + } + + public void setType(int x, int y, int z, Block block) { + BlockState state = world.getBlockAt(x, y, z).getState(); + state.setTypeId(Block.getId(block)); + list.add(state); + } + + public void updateList() { + for (BlockState state : list) { + state.update(true); + } + } + + public List getList() { + return list; + } + + public World getWorld() { + return world; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java new file mode 100644 index 0000000..6491b10 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java @@ -0,0 +1,130 @@ +package org.bukkit.craftbukkit.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.minecraft.server.ChatClickable; +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.ChatModifier; +import net.minecraft.server.EnumChatFormat; +import net.minecraft.server.EnumClickAction; +import net.minecraft.server.IChatBaseComponent; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; + +public final class CraftChatMessage { + private static class StringMessage { + private static final Map formatMap; + private static final Pattern INCREMENTAL_PATTERN = Pattern.compile("(" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + "[0-9a-fk-or])|(\\n)|((?:(?:https?)://)?(?:[-\\w_\\.]{2,}\\.[a-z]{2,4}.*?(?=[\\.\\?!,;:]?(?:[" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + " \\n]|$))))", Pattern.CASE_INSENSITIVE); + + static { + Builder builder = ImmutableMap.builder(); + for (EnumChatFormat format : EnumChatFormat.values()) { + builder.put(Character.toLowerCase(format.getChar()), format); + } + formatMap = builder.build(); + } + + private final List list = new ArrayList(); + private IChatBaseComponent currentChatComponent = new ChatComponentText(""); + private ChatModifier modifier = new ChatModifier(); + private final IChatBaseComponent[] output; + private int currentIndex; + private final String message; + + private StringMessage(String message) { + this.message = message; + if (message == null) { + output = new IChatBaseComponent[] { currentChatComponent }; + return; + } + list.add(currentChatComponent); + + Matcher matcher = INCREMENTAL_PATTERN.matcher(message); + String match = null; + while (matcher.find()) { + int groupId = 0; + while ((match = matcher.group(++groupId)) == null) { + // NOOP + } + appendNewComponent(matcher.start(groupId)); + switch (groupId) { + case 1: + EnumChatFormat format = formatMap.get(match.toLowerCase().charAt(1)); + if (format == EnumChatFormat.RESET) { + modifier = new ChatModifier(); + } else if (format.isFormat()) { + switch (format) { + case BOLD: + modifier.setBold(Boolean.TRUE); + break; + case ITALIC: + modifier.setItalic(Boolean.TRUE); + break; + case STRIKETHROUGH: + modifier.setStrikethrough(Boolean.TRUE); + break; + case UNDERLINE: + modifier.setUnderline(Boolean.TRUE); + break; + case RANDOM: + modifier.setRandom(Boolean.TRUE); + break; + default: + throw new AssertionError("Unexpected message format"); + } + } else { // Color resets formatting + modifier = new ChatModifier().setColor(format); + } + break; + case 2: + currentChatComponent = null; + break; + case 3: + if ( !( match.startsWith( "http://" ) || match.startsWith( "https://" ) ) ) { + match = "http://" + match; + } + modifier.setChatClickable(new ChatClickable(EnumClickAction.OPEN_URL, match)); + appendNewComponent(matcher.end(groupId)); + modifier.setChatClickable((ChatClickable) null); + } + currentIndex = matcher.end(groupId); + } + + if (currentIndex < message.length()) { + appendNewComponent(message.length()); + } + + output = list.toArray(new IChatBaseComponent[list.size()]); + } + + private void appendNewComponent(int index) { + if (index <= currentIndex) { + return; + } + IChatBaseComponent addition = new ChatComponentText(message.substring(currentIndex, index)).setChatModifier(modifier); + currentIndex = index; + modifier = modifier.clone(); + if (currentChatComponent == null) { + currentChatComponent = new ChatComponentText(""); + list.add(currentChatComponent); + } + currentChatComponent.addSibling(addition); + } + + private IChatBaseComponent[] getOutput() { + return output; + } + } + + public static IChatBaseComponent[] fromString(String message) { + return new StringMessage(message).getOutput(); + } + + private CraftChatMessage() { + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftDamageSource.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftDamageSource.java new file mode 100644 index 0000000..a8e2b5e --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftDamageSource.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit.util; + +import net.minecraft.server.DamageSource; + +// Util class to create custom DamageSources. +public final class CraftDamageSource extends DamageSource { + public static DamageSource copyOf(final DamageSource original) { + CraftDamageSource newSource = new CraftDamageSource(original.translationIndex); + + // Check ignoresArmor + if (original.ignoresArmor()) { + newSource.setIgnoreArmor(); + } + + // Check magic + if (original.isMagic()) { + newSource.setMagic(); + } + + // Check fire + if (original.isExplosion()) { + newSource.setExplosion(); + } + + return newSource; + } + + private CraftDamageSource(String identifier) { + super(identifier); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftIconCache.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftIconCache.java new file mode 100644 index 0000000..e52ef47 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftIconCache.java @@ -0,0 +1,11 @@ +package org.bukkit.craftbukkit.util; + +import org.bukkit.util.CachedServerIcon; + +public class CraftIconCache implements CachedServerIcon { + public final String value; + + public CraftIconCache(final String value) { + this.value = value; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java new file mode 100644 index 0000000..52aa5d1 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -0,0 +1,132 @@ +package org.bukkit.craftbukkit.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import net.minecraft.server.Block; +import net.minecraft.server.Blocks; +import net.minecraft.server.Item; +import net.minecraft.server.MojangsonParser; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.StatisticList; + +import org.bukkit.Achievement; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.UnsafeValues; +import org.bukkit.craftbukkit.CraftStatistic; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.StringUtil; + +@SuppressWarnings("deprecation") +public final class CraftMagicNumbers implements UnsafeValues { + public static final UnsafeValues INSTANCE = new CraftMagicNumbers(); + + private CraftMagicNumbers() {} + + public static Block getBlock(org.bukkit.block.Block block) { + return getBlock(block.getType()); + } + + @Deprecated + // A bad method for bad magic. + public static Block getBlock(int id) { + return getBlock(Material.getMaterial(id)); + } + + @Deprecated + // A bad method for bad magic. + public static int getId(Block block) { + return Block.getId(block); + } + + public static Material getMaterial(Block block) { + return Material.getMaterial(Block.getId(block)); + } + + public static Item getItem(Material material) { + // TODO: Don't use ID + Item item = Item.getById(material.getId()); + return item; + } + + @Deprecated + // A bad method for bad magic. + public static Item getItem(int id) { + return Item.getById(id); + } + + @Deprecated + // A bad method for bad magic. + public static int getId(Item item) { + return Item.getId(item); + } + + public static Material getMaterial(Item item) { + // TODO: Don't use ID + Material material = Material.getMaterial(Item.getId(item)); + + if (material == null) { + return Material.AIR; + } + + return material; + } + + public static Block getBlock(Material material) { + // TODO: Don't use ID + Block block = Block.getById(material.getId()); + + if (block == null) { + return Blocks.AIR; + } + + return block; + } + + @Override + public Material getMaterialFromInternalName(String name) { + return getMaterial((Item) Item.REGISTRY.get(name)); + } + + @Override + public List tabCompleteInternalMaterialName(String token, List completions) { + return StringUtil.copyPartialMatches(token, Item.REGISTRY.keySet(), completions); + } + + @Override + public ItemStack modifyItemStack(ItemStack stack, String arguments) { + net.minecraft.server.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + nmsStack.setTag((NBTTagCompound) MojangsonParser.parse(arguments)); + + stack.setItemMeta(CraftItemStack.getItemMeta(nmsStack)); + + return stack; + } + + @Override + public Statistic getStatisticFromInternalName(String name) { + return CraftStatistic.getBukkitStatisticByName(name); + } + + @Override + public Achievement getAchievementFromInternalName(String name) { + return CraftStatistic.getBukkitAchievementByName(name); + } + + @Override + public List tabCompleteInternalStatisticOrAchievementName(String token, List completions) { + List matches = new ArrayList(); + Iterator iterator = StatisticList.stats.iterator(); + while (iterator.hasNext()) { + String statistic = ((net.minecraft.server.Statistic) iterator.next()).name; + if (statistic.startsWith(token)) { + matches.add(statistic); + } + } + return matches; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftPotionUtils.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftPotionUtils.java new file mode 100644 index 0000000..f7a54b4 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/CraftPotionUtils.java @@ -0,0 +1,45 @@ +package org.bukkit.craftbukkit.util; + +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import net.minecraft.server.MobEffect; +import net.minecraft.server.MobEffectList; + +public final class CraftPotionUtils { + + public static PotionEffectType toBukkit(MobEffectList nms) { + return PotionEffectType.getById(nms.getId()); + } + + public static PotionEffect toBukkit(MobEffect effect) { + return new PotionEffect( + PotionEffectType.getById(effect.getEffectId()), + effect.getDuration(), + effect.getAmplifier(), + effect.isAmbient()); + } + + public static MobEffectList toNMS(PotionEffectType effect) { + return MobEffectList.byId[effect.getId()]; + } + + public static MobEffect toNMS(PotionEffect effect) { + return new MobEffect(effect.getType().getId(), + effect.getDuration(), + effect.getAmplifier(), + effect.isAmbient()); + } + + public static MobEffect cloneWithDuration(MobEffect effect, int duration) { + return new MobEffect(effect.getEffectId(), + duration, + effect.getAmplifier(), + effect.isAmbient()); + } + + public static void extendDuration(MobEffect effect, int duration) { + effect.a(cloneWithDuration(effect, duration)); + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java new file mode 100644 index 0000000..712c44f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java @@ -0,0 +1,10 @@ +package org.bukkit.craftbukkit.util; + +import java.io.File; +import java.io.FilenameFilter; + +public class DatFileFilter implements FilenameFilter { + public boolean accept(File dir, String name) { + return name.endsWith(".dat"); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ForwardLogHandler.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ForwardLogHandler.java new file mode 100644 index 0000000..4db463a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ForwardLogHandler.java @@ -0,0 +1,52 @@ +package org.bukkit.craftbukkit.util; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +public class ForwardLogHandler extends ConsoleHandler { + private Map cachedLoggers = new ConcurrentHashMap(); + + private Logger getLogger(String name) { + Logger logger = cachedLoggers.get(name); + if (logger == null) { + logger = LogManager.getLogger(name); + cachedLoggers.put(name, logger); + } + + return logger; + } + + @Override + public void publish(LogRecord record) { + Logger logger = getLogger(record.getLoggerName()); + Throwable exception = record.getThrown(); + Level level = record.getLevel(); + String message = getFormatter().formatMessage(record); + + if (level == Level.SEVERE) { + logger.error(message, exception); + } else if (level == Level.WARNING) { + logger.warn(message, exception); + } else if (level == Level.INFO) { + logger.info(message, exception); + } else if (level == Level.CONFIG) { + logger.debug(message, exception); + } else { + logger.trace(message, exception); + } + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/HashTreeSet.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/HashTreeSet.java new file mode 100644 index 0000000..e690831 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/HashTreeSet.java @@ -0,0 +1,129 @@ +package org.bukkit.craftbukkit.util; + +import org.bukkit.Bukkit; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +public class HashTreeSet implements Set { + + private HashSet hash = new HashSet(); + private TreeSet tree = new TreeSet(); + + public HashTreeSet() { + + } + + @Override + public int size() { + return hash.size(); + } + + @Override + public boolean isEmpty() { + return hash.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return hash.contains(o); + } + + @Override + public Iterator iterator() { + return new Iterator() { + + private Iterator it = tree.iterator(); + private V last; + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public V next() { + if (!Bukkit.isPrimaryThread()) { + throw new IllegalStateException("Async access of HTS iterator"); + } + return last = it.next(); + } + + @Override + public void remove() { + if (last == null) { + throw new IllegalStateException(); + } + it.remove(); + hash.remove(last); + last = null; + } + }; + } + + @Override + public Object[] toArray() { + return hash.toArray(); + } + + @Override + public Object[] toArray(Object[] a) { + return hash.toArray(a); + } + + @Override + public boolean add(V e) { + if (!Bukkit.isPrimaryThread()) { + throw new IllegalStateException("Async access of HTS add"); + } + + hash.add(e); + return tree.add(e); + } + + @Override + public boolean remove(Object o) { + if (!Bukkit.isPrimaryThread()) { + throw new IllegalStateException("Async access of HTS remove"); + } + hash.remove(o); + return tree.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return hash.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + tree.addAll(c); + return hash.addAll(c); + } + + @Override + public boolean retainAll(Collection c) { + tree.retainAll(c); + return hash.retainAll(c); + } + + @Override + public boolean removeAll(Collection c) { + tree.removeAll(c); + return hash.removeAll(c); + } + + @Override + public void clear() { + hash.clear(); + tree.clear(); + } + + public V first() { + return tree.first(); + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Java15Compat.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Java15Compat.java new file mode 100644 index 0000000..c9c5f28 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Java15Compat.java @@ -0,0 +1,34 @@ +package org.bukkit.craftbukkit.util; + +import java.lang.reflect.Array; + +public class Java15Compat { + public static T[] Arrays_copyOf(T[] original, int newLength) { + if (0 <= newLength) { + return org.bukkit.util.Java15Compat.Arrays_copyOfRange(original, 0, newLength); + } + throw new NegativeArraySizeException(); + } + + public static long[] Arrays_copyOf(long[] original, int newLength) { + if (0 <= newLength) { + return Arrays_copyOfRange(original, 0, newLength); + } + throw new NegativeArraySizeException(); + } + + private static long[] Arrays_copyOfRange(long[] original, int start, int end) { + if (original.length >= start && 0 <= start) { + if (start <= end) { + int length = end - start; + int copyLength = Math.min(length, original.length - start); + long[] copy = (long[]) Array.newInstance(original.getClass().getComponentType(), length); + System.arraycopy(original, start, copy, 0, copyLength); + return copy; + } + throw new IllegalArgumentException(); + } + throw new ArrayIndexOutOfBoundsException(); + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LazyHashSet.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LazyHashSet.java new file mode 100644 index 0000000..d106c0a --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LazyHashSet.java @@ -0,0 +1,98 @@ +package org.bukkit.craftbukkit.util; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + + +public abstract class LazyHashSet implements Set { + Set reference = null; + + public int size() { + return getReference().size(); + } + + public boolean isEmpty() { + return getReference().isEmpty(); + } + + public boolean contains(Object o) { + return getReference().contains(o); + } + + public Iterator iterator() { + return getReference().iterator(); + } + + public Object[] toArray() { + return getReference().toArray(); + } + + public T[] toArray(T[] a) { + return getReference().toArray(a); + } + + public boolean add(E o) { + return getReference().add(o); + } + + public boolean remove(Object o) { + return getReference().remove(o); + } + + public boolean containsAll(Collection c) { + return getReference().containsAll(c); + } + + public boolean addAll(Collection c) { + return getReference().addAll(c); + } + + public boolean retainAll(Collection c) { + return getReference().retainAll(c); + } + + public boolean removeAll(Collection c) { + return getReference().removeAll(c); + } + + public void clear() { + getReference().clear(); + } + + public Set getReference() { + Set reference = this.reference ; + if (reference != null) { + return reference; + } + return this.reference = makeReference(); + } + + abstract Set makeReference(); + + public boolean isLazy() { + return reference == null; + } + + @Override + public int hashCode() { + return 157 * getReference().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || this.getClass() != obj.getClass()) { + return false; + } + LazyHashSet that = (LazyHashSet) obj; + return (this.isLazy() && that.isLazy()) || this.getReference().equals(that.getReference()); + } + + @Override + public String toString() { + return getReference().toString(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LazyPlayerSet.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LazyPlayerSet.java new file mode 100644 index 0000000..ae19da4 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LazyPlayerSet.java @@ -0,0 +1,25 @@ +package org.bukkit.craftbukkit.util; + +import java.util.HashSet; +import java.util.List; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; + +import org.bukkit.entity.Player; + +public class LazyPlayerSet extends LazyHashSet { + + @Override + HashSet makeReference() { + if (reference != null) { + throw new IllegalStateException("Reference already created!"); + } + List players = MinecraftServer.getServer().getPlayerList().players; + HashSet reference = new HashSet(players.size()); + for (EntityPlayer player : players) { + reference.add(player.getBukkitEntity()); + } + return reference; + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongHash.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongHash.java new file mode 100644 index 0000000..1765c79 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongHash.java @@ -0,0 +1,15 @@ +package org.bukkit.craftbukkit.util; + +public class LongHash { + public static long toLong(int msw, int lsw) { + return ((long) msw << 32) + lsw - Integer.MIN_VALUE; + } + + public static int msw(long l) { + return (int) (l >> 32); + } + + public static int lsw(long l) { + return (int) (l & -1L) + Integer.MIN_VALUE; // Spigot - remove redundant & // Spigot Update - 20140921a + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongHashSet.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongHashSet.java new file mode 100644 index 0000000..22c96c5 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongHashSet.java @@ -0,0 +1,301 @@ +/* + Based on CompactHashSet Copyright 2011 Ontopia Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package org.bukkit.craftbukkit.util; + +import java.util.Iterator; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; + +public class LongHashSet { + private final static int INITIAL_SIZE = 3; + private final static double LOAD_FACTOR = 0.75; + + private final static long FREE = 0; + private final static long REMOVED = Long.MIN_VALUE; + + private int freeEntries; + private int elements; + private long[] values; + private int modCount; + + public LongHashSet() { + this(INITIAL_SIZE); + } + + public LongHashSet(int size) { + values = new long[(size==0 ? 1 : size)]; + elements = 0; + freeEntries = values.length; + modCount = 0; + } + + public Iterator iterator() { + return new Itr(); + } + + public int size() { + return elements; + } + + public boolean isEmpty() { + return elements == 0; + } + + public boolean contains(int msw, int lsw) { + return contains(LongHash.toLong(msw, lsw)); + } + + public boolean contains(long value) { + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % values.length; + int offset = 1; + + // search for the object (continue while !null and !this object) + while(values[index] != FREE && !(hash(values[index]) == hash && values[index] == value)) { + index = ((index + offset) & 0x7FFFFFFF) % values.length; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + return values[index] != FREE; + } + + public boolean add(int msw, int lsw) { + return add(LongHash.toLong(msw, lsw)); + } + + public boolean add(long value) { + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % values.length; + int offset = 1; + int deletedix = -1; + + // search for the object (continue while !null and !this object) + while(values[index] != FREE && !(hash(values[index]) == hash && values[index] == value)) { + // if there's a deleted object here we can put this object here, + // provided it's not in here somewhere else already + if (values[index] == REMOVED) { + deletedix = index; + } + + index = ((index + offset) & 0x7FFFFFFF) % values.length; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + if (values[index] == FREE) { + if (deletedix != -1) { // reusing a deleted cell + index = deletedix; + } else { + freeEntries--; + } + + modCount++; + elements++; + values[index] = value; + + if (1 - (freeEntries / (double) values.length) > LOAD_FACTOR) { + rehash(); + } + + return true; + } else { + return false; + } + } + + public void remove(int msw, int lsw) { + remove(LongHash.toLong(msw, lsw)); + } + + public boolean remove(long value) { + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % values.length; + int offset = 1; + + // search for the object (continue while !null and !this object) + while(values[index] != FREE && !(hash(values[index]) == hash && values[index] == value)) { + index = ((index + offset) & 0x7FFFFFFF) % values.length; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + if (values[index] != FREE) { + values[index] = REMOVED; + modCount++; + elements--; + return true; + } else { + return false; + } + } + + public void clear() { + elements = 0; + for (int ix = 0; ix < values.length; ix++) { + values[ix] = FREE; + } + + freeEntries = values.length; + modCount++; + } + + public long[] toArray() { + long[] result = new long[elements]; + long[] values = Java15Compat.Arrays_copyOf(this.values, this.values.length); + int pos = 0; + + for (long value : values) { + if (value != FREE && value != REMOVED) { + result[pos++] = value; + } + } + + return result; + } + + public long popFirst() { + for (long value : values) { + if (value != FREE && value != REMOVED) { + remove(value); + return value; + } + } + + return 0; + } + + public long[] popAll() { + long[] ret = toArray(); + clear(); + return ret; + } + + // This method copied from Murmur3, written by Austin Appleby released under Public Domain + private int hash(long value) { + value ^= value >>> 33; + value *= 0xff51afd7ed558ccdL; + value ^= value >>> 33; + value *= 0xc4ceb9fe1a85ec53L; + value ^= value >>> 33; + return (int) value; + } + + private void rehash() { + int gargagecells = values.length - (elements + freeEntries); + if (gargagecells / (double) values.length > 0.05) { + rehash(values.length); + } else { + rehash(values.length * 2 + 1); + } + } + + private void rehash(int newCapacity) { + long[] newValues = new long[newCapacity]; + + for (long value : values) { + if (value == FREE || value == REMOVED) { + continue; + } + + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % newCapacity; + int offset = 1; + + // search for the object + while (newValues[index] != FREE) { + index = ((index + offset) & 0x7FFFFFFF) % newCapacity; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + newValues[index] = value; + } + + values = newValues; + freeEntries = values.length - elements; + } + + private class Itr implements Iterator { + private int index; + private int lastReturned = -1; + private int expectedModCount; + + public Itr() { + for (index = 0; index < values.length && (values[index] == FREE || values[index] == REMOVED); index++) { + // This is just to drive the index forward to the first valid entry + } + expectedModCount = modCount; + } + + public boolean hasNext() { + return index != values.length; + } + + public Long next() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + int length = values.length; + if (index >= length) { + lastReturned = -2; + throw new NoSuchElementException(); + } + + lastReturned = index; + for (index += 1; index < length && (values[index] == FREE || values[index] == REMOVED); index++) { + // This is just to drive the index forward to the next valid entry + } + + if (values[lastReturned] == FREE) { + return FREE; + } else { + return values[lastReturned]; + } + } + + public void remove() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (lastReturned == -1 || lastReturned == -2) { + throw new IllegalStateException(); + } + + if (values[lastReturned] != FREE && values[lastReturned] != REMOVED) { + values[lastReturned] = REMOVED; + elements--; + modCount++; + expectedModCount = modCount; + } + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java new file mode 100644 index 0000000..01861cc --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java @@ -0,0 +1,424 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import static org.bukkit.craftbukkit.util.Java15Compat.Arrays_copyOf; + +@SuppressWarnings("unchecked") +public class LongObjectHashMap implements Cloneable, Serializable { + static final long serialVersionUID = 2841537710170573815L; + + private static final long EMPTY_KEY = Long.MIN_VALUE; + private static final int BUCKET_SIZE = 4096; + + private transient long[][] keys; + private transient V[][] values; + private transient int modCount; + private transient int size; + + public LongObjectHashMap() { + initialize(); + } + + public LongObjectHashMap(Map map) { + this(); + putAll(map); + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean containsKey(long key) { + return get(key) != null; + } + + public boolean containsValue(V value) { + for (V val : values()) { + if (val == value || val.equals(value)) { + return true; + } + } + + return false; + } + + public V get(long key) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] inner = keys[index]; + if (inner == null) return null; + + for (int i = 0; i < inner.length; i++) { + long innerKey = inner[i]; + if (innerKey == EMPTY_KEY) { + return null; + } else if (innerKey == key) { + return values[index][i]; + } + } + + return null; + } + + public V put(long key, V value) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] innerKeys = keys[index]; + V[] innerValues = values[index]; + modCount++; + + if (innerKeys == null) { + // need to make a new chain + keys[index] = innerKeys = new long[8]; + Arrays.fill(innerKeys, EMPTY_KEY); + values[index] = innerValues = (V[]) new Object[8]; + innerKeys[0] = key; + innerValues[0] = value; + size++; + } else { + int i; + for (i = 0; i < innerKeys.length; i++) { + // found an empty spot in the chain to put this + if (innerKeys[i] == EMPTY_KEY) { + size++; + innerKeys[i] = key; + innerValues[i] = value; + return null; + } + + // found an existing entry in the chain with this key, replace it + if (innerKeys[i] == key) { + V oldValue = innerValues[i]; + innerKeys[i] = key; + innerValues[i] = value; + return oldValue; + } + } + + // chain is full, resize it and add our new entry + keys[index] = innerKeys = Arrays_copyOf(innerKeys, i << 1); + Arrays.fill(innerKeys, i, innerKeys.length, EMPTY_KEY); + values[index] = innerValues = Arrays_copyOf(innerValues, i << 1); + innerKeys[i] = key; + innerValues[i] = value; + size++; + } + + return null; + } + + public V remove(long key) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] inner = keys[index]; + if (inner == null) { + return null; + } + + for (int i = 0; i < inner.length; i++) { + // hit the end of the chain, didn't find this entry + if (inner[i] == EMPTY_KEY) { + break; + } + + if (inner[i] == key) { + V value = values[index][i]; + + for (i++; i < inner.length; i++) { + if (inner[i] == EMPTY_KEY) { + break; + } + + inner[i - 1] = inner[i]; + values[index][i - 1] = values[index][i]; + } + + inner[i - 1] = EMPTY_KEY; + values[index][i - 1] = null; + size--; + modCount++; + return value; + } + } + + return null; + } + + public void putAll(Map map) { + for (Map.Entry entry : map.entrySet()) { + put((Long) entry.getKey(), (V) entry.getValue()); + } + } + + public void clear() { + if (size == 0) { + return; + } + + modCount++; + size = 0; + Arrays.fill(keys, null); + Arrays.fill(values, null); + } + + public Set keySet() { + return new KeySet(); + } + + public Collection values() { + return new ValueCollection(); + } + + /** + * Returns a Set of Entry objects for the HashMap. This is not how the internal + * implementation is laid out so this constructs the entire Set when called. For + * this reason it should be avoided if at all possible. + * + * @return Set of Entry objects + * @deprecated + */ + @Deprecated + public Set> entrySet() { + HashSet> set = new HashSet>(); + for (long key : keySet()) { + set.add(new Entry(key, get(key))); + } + + return set; + } + + public Object clone() throws CloneNotSupportedException { + LongObjectHashMap clone = (LongObjectHashMap) super.clone(); + // Make sure we clear any existing information from the clone + clone.clear(); + // Make sure the clone is properly setup for new entries + clone.initialize(); + + // Iterate through the data normally to do a safe clone + for (long key : keySet()) { + final V value = get(key); + clone.put(key, value); + } + + return clone; + } + + private void initialize() { + keys = new long[BUCKET_SIZE][]; + values = (V[][]) new Object[BUCKET_SIZE][]; + } + + private long keyIndex(long key) { + key ^= key >>> 33; + key *= 0xff51afd7ed558ccdL; + key ^= key >>> 33; + key *= 0xc4ceb9fe1a85ec53L; + key ^= key >>> 33; + return key; + } + + private void writeObject(ObjectOutputStream outputStream) throws IOException { + outputStream.defaultWriteObject(); + + for (long key : keySet()) { + V value = get(key); + outputStream.writeLong(key); + outputStream.writeObject(value); + } + + outputStream.writeLong(EMPTY_KEY); + outputStream.writeObject(null); + } + + private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException { + inputStream.defaultReadObject(); + initialize(); + + while (true) { + long key = inputStream.readLong(); + V value = (V) inputStream.readObject(); + if (key == EMPTY_KEY && value == null) { + break; + } + + put(key, value); + } + } + + + private class ValueIterator implements Iterator { + private int count; + private int index; + private int innerIndex; + private int expectedModCount; + private long lastReturned = EMPTY_KEY; + + long prevKey = EMPTY_KEY; + V prevValue; + + ValueIterator() { + expectedModCount = LongObjectHashMap.this.modCount; + } + + public boolean hasNext() { + return count < LongObjectHashMap.this.size; + } + + public void remove() { + if (LongObjectHashMap.this.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (lastReturned == EMPTY_KEY) { + throw new IllegalStateException(); + } + + count--; + LongObjectHashMap.this.remove(lastReturned); + lastReturned = EMPTY_KEY; + expectedModCount = LongObjectHashMap.this.modCount; + } + + public V next() { + if (LongObjectHashMap.this.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (!hasNext()) { + throw new NoSuchElementException(); + } + + long[][] keys = LongObjectHashMap.this.keys; + count++; + + if (prevKey != EMPTY_KEY) { + innerIndex++; + } + + for (; index < keys.length; index++) { + if (keys[index] != null) { + for (; innerIndex < keys[index].length; innerIndex++) { + long key = keys[index][innerIndex]; + V value = values[index][innerIndex]; + if (key == EMPTY_KEY) { + break; + } + + lastReturned = key; + prevKey = key; + prevValue = value; + return prevValue; + } + innerIndex = 0; + } + } + + throw new NoSuchElementException(); + } + } + + private class KeyIterator implements Iterator { + final ValueIterator iterator; + + public KeyIterator() { + iterator = new ValueIterator(); + } + + public void remove() { + iterator.remove(); + } + + public boolean hasNext() { + return iterator.hasNext(); + } + + public Long next() { + iterator.next(); + return iterator.prevKey; + } + } + + + private class KeySet extends AbstractSet { + public void clear() { + LongObjectHashMap.this.clear(); + } + + public int size() { + return LongObjectHashMap.this.size(); + } + + public boolean contains(Object key) { + return key instanceof Long && LongObjectHashMap.this.containsKey((Long) key); + + } + + public boolean remove(Object key) { + return LongObjectHashMap.this.remove((Long) key) != null; + } + + public Iterator iterator() { + return new KeyIterator(); + } + } + + + private class ValueCollection extends AbstractCollection { + public void clear() { + LongObjectHashMap.this.clear(); + } + + public int size() { + return LongObjectHashMap.this.size(); + } + + public boolean contains(Object value) { + return LongObjectHashMap.this.containsValue((V) value); + } + + public Iterator iterator() { + return new ValueIterator(); + } + } + + + private class Entry implements Map.Entry { + private final Long key; + private V value; + + Entry(long k, V v) { + key = k; + value = v; + } + + public Long getKey() { + return key; + } + + public V getValue() { + return value; + } + + public V setValue(V v) { + V old = value; + value = v; + put(key, v); + return old; + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/MojangNameLookup.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/MojangNameLookup.java new file mode 100644 index 0000000..1db4874 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/MojangNameLookup.java @@ -0,0 +1,63 @@ +package org.bukkit.craftbukkit.util; + +import net.minecraft.util.com.google.gson.Gson; +import net.minecraft.util.com.google.common.base.Charsets; +import net.minecraft.util.org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.UUID; + +public class MojangNameLookup { + private static final Logger logger = LogManager.getFormatterLogger(MojangNameLookup.class); + + public static String lookupName(UUID id) { + if (id == null) { + return null; + } + + InputStream inputStream = null; + try { + URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + id.toString().replace("-", "")); + URLConnection connection = url.openConnection(); + connection.setConnectTimeout(15000); + connection.setReadTimeout(15000); + connection.setUseCaches(false); + inputStream = connection.getInputStream(); + String result = IOUtils.toString(inputStream, Charsets.UTF_8); + Gson gson = new Gson(); + Response response = gson.fromJson(result, Response.class); + if (response == null || response.name == null) { + logger.warn("Failed to lookup name from UUID"); + return null; + } + + if (response.cause != null && response.cause.length() > 0) { + logger.warn("Failed to lookup name from UUID: %s", response.errorMessage); + return null; + } + + return response.name; + } catch (MalformedURLException ex) { + logger.warn("Malformed URL in UUID lookup"); + return null; + } catch (IOException ex) { + IOUtils.closeQuietly(inputStream); + } finally { + IOUtils.closeQuietly(inputStream); + } + + return null; + } + + private class Response { + String errorMessage; + String cause; + String name; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java new file mode 100644 index 0000000..ae3481b --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.util; + +import net.minecraft.server.ExceptionWorldConflict; +import net.minecraft.server.MinecraftServer; + +public class ServerShutdownThread extends Thread { + private final MinecraftServer server; + + public ServerShutdownThread(MinecraftServer server) { + this.server = server; + } + + @Override + public void run() { + try { + server.stop(); + } catch (ExceptionWorldConflict ex) { + ex.printStackTrace(); + } finally { + try { + server.reader.getTerminal().restore(); + } catch (Exception e) { + } + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ShortConsoleLogFormatter.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ShortConsoleLogFormatter.java new file mode 100644 index 0000000..2dbfef9 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/ShortConsoleLogFormatter.java @@ -0,0 +1,61 @@ +package org.bukkit.craftbukkit.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; +import joptsimple.OptionException; +import joptsimple.OptionSet; +import net.minecraft.server.MinecraftServer; + +public class ShortConsoleLogFormatter extends Formatter { + private final SimpleDateFormat date; + + public ShortConsoleLogFormatter(MinecraftServer server) { + OptionSet options = server.options; + SimpleDateFormat date = null; + + if (options.has("date-format")) { + try { + Object object = options.valueOf("date-format"); + + if ((object != null) && (object instanceof SimpleDateFormat)) { + date = (SimpleDateFormat) object; + } + } catch (OptionException ex) { + System.err.println("Given date format is not valid. Falling back to default."); + } + } else if (options.has("nojline")) { + date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + + if (date == null) { + date = new SimpleDateFormat("HH:mm:ss"); + } + + this.date = date; + } + + @Override + public String format(LogRecord record) { + StringBuilder builder = new StringBuilder(); + Throwable ex = record.getThrown(); + + builder.append(date.format(record.getMillis())); + builder.append(" ["); + builder.append(record.getLevel().getLocalizedName().toUpperCase()); + builder.append("] "); + builder.append(formatMessage(record)); + builder.append('\n'); + + if (ex != null) { + StringWriter writer = new StringWriter(); + ex.printStackTrace(new PrintWriter(writer)); + builder.append(writer); + } + + return builder.toString(); + } + +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/StructureGrowDelegate.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/StructureGrowDelegate.java new file mode 100644 index 0000000..f9cc7d6 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/StructureGrowDelegate.java @@ -0,0 +1,70 @@ +package org.bukkit.craftbukkit.util; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.Block; +import net.minecraft.server.Blocks; +import net.minecraft.server.World; + +import org.bukkit.BlockChangeDelegate; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.material.MaterialData; + +public class StructureGrowDelegate implements BlockChangeDelegate { + private final CraftWorld world; + private final List blocks = new ArrayList(); + + public StructureGrowDelegate(World world) { + this.world = world.getWorld(); + } + + public boolean setRawTypeId(int x, int y, int z, int type) { + return setRawTypeIdAndData(x, y, z, type, 0); + } + + public boolean setRawTypeIdAndData(int x, int y, int z, int type, int data) { + BlockState state = world.getBlockAt(x, y, z).getState(); + state.setTypeId(type); + state.setData(new MaterialData(type, (byte) data)); + blocks.add(state); + return true; + } + + public boolean setTypeId(int x, int y, int z, int typeId) { + return setRawTypeId(x, y, z, typeId); + } + + public boolean setTypeIdAndData(int x, int y, int z, int typeId, int data) { + return setRawTypeIdAndData(x, y, z, typeId, data); + } + + public int getTypeId(int x, int y, int z) { + for (BlockState state : blocks) { + if (state.getX() == x && state.getY() == y && state.getZ() == z) { + return state.getTypeId(); + } + } + + return world.getBlockTypeIdAt(x, y, z); + } + + public int getHeight() { + return world.getMaxHeight(); + } + + public List getBlocks() { + return blocks; + } + + public boolean isEmpty(int x, int y, int z) { + for (BlockState state : blocks) { + if (state.getX() == x && state.getY() == y && state.getZ() == z) { + return Block.getById(state.getTypeId()) == Blocks.AIR; + } + } + + return world.getBlockAt(x, y, z).isEmpty(); + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java new file mode 100644 index 0000000..0bdfde6 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java @@ -0,0 +1,52 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; +import jline.console.ConsoleReader; +import net.minecraft.util.com.mojang.util.QueueLogAppender; +import org.bukkit.craftbukkit.Main; + +public class TerminalConsoleWriterThread implements Runnable { + final private ConsoleReader reader; + final private OutputStream output; + + public TerminalConsoleWriterThread(OutputStream output, ConsoleReader reader) { + this.output = output; + this.reader = reader; + } + + public void run() { + String message; + + // Using name from log4j config in vanilla jar + while (true) { + message = QueueLogAppender.getNextLogEvent("TerminalConsole"); + if (message == null) { + continue; + } + + try { + if (Main.useJline) { + reader.print(ConsoleReader.RESET_LINE + ""); + reader.flush(); + output.write(message.getBytes()); + output.flush(); + + try { + reader.drawLine(); + } catch (Throwable ex) { + reader.getCursorBuffer().clear(); + } + reader.flush(); + } else { + output.write(message.getBytes()); + output.flush(); + } + } catch (IOException ex) { + Logger.getLogger(TerminalConsoleWriterThread.class.getName()).log(Level.SEVERE, null, ex); + } + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java new file mode 100644 index 0000000..1351ecd --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java @@ -0,0 +1,277 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.RandomAccess; + +// implementation of an ArrayList that offers a getter without range checks +@SuppressWarnings("unchecked") +public class UnsafeList extends AbstractList implements List, RandomAccess, Cloneable, Serializable { + private static final long serialVersionUID = 8683452581112892191L; + + private transient Object[] data; + private int size; + private int initialCapacity; + + private Iterator[] iterPool = new Iterator[1]; + private int maxPool; + private int poolCounter; + + public UnsafeList(int capacity, int maxIterPool) { + super(); + if (capacity < 0) capacity = 32; + int rounded = Integer.highestOneBit(capacity - 1) << 1; + data = new Object[rounded]; + initialCapacity = rounded; + maxPool = maxIterPool; + iterPool[0] = new Itr(); + } + + public UnsafeList(int capacity) { + this(capacity, 5); + } + + public UnsafeList() { + this(32); + } + + public E get(int index) { + rangeCheck(index); + + return (E) data[index]; + } + + public E unsafeGet(int index) { + return (E) data[index]; + } + + public E set(int index, E element) { + rangeCheck(index); + + E old = (E) data[index]; + data[index] = element; + return old; + } + + public boolean add(E element) { + growIfNeeded(); + data[size++] = element; + return true; + } + + public void add(int index, E element) { + growIfNeeded(); + System.arraycopy(data, index, data, index + 1, size - index); + data[index] = element; + size++; + } + + public E remove(int index) { + rangeCheck(index); + + E old = (E) data[index]; + int movedCount = size - index - 1; + if (movedCount > 0) { + System.arraycopy(data, index + 1, data, index, movedCount); + } + data[--size] = null; + + return old; + } + + public boolean remove(Object o) { + int index = indexOf(o); + if (index >= 0) { + remove(index); + return true; + } + + return false; + } + + public int indexOf(Object o) { + for (int i = 0; i < size; i++) { + if (o == data[i] || o.equals(data[i])) { + return i; + } + } + + return -1; + } + + public boolean contains(Object o) { + return indexOf(o) >= 0; + } + + public void clear() { + // Create new array to reset memory usage to initial capacity + size = 0; + + // If array has grown too large create new one, otherwise just clear it + if (data.length > initialCapacity << 3) { + data = new Object[initialCapacity]; + } else { + for (int i = 0; i < data.length; i++) { + data[i] = null; + } + } + } + + // actually rounds up to nearest power of two + public void trimToSize() { + int old = data.length; + int rounded = Integer.highestOneBit(size - 1) << 1; + if (rounded < old) { + data = Java15Compat.Arrays_copyOf(data, rounded); + } + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public Object clone() throws CloneNotSupportedException { + UnsafeList copy = (UnsafeList) super.clone(); + copy.data = Java15Compat.Arrays_copyOf(data, size); + copy.size = size; + copy.initialCapacity = initialCapacity; + copy.iterPool = new Iterator[1]; + copy.iterPool[0] = new Itr(); + copy.maxPool = maxPool; + copy.poolCounter = 0; + return copy; + } + + public Iterator iterator() { + // Try to find an iterator that isn't in use + for (Iterator iter : iterPool) { + if (!((Itr) iter).valid) { + Itr iterator = (Itr) iter; + iterator.reset(); + return iterator; + } + } + + // Couldn't find one, see if we can grow our pool size + if (iterPool.length < maxPool) { + Iterator[] newPool = new Iterator[iterPool.length + 1]; + System.arraycopy(iterPool, 0, newPool, 0, iterPool.length); + iterPool = newPool; + + iterPool[iterPool.length - 1] = new Itr(); + return iterPool[iterPool.length - 1]; + } + + // Still couldn't find a free one, round robin replace one with a new iterator + // This is done in the hope that the new one finishes so can be reused + poolCounter = ++poolCounter % iterPool.length; + iterPool[poolCounter] = new Itr(); + return iterPool[poolCounter]; + } + + private void rangeCheck(int index) { + if (index >= size || index < 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + } + } + + private void growIfNeeded() { + if (size == data.length) { + Object[] newData = new Object[data.length << 1]; + System.arraycopy(data, 0, newData, 0, size); + data = newData; + } + } + + private void writeObject(ObjectOutputStream os) throws IOException { + os.defaultWriteObject(); + + os.writeInt(size); + os.writeInt(initialCapacity); + for (int i = 0; i < size; i++) { + os.writeObject(data[i]); + } + os.writeInt(maxPool); + } + + private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { + is.defaultReadObject(); + + size = is.readInt(); + initialCapacity = is.readInt(); + data = new Object[Integer.highestOneBit(size - 1) << 1]; + for (int i = 0; i < size; i++) { + data[i] = is.readObject(); + } + maxPool = is.readInt(); + iterPool = new Iterator[1]; + iterPool[0] = new Itr(); + } + + public class Itr implements Iterator { + int index; + int lastRet = -1; + int expectedModCount = modCount; + public boolean valid = true; + + public void reset() { + index = 0; + lastRet = -1; + expectedModCount = modCount; + valid = true; + } + + public boolean hasNext() { + valid = index != size; + return valid; + } + + public E next() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + int i = index; + if (i >= size) { + throw new NoSuchElementException(); + } + + if (i >= data.length) { + throw new ConcurrentModificationException(); + } + + index = i + 1; + return (E) data[lastRet = i]; + } + + public void remove() { + if (lastRet < 0) { + throw new IllegalStateException(); + } + + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + try { + UnsafeList.this.remove(lastRet); + index = lastRet; + lastRet = -1; + expectedModCount = modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Versioning.java new file mode 100644 index 0000000..a22c285 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Versioning.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.bukkit.Bukkit; + +public final class Versioning { + public static String getBukkitVersion() { + String result = "Unknown-Version"; + + InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/net.frozenorb/sourspigot-api/pom.properties"); + Properties properties = new Properties(); + + if (stream != null) { + try { + properties.load(stream); + + result = properties.getProperty("version"); + } catch (IOException ex) { + Logger.getLogger(Versioning.class.getName()).log(Level.SEVERE, "Could not get Bukkit version!", ex); + } + } + + return result; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Waitable.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Waitable.java new file mode 100644 index 0000000..5cd1154 --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/Waitable.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.util; + +import java.util.concurrent.ExecutionException; + + +public abstract class Waitable implements Runnable { + private enum Status { + WAITING, + RUNNING, + FINISHED, + } + Throwable t = null; + T value = null; + Status status = Status.WAITING; + + public final void run() { + synchronized (this) { + if (status != Status.WAITING) { + throw new IllegalStateException("Invalid state " + status); + } + status = Status.RUNNING; + } + try { + value = evaluate(); + } catch (Throwable t) { + this.t = t; + } finally { + synchronized (this) { + status = Status.FINISHED; + this.notifyAll(); + } + } + } + + protected abstract T evaluate(); + + public synchronized T get() throws InterruptedException, ExecutionException { + while (status != Status.FINISHED) { + this.wait(); + } + if (t != null) { + throw new ExecutionException(t); + } + return value; + } +} diff --git a/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/WeakCollection.java b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/WeakCollection.java new file mode 100644 index 0000000..7e7363f --- /dev/null +++ b/vspigot-server/src/main/java/org/bukkit/craftbukkit/util/WeakCollection.java @@ -0,0 +1,169 @@ +package org.bukkit.craftbukkit.util; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.apache.commons.lang.Validate; + +public final class WeakCollection implements Collection { + static final Object NO_VALUE = new Object(); + private final Collection> collection; + + public WeakCollection() { + collection = new ArrayList>(); + } + + public boolean add(T value) { + Validate.notNull(value, "Cannot add null value"); + return collection.add(new WeakReference(value)); + } + + public boolean addAll(Collection collection) { + Collection> values = this.collection; + boolean ret = false; + for (T value : collection) { + Validate.notNull(value, "Cannot add null value"); + ret |= values.add(new WeakReference(value)); + } + return ret; + } + + public void clear() { + collection.clear(); + } + + public boolean contains(Object object) { + if (object == null) { + return false; + } + for (T compare : this) { + if (object.equals(compare)) { + return true; + } + } + return false; + } + + public boolean containsAll(Collection collection) { + return toCollection().containsAll(collection); + } + + public boolean isEmpty() { + return !iterator().hasNext(); + } + + public Iterator iterator() { + return new Iterator() { + Iterator> it = collection.iterator(); + Object value = NO_VALUE; + + public boolean hasNext() { + Object value = this.value; + if (value != null && value != NO_VALUE) { + return true; + } + + Iterator> it = this.it; + value = null; + + while (it.hasNext()) { + WeakReference ref = it.next(); + value = ref.get(); + if (value == null) { + it.remove(); + } else { + this.value = value; + return true; + } + } + return false; + } + + public T next() throws NoSuchElementException { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + + @SuppressWarnings("unchecked") + T value = (T) this.value; + this.value = NO_VALUE; + return value; + } + + public void remove() throws IllegalStateException { + if (value != NO_VALUE) { + throw new IllegalStateException("No last element"); + } + + value = null; + it.remove(); + } + }; + } + + public boolean remove(Object object) { + if (object == null) { + return false; + } + + Iterator it = this.iterator(); + while (it.hasNext()) { + if (object.equals(it.next())) { + it.remove(); + return true; + } + } + return false; + } + + public boolean removeAll(Collection collection) { + Iterator it = this.iterator(); + boolean ret = false; + while (it.hasNext()) { + if (collection.contains(it.next())) { + ret = true; + it.remove(); + } + } + return ret; + } + + public boolean retainAll(Collection collection) { + Iterator it = this.iterator(); + boolean ret = false; + while (it.hasNext()) { + if (!collection.contains(it.next())) { + ret = true; + it.remove(); + } + } + return ret; + } + + public int size() { + int s = 0; + for (T value : this) { + s++; + } + return s; + } + + public Object[] toArray() { + return this.toArray(new Object[0]); + } + + public T[] toArray(T[] array) { + return toCollection().toArray(array); + } + + private Collection toCollection() { + ArrayList collection = new ArrayList(); + for (T value : this) { + collection.add(value); + } + return collection; + } +} diff --git a/vspigot-server/src/main/java/org/github/paperspigot/PaperSpigotConfig.java b/vspigot-server/src/main/java/org/github/paperspigot/PaperSpigotConfig.java new file mode 100644 index 0000000..3d36305 --- /dev/null +++ b/vspigot-server/src/main/java/org/github/paperspigot/PaperSpigotConfig.java @@ -0,0 +1,223 @@ +package org.github.paperspigot; + +import com.google.common.base.Throwables; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +public class PaperSpigotConfig +{ + + private static final File CONFIG_FILE = new File( "config/server", "paper.yml" ); // MineHQ - Dedicated config directory + private static final String HEADER = "This is the main configuration file for PaperSpigot.\n" + + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" + + "with caution, and make sure you know what each option does before configuring.\n" + + "\n" + + "If you need help with the configuration or have any questions related to PaperSpigot,\n" + + "join us at the IRC.\n" + + "\n" + + "IRC: #paperspigot @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=PaperSpigot )\n"; + /*========================================================================*/ + static YamlConfiguration config; + static int version; + static Map commands; + /*========================================================================*/ + + static { init(); } + + public static void init() + { + config = new YamlConfiguration(); + try + { + config.load ( CONFIG_FILE ); + } catch ( IOException ex ) + { + } catch ( InvalidConfigurationException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not load paper.yml, please correct your syntax errors", ex ); + throw Throwables.propagate( ex ); + } + config.options().header( HEADER ); + config.options().copyDefaults( true ); + + commands = new HashMap(); + + version = getInt( "config-version", 6 ); + set( "config-version", 6 ); + readConfig( PaperSpigotConfig.class, null ); + } + + public static void registerCommands() + { + for ( Map.Entry entry : commands.entrySet() ) + { + MinecraftServer.getServer().server.getCommandMap().register( entry.getKey(), "PaperSpigot", entry.getValue() ); + } + } + + static void readConfig(Class clazz, Object instance) + { + for ( Method method : clazz.getDeclaredMethods() ) + { + if ( Modifier.isPrivate( method.getModifiers() ) ) + { + if ( method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE ) + { + try + { + method.setAccessible( true ); + method.invoke( instance ); + } catch ( InvocationTargetException ex ) + { + throw Throwables.propagate( ex.getCause() ); + } catch ( Exception ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Error invoking " + method, ex ); + } + } + } + } + + try + { + config.save( CONFIG_FILE ); + } catch ( IOException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not save " + CONFIG_FILE, ex ); + } + } + + private static void set(String path, Object val) + { + config.set( path, val ); + } + + private static boolean getBoolean(String path, boolean def) + { + config.addDefault( path, def ); + return config.getBoolean( path, config.getBoolean( path ) ); + } + + private static double getDouble(String path, double def) + { + config.addDefault( path, def ); + return config.getDouble( path, config.getDouble( path ) ); + } + + private static float getFloat(String path, float def) + { + config.addDefault( path, def ); + return config.getFloat( path, config.getFloat( path ) ); + } + + private static int getInt(String path, int def) + { + config.addDefault( path, def ); + return config.getInt( path, config.getInt( path ) ); + } + + private static List getList(String path, T def) + { + config.addDefault( path, def ); + return (List) config.getList( path, config.getList( path ) ); + } + + private static String getString(String path, String def) + { + config.addDefault( path, def ); + return config.getString( path, config.getString( path ) ); + } + + public static double babyZombieMovementSpeed; + private static void babyZombieMovementSpeed() + { + babyZombieMovementSpeed = getDouble( "settings.baby-zombie-movement-speed", 0.5D); // Player moves at 0.1F, for reference + } + + public static boolean asyncCatcherFeature; + private static void asyncCatcherFeature() + { + asyncCatcherFeature = getBoolean( "settings.async-plugin-bad-magic-catcher", true ); + if (!asyncCatcherFeature) { + Bukkit.getLogger().log( Level.INFO, "Disabling async plugin bad ju-ju catcher, this may be bad depending on your plugins" ); + } + } + + public static boolean interactLimitEnabled; + private static void interactLimitEnabled() + { + interactLimitEnabled = getBoolean( "settings.limit-player-interactions", true ); + if (!interactLimitEnabled) { + Bukkit.getLogger().log( Level.INFO, "Disabling player interaction limiter, your server may be more vulnerable to malicious users" ); + } + } + + public static double strengthEffectModifier; + public static double weaknessEffectModifier; + private static void effectModifiers() + { + strengthEffectModifier = getDouble( "effect-modifiers.strength", 1.3D ); + weaknessEffectModifier = getDouble( "effect-modifiers.weakness", -0.5D ); + } + + public static int maxPacketsPerPlayer; + private static void maxPacketsPerPlayer() + { + maxPacketsPerPlayer = getInt( "max-packets-per-player", 1000 ); + } + + public static boolean stackableLavaBuckets; + public static boolean stackableWaterBuckets; + public static boolean stackableMilkBuckets; + private static void stackableBuckets() + { + stackableLavaBuckets = getBoolean( "stackable-buckets.lava", false ); + stackableWaterBuckets = getBoolean( "stackable-buckets.water", false ); + stackableMilkBuckets = getBoolean( "stackable-buckets.milk", false ); + + Field maxStack; + + try { + maxStack = Material.class.getDeclaredField("maxStack"); + maxStack.setAccessible(true); + + Field modifiers = Field.class.getDeclaredField("modifiers"); + modifiers.setAccessible(true); + modifiers.setInt(maxStack, maxStack.getModifiers() & ~Modifier.FINAL); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + try { + if (stackableLavaBuckets) { + maxStack.set(Material.LAVA_BUCKET, Material.BUCKET.getMaxStackSize()); + } + + if (stackableWaterBuckets) { + maxStack.set(Material.WATER_BUCKET, Material.BUCKET.getMaxStackSize()); + } + + if (stackableMilkBuckets) { + maxStack.set(Material.MILK_BUCKET, Material.BUCKET.getMaxStackSize()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/vspigot-server/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java b/vspigot-server/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java new file mode 100644 index 0000000..cbfc9b1 --- /dev/null +++ b/vspigot-server/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java @@ -0,0 +1,284 @@ +package org.github.paperspigot; + +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; + +public class PaperSpigotWorldConfig +{ + + private final String worldName; + private final YamlConfiguration config; + private boolean verbose; + + public PaperSpigotWorldConfig(String worldName) + { + this.worldName = worldName; + this.config = PaperSpigotConfig.config; + init(); + } + + public void init() + { + this.verbose = getBoolean( "verbose", true ); + + log( "-------- World Settings For [" + worldName + "] --------" ); + PaperSpigotConfig.readConfig( PaperSpigotWorldConfig.class, this ); + } + + private void log(String s) + { + if ( verbose ) + { + Bukkit.getLogger().info( s ); + } + } + + private void set(String path, Object val) + { + config.set( "world-settings.default." + path, val ); + } + + private boolean getBoolean(String path, boolean def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getBoolean( "world-settings." + worldName + "." + path, config.getBoolean( "world-settings.default." + path ) ); + } + + private double getDouble(String path, double def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getDouble( "world-settings." + worldName + "." + path, config.getDouble( "world-settings.default." + path ) ); + } + + private int getInt(String path, int def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getInt( "world-settings." + worldName + "." + path, config.getInt( "world-settings.default." + path ) ); + } + + private float getFloat(String path, float def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getFloat( "world-settings." + worldName + "." + path, config.getFloat( "world-settings.default." + path ) ); + } + + private List getList(String path, T def) + { + config.addDefault( "world-settings.default." + path, def ); + return (List) config.getList( "world-settings." + worldName + "." + path, config.getList( "world-settings.default." + path ) ); + } + + private String getString(String path, String def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getString( "world-settings." + worldName + "." + path, config.getString( "world-settings.default." + path ) ); + } + + public boolean allowUndeadHorseLeashing; + private void allowUndeadHorseLeashing() + { + allowUndeadHorseLeashing = getBoolean( "allow-undead-horse-leashing", true ); + log( "Allow undead horse types to be leashed: " + allowUndeadHorseLeashing ); + } + + public double squidMinSpawnHeight; + public double squidMaxSpawnHeight; + private void squidSpawnHeight() + { + squidMinSpawnHeight = getDouble( "squid-spawn-height.minimum", 45.0D ); + squidMaxSpawnHeight = getDouble( "squid-spawn-height.maximum", 63.0D ); + log( "Squids will spawn between Y: " + squidMinSpawnHeight + " and Y: " + squidMaxSpawnHeight); + } + + public float playerBlockingDamageMultiplier; + private void playerBlockingDamageMultiplier() + { + playerBlockingDamageMultiplier = getFloat( "player-blocking-damage-multiplier", 0.5F ); + log( "Player blocking damage multiplier set to " + playerBlockingDamageMultiplier); + } + + public int cactusMaxHeight; + public int reedMaxHeight; + private void blockGrowthHeight() + { + cactusMaxHeight = getInt( "max-growth-height.cactus", 3 ); + reedMaxHeight = getInt( "max-growth-height.reeds", 3 ); + log( "Max height for cactus growth " + cactusMaxHeight + ". Max height for reed growth " + reedMaxHeight); + } + + public boolean invertedDaylightDetectors; + private void invertedDaylightDetectors() + { + invertedDaylightDetectors = getBoolean( "inverted-daylight-detectors", false ); + log( "Inverted Redstone Lamps: " + invertedDaylightDetectors ); + } + + public int fishingMinTicks; + public int fishingMaxTicks; + private void fishingTickRange() + { + fishingMinTicks = getInt( "fishing-time-range.MinimumTicks", 100 ); + fishingMaxTicks = getInt( "fishing-time-range.MaximumTicks", 900 ); + } + + public float blockBreakExhaustion; + public float playerSwimmingExhaustion; + private void exhaustionValues () + { + blockBreakExhaustion = getFloat( "player-exhaustion.block-break", 0.025F ); + playerSwimmingExhaustion = getFloat("player-exhaustion.swimming", 0.015F ); + } + + public Integer softDespawnDistance; + public Integer hardDespawnDistance; + private void despawnDistances() + { + softDespawnDistance = getInt( "despawn-ranges.soft", 32 ); // 32^2 = 1024, Minecraft Default + hardDespawnDistance = getInt( "despawn-ranges.hard", 128 ); // 128^2 = 16384, Minecraft Default; + + if ( softDespawnDistance > hardDespawnDistance) + { + softDespawnDistance = hardDespawnDistance; + } + + log( "Living Entity Despawn Ranges: Soft: " + softDespawnDistance + " Hard: " + hardDespawnDistance ); + + softDespawnDistance = softDespawnDistance*softDespawnDistance; + hardDespawnDistance = hardDespawnDistance*hardDespawnDistance; + } + + public boolean keepSpawnInMemory; + private void keepSpawnInMemory() + { + keepSpawnInMemory = getBoolean( "keep-spawn-loaded", true ); + log( "Keep spawn chunk loaded: " + keepSpawnInMemory ); + } + + public double fallingBlockHeightNerf; + private void fallingBlockheightNerf() + { + // Technically a little disingenuous as it applies to all falling blocks but alas, backwards compat prevails! + fallingBlockHeightNerf = getDouble( "tnt-entity-height-nerf", 0 ); + if (fallingBlockHeightNerf != 0) { + log( "TNT/Falling Block Height Limit set to Y: " + fallingBlockHeightNerf); + } + } + + public int waterOverLavaFlowSpeed; + private void waterOverLavaFlowSpeed() + { + waterOverLavaFlowSpeed = getInt( "water-over-lava-flow-speed", 5 ); + log( "Water over lava flow speed: " + waterOverLavaFlowSpeed); + } + + public boolean removeInvalidMobSpawnerTEs; + private void removeInvalidMobSpawnerTEs() + { + removeInvalidMobSpawnerTEs = getBoolean( "remove-invalid-mob-spawner-tile-entities", true ); + log( "Remove invalid mob spawner tile entities: " + removeInvalidMobSpawnerTEs ); + } + + public boolean removeUnloadedEnderPearls; + public boolean removeUnloadedTNTEntities; + public boolean removeUnloadedFallingBlocks; + private void removeUnloaded() + { + removeUnloadedEnderPearls = getBoolean( "remove-unloaded.enderpearls", true ); + removeUnloadedTNTEntities = getBoolean( "remove-unloaded.tnt-entities", true ); + removeUnloadedFallingBlocks = getBoolean( "remove-unloaded.falling-blocks", true ); + } + + public boolean boatsDropBoats; + public boolean lessPickyTorches; + public boolean disablePlayerCrits; + private void mechanicsChanges() + { + boatsDropBoats = getBoolean( "game-mechanics.boats-drop-boats", false ); + lessPickyTorches = getBoolean( "game-mechanics.less-picky-torch-placement", false ); + disablePlayerCrits = getBoolean( "game-mechanics.disable-player-crits", false); + } + + public int tickNextTickListCap; + public boolean tickNextTickListCapIgnoresRedstone; + private void tickNextTickListCap() + { + tickNextTickListCap = getInt( "tick-next-tick-list-cap", 10000 ); // Higher values will be friendlier to vanilla style mechanics (to a point) but may hurt performance + tickNextTickListCapIgnoresRedstone = getBoolean("tick-next-tick-list-cap-ignores-redstone", false); // Redstone TickNextTicks will always bypass the preceding cap. + log( "WorldServer TickNextTickList cap set at " + tickNextTickListCap ); + log( "WorldServer TickNextTickList cap always processes redstone: " + tickNextTickListCapIgnoresRedstone ); + } + + public boolean useAsyncLighting; + private void useAsyncLighting() + { + useAsyncLighting = getBoolean( "use-async-lighting", false ); + log( "World async lighting: " + useAsyncLighting ); + } + + public boolean generateCanyon; + public boolean generateCaves; + public boolean generateDungeon; + public boolean generateFortress; + public boolean generateMineshaft; + public boolean generateStronghold; + public boolean generateTemple; + public boolean generateVillage; + public boolean generateFlatBedrock; + private void generatorSettings() + { + generateCanyon = getBoolean( "generator-settings.canyon", true ); + generateCaves = getBoolean( "generator-settings.caves", true ); + generateDungeon = getBoolean( "generator-settings.dungeon", true ); + generateFortress = getBoolean( "generator-settings.fortress", true ); + generateMineshaft = getBoolean( "generator-settings.mineshaft", true ); + generateStronghold = getBoolean( "generator-settings.stronghold", true ); + generateTemple = getBoolean( "generator-settings.temple", true ); + generateVillage = getBoolean( "generator-settings.village", true ); + generateFlatBedrock = getBoolean( "generator-settings.flat-bedrock", false ); + } + + public boolean loadUnloadedEnderPearls; + public boolean loadUnloadedTNTEntities; + public boolean loadUnloadedFallingBlocks; + private void loadUnloaded() + { + loadUnloadedEnderPearls = getBoolean( "load-chunks.enderpearls", false ); + loadUnloadedTNTEntities = getBoolean( "load-chunks.tnt-entities", false ); + loadUnloadedFallingBlocks = getBoolean( "load-chunks.falling-blocks", false ); + } + + public boolean fallingBlocksCollideWithSigns; + private void fallingBlocksCollideWithSigns() + { + fallingBlocksCollideWithSigns = getBoolean( "falling-blocks-collide-with-signs", false ); + } + + public boolean disableEndCredits; + private void disableEndCredits() + { + disableEndCredits = getBoolean( "game-mechanics.disable-end-credits", false ); + } + + public boolean optimizeExplosions; + private void optimizeExplosions() + { + optimizeExplosions = getBoolean( "optimize-explosions", false ); + } + + public boolean fastDrainLava; + public boolean fastDrainWater; + private void fastDraining() + { + fastDrainLava = getBoolean( "fast-drain.lava", false ); + fastDrainWater = getBoolean( "fast-drain.water", false ); + } + + public int lavaFlowSpeedNormal; + public int lavaFlowSpeedNether; + private void lavaFlowSpeed() + { + lavaFlowSpeedNormal = getInt( "lava-flow-speed.normal", 30 ); + lavaFlowSpeedNether = getInt( "lava-flow-speed.nether", 10 ); + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/ActivationRange.java b/vspigot-server/src/main/java/org/spigotmc/ActivationRange.java new file mode 100644 index 0000000..ebe18af --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/ActivationRange.java @@ -0,0 +1,319 @@ +package org.spigotmc; + +import java.util.ArrayList; +import java.util.List; +import net.minecraft.server.AxisAlignedBB; +import net.minecraft.server.Chunk; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityAmbient; +import net.minecraft.server.EntityAnimal; +import net.minecraft.server.EntityArrow; +import net.minecraft.server.EntityComplexPart; +import net.minecraft.server.EntityCreature; +import net.minecraft.server.EntityEnderCrystal; +import net.minecraft.server.EntityEnderDragon; +import net.minecraft.server.EntityFallingBlock; // PaperSpigot +import net.minecraft.server.EntityFireball; +import net.minecraft.server.EntityFireworks; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityMonster; +import net.minecraft.server.EntityProjectile; +import net.minecraft.server.EntitySheep; +import net.minecraft.server.EntitySlime; +import net.minecraft.server.EntityTNTPrimed; +import net.minecraft.server.EntityVillager; +import net.minecraft.server.EntityWeather; +import net.minecraft.server.EntityWither; +import net.minecraft.server.MathHelper; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.World; +import org.bukkit.craftbukkit.SpigotTimings; + +public class ActivationRange +{ + + static AxisAlignedBB maxBB = AxisAlignedBB.a( 0, 0, 0, 0, 0, 0 ); + static AxisAlignedBB miscBB = AxisAlignedBB.a( 0, 0, 0, 0, 0, 0 ); + static AxisAlignedBB animalBB = AxisAlignedBB.a( 0, 0, 0, 0, 0, 0 ); + static AxisAlignedBB monsterBB = AxisAlignedBB.a( 0, 0, 0, 0, 0, 0 ); + + // Kohi - interval to update activation states + public static int INTERVAL = 10; + + /** + * Initializes an entities type on construction to specify what group this + * entity is in for activation ranges. + * + * @param entity + * @return group id + */ + public static byte initializeEntityActivationType(Entity entity) + { + if ( entity instanceof EntityMonster || entity instanceof EntitySlime ) + { + return 1; // Monster + } else if ( entity instanceof EntityCreature || entity instanceof EntityAmbient ) + { + return 2; // Animal + } else + { + return 3; // Misc + } + } + + /** + * These entities are excluded from Activation range checks. + * + * @param entity + * @param world + * @return boolean If it should always tick. + */ + public static boolean initializeEntityActivationState(Entity entity, SpigotWorldConfig config) + { + // Kohi - add EntityArrow to this list of entity classes + // We shouldn't need to, as we test for EntityProjectile, but kohi does so why not. + + if ( ( entity.activationType == 3 && config.miscActivationRange == 0 ) + || ( entity.activationType == 2 && config.animalActivationRange == 0 ) + || ( entity.activationType == 1 && config.monsterActivationRange == 0 ) + || entity instanceof EntityHuman + || entity instanceof EntityProjectile + || entity instanceof EntityArrow + || entity instanceof EntityEnderDragon + || entity instanceof EntityComplexPart + || entity instanceof EntityWither + || entity instanceof EntityFireball + || entity instanceof EntityWeather + || entity instanceof EntityTNTPrimed + || entity instanceof EntityEnderCrystal + || entity instanceof EntityFireworks ) + { + return true; + } + + return false; + } + + /** + * Utility method to grow an AABB without creating a new AABB or touching + * the pool, so we can re-use ones we have. + * + * @param target + * @param source + * @param x + * @param y + * @param z + */ + public static void growBB(AxisAlignedBB target, AxisAlignedBB source, int x, int y, int z) + { + target.a = source.a - x; + target.b = source.b - y; + target.c = source.c - z; + target.d = source.d + x; + target.e = source.e + y; + target.f = source.f + z; + } + + /** + * Find what entities are in range of the players in the world and set + * active if in range. + * + * @param world + */ + public static void activateEntities(World world) + { + if (MinecraftServer.currentTick % INTERVAL != 0) return; // Kohi - only update on our interval + + SpigotTimings.entityActivationCheckTimer.startTiming(); + final int miscActivationRange = world.spigotConfig.miscActivationRange; + final int animalActivationRange = world.spigotConfig.animalActivationRange; + final int monsterActivationRange = world.spigotConfig.monsterActivationRange; + + int maxRange = Math.max(monsterActivationRange, animalActivationRange); + maxRange = Math.max(maxRange, miscActivationRange); + maxRange = Math.min((world.spigotConfig.viewDistance << 4) - 8, maxRange); + + for (Entity player : (List) world.players) { + player.activatedTick = MinecraftServer.currentTick; + growBB( maxBB, player.boundingBox, maxRange, 256, maxRange ); + growBB( miscBB, player.boundingBox, miscActivationRange, 256, miscActivationRange ); + growBB( animalBB, player.boundingBox, animalActivationRange, 256, animalActivationRange ); + growBB( monsterBB, player.boundingBox, monsterActivationRange, 256, monsterActivationRange ); + + int i = MathHelper.floor(maxBB.a / 16.0D); + int j = MathHelper.floor(maxBB.d / 16.0D); + int k = MathHelper.floor(maxBB.c / 16.0D); + int l = MathHelper.floor(maxBB.f / 16.0D); + + Chunk chunk = null; // MineHQ + for (int i1 = i; i1 <= j; ++i1) { + for (int j1 = k; j1 <= l; ++j1) { + // MineHQ start + if ((chunk = world.getChunkIfLoaded(i1, j1)) != null) { + activateChunkEntities(chunk); + } + // MineHQ end + } + } + } + } + + /** + * Checks for the activation state of all entities in this chunk. + * + * @param chunk + */ + private static void activateChunkEntities(Chunk chunk) + { + for ( List slice : chunk.entitySlices ) + { + for ( Entity entity : slice ) + { + if ( entity.activatedTick > MinecraftServer.currentTick + INTERVAL ) + { + continue; + } + if ( entity.defaultActivationState || checkEntityImmunities( entity ) ) + { + entity.activatedTick = MinecraftServer.currentTick + INTERVAL; + continue; + } + + switch ( entity.activationType ) + { + case 1: + if ( monsterBB.b( entity.boundingBox ) ) + { + entity.activatedTick = MinecraftServer.currentTick + INTERVAL; + } + break; + case 2: + if ( animalBB.b( entity.boundingBox ) ) + { + entity.activatedTick = MinecraftServer.currentTick + INTERVAL; + } + break; + case 3: + default: + if ( miscBB.b( entity.boundingBox ) ) + { + entity.activatedTick = MinecraftServer.currentTick + INTERVAL; + } + } + } + } + } + + /** + * If an entity is not in range, do some more checks to see if we should + * give it a shot. + * + * @param entity + * @return + */ + public static boolean checkEntityImmunities(Entity entity) + { + // quick checks. + if ( entity.inWater /* isInWater */ || entity.fireTicks > 0 ) + { + return true; + } + + // Kohi - remove arrow checks, they are excluded already + if ( !entity.onGround || entity.passenger != null || entity.vehicle != null ) + { + return true; + } + // special cases. + if ( entity instanceof EntityLiving ) + { + EntityLiving living = (EntityLiving) entity; + // Kohi - remove hurtticks check, we will activate entities in their hurt routine + if ( living.attackTicks > 0 || living.getEffects().size() > 0 ) + { + return true; + } + if ( entity instanceof EntityCreature ) + { + EntityCreature creature = (EntityCreature) entity; + if ( creature.target != null ) + { + return true; + } + if ( creature.getLeashHolder() != null ) + { + return true; + } + } + if ( entity instanceof EntityVillager && ( (EntityVillager) entity ).bY() /* Getter for first boolean */ ) + { + return true; + } + if ( entity instanceof EntityAnimal ) + { + EntityAnimal animal = (EntityAnimal) entity; + if ( animal.ce() /*love*/ ) + { + return true; + } + if ( entity instanceof EntitySheep && ( (EntitySheep) entity ).isSheared() ) + { + return true; + } + } + } + return false; + } + + /** + * Checks if the entity is active for this tick. + * + * @param entity + * @return + */ + public static boolean checkIfActive(Entity entity) + { + SpigotTimings.checkIfActiveTimer.startTiming(); + + // PaperSpigot start - EAR backport + // Never safe to skip fireworks or entities not yet added to chunk and we don't skip falling blocks + if ( !entity.isAddedToChunk() || entity instanceof EntityFireworks || entity instanceof EntityFallingBlock || entity.loadChunks ) { + SpigotTimings.checkIfActiveTimer.stopTiming(); + return true; + } + // PaperSpigot end + + boolean isActive = entity.activatedTick >= MinecraftServer.currentTick || entity.defaultActivationState; + + // Kohi - if tps is less than 17 don't activate entities 2/3 of the time + if ( isActive && !entity.defaultActivationState && MinecraftServer.getServer().recentTps[0] < 17.0 && entity.ticksLived % 3 != 0 ) + { + isActive = false; + } + + // Kohi - activate entities with a 1 in 20 chance randomly + if ( !isActive && entity.world.random.nextInt( 20 ) == 0 ) + { + isActive = true; + // and check immunities + if ( checkEntityImmunities( entity ) ) + { + entity.activatedTick = MinecraftServer.currentTick + 40; + } + } + + // Kohi - remove immunity checks and other things that were here + + int x = MathHelper.floor( entity.locX ); + int z = MathHelper.floor( entity.locZ ); + // Make sure not on edge of unloaded chunk + Chunk chunk = entity.world.getChunkIfLoaded( x >> 4, z >> 4 ); + if ( isActive && !( chunk != null && chunk.areNeighborsLoaded( 1 ) ) ) + { + isActive = false; + } + SpigotTimings.checkIfActiveTimer.stopTiming(); + return isActive; + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/AntiXray.java b/vspigot-server/src/main/java/org/spigotmc/AntiXray.java new file mode 100644 index 0000000..80e538c --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/AntiXray.java @@ -0,0 +1,257 @@ +package org.spigotmc; + +import net.minecraft.util.gnu.trove.set.TByteSet; +import net.minecraft.util.gnu.trove.set.hash.TByteHashSet; +import net.minecraft.server.Block; +import net.minecraft.server.Blocks; +import net.minecraft.server.World; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class AntiXray +{ + + private static final CustomTimingsHandler update = new CustomTimingsHandler( "xray - update" ); + private static final CustomTimingsHandler obfuscate = new CustomTimingsHandler( "xray - obfuscate" ); + /*========================================================================*/ + // Used to keep track of which blocks to obfuscate + private final boolean[] obfuscateBlocks = new boolean[ Short.MAX_VALUE ]; + // Used to select a random replacement ore + private final byte[] replacementOres; + + public AntiXray(SpigotWorldConfig config) + { + // Set all listed blocks as true to be obfuscated + for ( int id : ( config.engineMode == 1 ) ? config.hiddenBlocks : config.replaceBlocks ) + { + obfuscateBlocks[id] = true; + } + + // For every block + TByteSet blocks = new TByteHashSet(); + for ( Integer i : config.hiddenBlocks ) + { + Block block = Block.getById( i ); + // Check it exists and is not a tile entity + if ( block != null && !block.isTileEntity() ) + { + // Add it to the set of replacement blocks + blocks.add( (byte) (int) i ); + } + } + // Bake it to a flat array of replacements + replacementOres = blocks.toArray(); + } + + /** + * Starts the timings handler, then updates all blocks within the set radius + * of the given coordinate, revealing them if they are hidden ores. + */ + public void updateNearbyBlocks(World world, int x, int y, int z) + { + if ( world.spigotConfig.antiXray ) + { + update.startTiming(); + updateNearbyBlocks( world, x, y, z, 2, false ); // 2 is the radius, we shouldn't change it as that would make it exponentially slower + update.stopTiming(); + } + } + + /** + * Starts the timings handler, and then removes all non exposed ores from + * the chunk buffer. + */ + public void obfuscateSync(int chunkX, int chunkY, int bitmask, byte[] buffer, World world) + { + if ( world.spigotConfig.antiXray ) + { + obfuscate.startTiming(); + obfuscate( chunkX, chunkY, bitmask, buffer, world, false ); + obfuscate.stopTiming(); + } + } + + /** + * Removes all non exposed ores from the chunk buffer. + */ + public void obfuscate(int chunkX, int chunkY, int bitmask, byte[] buffer, World world, boolean newFormat) + { + // If the world is marked as obfuscated + if ( world.spigotConfig.antiXray ) + { + // Initial radius to search around for air + int initialRadius = 1; + // Which block in the buffer we are looking at, anywhere from 0 to 16^4 + int index = 0; + // The iterator marking which random ore we should use next + int randomOre = 0; + + // Chunk corner X and Z blocks + int startX = chunkX << 4; + int startZ = chunkY << 4; + + byte replaceWithTypeId; + switch ( world.getWorld().getEnvironment() ) + { + case NETHER: + replaceWithTypeId = (byte) CraftMagicNumbers.getId(Blocks.NETHERRACK); + break; + case THE_END: + replaceWithTypeId = (byte) CraftMagicNumbers.getId(Blocks.WHITESTONE); + break; + default: + replaceWithTypeId = (byte) CraftMagicNumbers.getId(Blocks.STONE); + break; + } + + // Chunks can have up to 16 sections + for ( int i = 0; i < 16; i++ ) + { + // If the bitmask indicates this chunk is sent... + if ( ( bitmask & 1 << i ) != 0 ) + { + // Work through all blocks in the chunk, y,z,x + for ( int y = 0; y < 16; y++ ) + { + for ( int z = 0; z < 16; z++ ) + { + for ( int x = 0; x < 16; x++ ) + { + // For some reason we can get too far ahead of ourselves (concurrent modification on bulk chunks?) so if we do, just abort and move on + if ( index >= buffer.length ) + { + index++; + if ( newFormat ) index++; + continue; + } + // Grab the block ID in the buffer. + // TODO: extended IDs are not yet supported + int blockId; + int data = 0; + if ( newFormat ) + { + blockId = (buffer[ index ] & 0xFF) | ( ( buffer[ index + 1 ] & 0xFF ) << 8 ); + data = blockId & 0xF; + blockId >>>= 4; // Remove data value + } else + { + blockId = buffer[ index ] & 0xFF; + } + // Check if the block should be obfuscated + if ( obfuscateBlocks[blockId] ) + { + // The world isn't loaded, bail out + if ( !isLoaded( world, startX + x, ( i << 4 ) + y, startZ + z, initialRadius ) ) + { + index++; + if ( newFormat ) index++; + continue; + } + // On the otherhand, if radius is 0, or the nearby blocks are all non air, we can obfuscate + if ( !hasTransparentBlockAdjacent( world, startX + x, ( i << 4 ) + y, startZ + z, initialRadius ) ) + { + switch ( world.spigotConfig.engineMode ) + { + case 1: + // Replace with replacement material + if ( newFormat ) + { + char replace = (char) ((replaceWithTypeId << 4) | data); + buffer[ index ] = (byte) ( replace & 0xFF ); + buffer[ index + 1 ] = (byte) ( ( replace >> 8 ) & 0xFF ); + } else + { + buffer[ index ] = replaceWithTypeId; + } + break; + case 2: + // Replace with random ore. + if ( randomOre >= replacementOres.length ) + { + randomOre = 0; + } + if ( newFormat ) + { + char replace = (char) (replacementOres[ randomOre++ ] & 0xFF); + replace = (char) ((replace << 4) | data); + buffer[ index ] = (byte) ( replace & 0xFF ); + buffer[ index + 1 ] = (byte) ( ( replace >> 8 ) & 0xFF ); + } else + { + buffer[ index ] = replacementOres[ randomOre++ ]; + } + break; + } + } + } + + index++; + if (newFormat) index++; + } + } + } + } + } + } + } + + private void updateNearbyBlocks(World world, int x, int y, int z, int radius, boolean updateSelf) + { + // If the block in question is loaded + if ( world.isLoaded( x, y, z ) ) + { + // Get block id + Block block = world.getType(x, y, z); + + // See if it needs update + if ( updateSelf && obfuscateBlocks[Block.getId( block )] ) + { + // Send the update + world.notify( x, y, z ); + } + + // Check other blocks for updates + if ( radius > 0 ) + { + updateNearbyBlocks( world, x + 1, y, z, radius - 1, true ); + updateNearbyBlocks( world, x - 1, y, z, radius - 1, true ); + updateNearbyBlocks( world, x, y + 1, z, radius - 1, true ); + updateNearbyBlocks( world, x, y - 1, z, radius - 1, true ); + updateNearbyBlocks( world, x, y, z + 1, radius - 1, true ); + updateNearbyBlocks( world, x, y, z - 1, radius - 1, true ); + } + } + } + + private static boolean isLoaded(World world, int x, int y, int z, int radius) + { + return world.isLoaded( x, y, z ) + && ( radius == 0 || + ( isLoaded( world, x + 1, y, z, radius - 1 ) + && isLoaded( world, x - 1, y, z, radius - 1 ) + && isLoaded( world, x, y + 1, z, radius - 1 ) + && isLoaded( world, x, y - 1, z, radius - 1 ) + && isLoaded( world, x, y, z + 1, radius - 1 ) + && isLoaded( world, x, y, z - 1, radius - 1 ) ) ); + } + + private static boolean hasTransparentBlockAdjacent(World world, int x, int y, int z, int radius) + { + return !isSolidBlock(world.getType(x, y, z, false)) /* isSolidBlock */ + || ( radius > 0 + && ( hasTransparentBlockAdjacent( world, x + 1, y, z, radius - 1 ) + || hasTransparentBlockAdjacent( world, x - 1, y, z, radius - 1 ) + || hasTransparentBlockAdjacent( world, x, y + 1, z, radius - 1 ) + || hasTransparentBlockAdjacent( world, x, y - 1, z, radius - 1 ) + || hasTransparentBlockAdjacent( world, x, y, z + 1, radius - 1 ) + || hasTransparentBlockAdjacent( world, x, y, z - 1, radius - 1 ) ) ); + } + + private static boolean isSolidBlock(Block block) { + // Mob spawners are treated as solid blocks as far as the + // game is concerned for lighting and other tasks but for + // rendering they can be seen through therefor we special + // case them so that the antixray doesn't show the fake + // blocks around them. + return block.r() && block != Blocks.MOB_SPAWNER; + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/AsyncCatcher.java b/vspigot-server/src/main/java/org/spigotmc/AsyncCatcher.java new file mode 100644 index 0000000..f7e8d16 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/AsyncCatcher.java @@ -0,0 +1,17 @@ +package org.spigotmc; + +import net.minecraft.server.MinecraftServer; + +public class AsyncCatcher +{ + + public static boolean enabled = org.github.paperspigot.PaperSpigotConfig.asyncCatcherFeature; // PaperSpigot - Allow disabling of AsyncCatcher from PaperSpigotConfig + + public static void catchOp(String reason) + { + if ( enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread ) + { + throw new IllegalStateException( "Asynchronous " + reason + "!" ); + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/CachedMojangAPIConnection.java b/vspigot-server/src/main/java/org/spigotmc/CachedMojangAPIConnection.java new file mode 100644 index 0000000..b583719 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/CachedMojangAPIConnection.java @@ -0,0 +1,145 @@ +package org.spigotmc; + +import com.google.common.base.Charsets; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import net.minecraft.util.com.google.common.cache.Cache; +import net.minecraft.util.com.google.common.cache.CacheBuilder; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.Proxy; +import java.net.URL; +import java.util.concurrent.TimeUnit; + +public class CachedMojangAPIConnection extends HttpURLConnection +{ + private final CachedStreamHandlerFactory.CachedStreamHandler cachedStreamHandler; + private final Proxy proxy; + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + private ByteArrayInputStream inputStream; + private InputStream errorStream; + private boolean outClosed = false; + + private static final Cache cache = CacheBuilder.newBuilder() + .maximumSize( 10000 ) + .expireAfterAccess( 30, TimeUnit.MINUTES ) + .build(); + + public CachedMojangAPIConnection(CachedStreamHandlerFactory.CachedStreamHandler cachedStreamHandler, URL url, Proxy proxy) + { + super( url ); + this.cachedStreamHandler = cachedStreamHandler; + this.proxy = proxy; + } + + @Override + public void disconnect() + { + + } + + @Override + public boolean usingProxy() + { + return proxy != null; + } + + @Override + public void connect() throws IOException + { + + } + + @Override + public InputStream getInputStream() throws IOException + { + if ( inputStream == null ) + { + outClosed = true; + JsonArray users = new JsonParser().parse( new String( outputStream.toByteArray(), Charsets.UTF_8 ) ).getAsJsonArray(); + StringBuilder reply = new StringBuilder( "[" ); + StringBuilder missingUsers = new StringBuilder( "[" ); + for ( JsonElement user : users ) + { + String username = user.getAsString().toLowerCase(); + String info = cache.getIfPresent( username ); + if ( info != null ) + { + reply.append( info ).append( "," ); + } else + { + missingUsers + .append( "\"" ) + .append( username ) + .append( "\"" ) + .append( "," ); + } + } + if ( missingUsers.length() > 1 ) + { + missingUsers.deleteCharAt( missingUsers.length() - 1 ).append( "]" ); + } + if ( missingUsers.length() > 2 ) + { + HttpURLConnection connection; + if ( proxy == null ) + { + connection = (HttpURLConnection) cachedStreamHandler.getDefaultConnection( url ); + } else + { + connection = (HttpURLConnection) cachedStreamHandler.getDefaultConnection( url, proxy ); + } + connection.setRequestMethod( "POST" ); + connection.setRequestProperty( "Content-Type", "application/json" ); + connection.setDoInput( true ); + connection.setDoOutput( true ); + OutputStream out = connection.getOutputStream(); + out.write( missingUsers.toString().getBytes( Charsets.UTF_8 ) ); + out.flush(); + out.close(); + JsonArray newUsers = new JsonParser().parse( new InputStreamReader( connection.getInputStream(), Charsets.UTF_8 ) ).getAsJsonArray(); + for ( JsonElement user : newUsers ) + { + JsonObject u = user.getAsJsonObject(); + cache.put( u.get( "name" ).getAsString(), u.toString() ); + reply.append( u.toString() ).append( "," ); + } + responseCode = connection.getResponseCode(); + errorStream = connection.getErrorStream(); + } else + { + responseCode = HTTP_OK; + } + if ( reply.length() > 1 ) + { + reply.deleteCharAt( reply.length() - 1 ); + } + inputStream = new ByteArrayInputStream( reply.append( "]" ).toString().getBytes( Charsets.UTF_8 ) ); + } + return inputStream; + } + + @Override + public InputStream getErrorStream() + { + return errorStream; + } + + @Override + public OutputStream getOutputStream() throws IOException + { + if ( outClosed ) + { + throw new RuntimeException( "Write after send" ); + } + return outputStream; + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/CachedStreamHandlerFactory.java b/vspigot-server/src/main/java/org/spigotmc/CachedStreamHandlerFactory.java new file mode 100644 index 0000000..b9a8736 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/CachedStreamHandlerFactory.java @@ -0,0 +1,117 @@ +package org.spigotmc; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; + +public class CachedStreamHandlerFactory implements URLStreamHandlerFactory +{ + public static boolean isSet = false; + + @Override + public URLStreamHandler createURLStreamHandler(String protocol) + { + if ( protocol.equals( "http" ) || protocol.equals( "https" ) ) + { + return new CachedStreamHandler( protocol ); + } + return null; + } + + public class CachedStreamHandler extends URLStreamHandler + { + private final String protocol; + private final URLStreamHandler handler; + private final Method openCon; + private final Method openConProxy; + + public CachedStreamHandler(String protocol) + { + this.protocol = protocol; + if ( protocol.equals( "http" ) ) + { + handler = new sun.net.www.protocol.http.Handler(); + } else + { + handler = new sun.net.www.protocol.https.Handler(); + } + try + { + openCon = handler.getClass().getDeclaredMethod( "openConnection", URL.class ); + openCon.setAccessible( true ); + openConProxy = handler.getClass().getDeclaredMethod( "openConnection", URL.class, Proxy.class ); + openConProxy.setAccessible( true ); + } catch ( NoSuchMethodException e ) + { + throw new RuntimeException( e ); + } + } + + @Override + protected URLConnection openConnection(URL u) throws IOException + { + if ( u.getHost().equals( "api.mojang.com" ) + || u.getPath().startsWith( "/profiles/minecraft" ) ) + { + return cachedConnection( u ); + } + return getDefaultConnection( u ); + } + + @Override + protected URLConnection openConnection(URL u, Proxy p) throws IOException + { + if ( u.getHost().equals( "api.mojang.com" ) + || u.getPath().startsWith( "/profiles/minecraft" ) ) + { + return cachedConnection( u, p ); + } + return getDefaultConnection( u, p ); + } + + private URLConnection cachedConnection(URL u) + { + return cachedConnection( u, null ); + } + + private URLConnection cachedConnection(URL u, Proxy p) + { + return new CachedMojangAPIConnection( this, u, p ); + } + + public URLConnection getDefaultConnection(URL u) + { + try + { + return (URLConnection) openCon.invoke( handler, u ); + } catch ( IllegalAccessException e ) + { + e.printStackTrace(); + } catch ( InvocationTargetException e ) + { + e.printStackTrace(); + } + return null; + } + + public URLConnection getDefaultConnection(URL u, Proxy p) + { + try + { + return (URLConnection) openConProxy.invoke( handler, u, p ); + } catch ( IllegalAccessException e ) + { + e.printStackTrace(); + } catch ( InvocationTargetException e ) + { + e.printStackTrace(); + } + return null; + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/LimitStream.java b/vspigot-server/src/main/java/org/spigotmc/LimitStream.java new file mode 100644 index 0000000..1804518 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/LimitStream.java @@ -0,0 +1,39 @@ +package org.spigotmc; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import net.minecraft.server.NBTReadLimiter; + +public class LimitStream extends FilterInputStream +{ + + private final NBTReadLimiter limit; + + public LimitStream(InputStream is, NBTReadLimiter limit) + { + super( is ); + this.limit = limit; + } + + @Override + public int read() throws IOException + { + limit.a( 8 ); // PaperSpigot - backport security fix + return super.read(); + } + + @Override + public int read(byte[] b) throws IOException + { + limit.a( b.length * 8 ); // PaperSpigot - backport security fix + return super.read( b ); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + limit.a( len * 8 ); // PaperSpigot - backport security fix + return super.read( b, off, len ); + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/Metrics.java b/vspigot-server/src/main/java/org/spigotmc/Metrics.java new file mode 100644 index 0000000..6e46049 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/Metrics.java @@ -0,0 +1,645 @@ +/* + * Copyright 2011-2013 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ +package org.spigotmc; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.scheduler.BukkitTask; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import net.minecraft.server.MinecraftServer; + +/** + *

            The metrics class obtains data about a plugin and submits statistics about it to the metrics backend.

            + * Public methods provided by this class:

            + * + * Graph createGraph(String name);
            + * void addCustomData(BukkitMetrics.Plotter plotter);
            + * void start();
            + *
            + */ +public class Metrics { + + /** + * The current revision number + */ + private final static int REVISION = 6; + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "http://mcstats.org"; + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/report/%s"; + /** + * The separator to use for custom data. This MUST NOT change unless you are hosting your own version of metrics and + * want to change it. + */ + private static final String CUSTOM_DATA_SEPARATOR = "~~"; + /** + * Interval of time to ping (in minutes) + */ + private static final int PING_INTERVAL = 10; + /** + * All of the custom graphs to submit to metrics + */ + private final Set graphs = Collections.synchronizedSet(new HashSet()); + /** + * The default graph, used for addCustomData when you don't want a specific graph + */ + private final Graph defaultGraph = new Graph("Default"); + /** + * The plugin configuration file + */ + private final YamlConfiguration configuration; + /** + * The plugin configuration file + */ + private final File configurationFile; + /** + * Unique server id + */ + private final String guid; + /** + * Debug mode + */ + private final boolean debug; + /** + * Lock for synchronization + */ + private final Object optOutLock = new Object(); + /** + * The scheduled task + */ + private volatile Timer task = null; + + public Metrics() throws IOException { + // load the config + configurationFile = getConfigFile(); + configuration = YamlConfiguration.loadConfiguration(configurationFile); + + // add some defaults + configuration.addDefault("opt-out", false); + configuration.addDefault("guid", UUID.randomUUID().toString()); + configuration.addDefault("debug", false); + + // Do we need to create the file? + if (configuration.get("guid", null) == null) { + configuration.options().header("http://mcstats.org").copyDefaults(true); + configuration.save(configurationFile); + } + + // Load the guid then + guid = configuration.getString("guid"); + debug = configuration.getBoolean("debug", false); + } + + /** + * Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics + * website. Plotters can be added to the graph object returned. + * + * @param name The name of the graph + * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given + */ + public Graph createGraph(final String name) { + if (name == null) { + throw new IllegalArgumentException("Graph name cannot be null"); + } + + // Construct the graph object + final Graph graph = new Graph(name); + + // Now we can add our graph + graphs.add(graph); + + // and return back + return graph; + } + + /** + * Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend + * + * @param graph The name of the graph + */ + public void addGraph(final Graph graph) { + if (graph == null) { + throw new IllegalArgumentException("Graph cannot be null"); + } + + graphs.add(graph); + } + + /** + * Adds a custom data plotter to the default graph + * + * @param plotter The plotter to use to plot custom data + */ + public void addCustomData(final Plotter plotter) { + if (plotter == null) { + throw new IllegalArgumentException("Plotter cannot be null"); + } + + // Add the plotter to the graph o/ + defaultGraph.addPlotter(plotter); + + // Ensure the default graph is included in the submitted graphs + graphs.add(defaultGraph); + } + + /** + * Start measuring statistics. This will immediately create an async repeating task as the plugin and send the + * initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200 + * ticks. + * + * @return True if statistics measuring is running, otherwise false. + */ + public boolean start() { + synchronized (optOutLock) { + // Did we opt out? + if (isOptOut()) { + return false; + } + + // Is metrics already running? + if (task != null) { + return true; + } + + // Begin hitting the server with glorious data + task = new Timer("Spigot Metrics Thread", true); + + task.scheduleAtFixedRate(new TimerTask() { + private boolean firstPost = true; + + public void run() { + try { + // This has to be synchronized or it can collide with the disable method. + synchronized (optOutLock) { + // Disable Task, if it is running and the server owner decided to opt-out + if (isOptOut() && task != null) { + task.cancel(); + task = null; + // Tell all plotters to stop gathering information. + for (Graph graph : graphs) { + graph.onOptOut(); + } + } + } + + // We use the inverse of firstPost because if it is the first time we are posting, + // it is not a interval ping, so it evaluates to FALSE + // Each time thereafter it will evaluate to TRUE, i.e PING! + postPlugin(!firstPost); + + // After the first post we set firstPost to false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } + } + } + }, 0, TimeUnit.MINUTES.toMillis(PING_INTERVAL)); + + return true; + } + } + + /** + * Has the server owner denied plugin metrics? + * + * @return true if metrics should be opted out of it + */ + public boolean isOptOut() { + synchronized (optOutLock) { + try { + // Reload the metrics file + configuration.load(getConfigFile()); + } catch (IOException ex) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } + return true; + } catch (InvalidConfigurationException ex) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } + return true; + } + return configuration.getBoolean("opt-out", false); + } + } + + /** + * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. + * + * @throws java.io.IOException + */ + public void enable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.set("opt-out", false); + configuration.save(configurationFile); + } + + // Enable Task, if it is not running + if (task == null) { + start(); + } + } + } + + /** + * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. + * + * @throws java.io.IOException + */ + public void disable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (!isOptOut()) { + configuration.set("opt-out", true); + configuration.save(configurationFile); + } + + // Disable Task, if it is running + if (task != null) { + task.cancel(); + task = null; + } + } + } + + /** + * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use + // is to abuse the plugin object we already have + // plugin.getDataFolder() => base/plugins/PluginA/ + // pluginsFolder => base/plugins/ + // The base is not necessarily relative to the startup directory. + // File pluginsFolder = plugin.getDataFolder().getParentFile(); + + // return => base/plugins/PluginMetrics/config.yml + return new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "PluginMetrics"), "config.yml"); + } + + /** + * Generic method that posts a plugin to the metrics website + */ + private void postPlugin(final boolean isPing) throws IOException { + // Server software specific section + String pluginName = "PaperSpigot"; // PaperSpigot - Glorious usage data + boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled + String pluginVersion = (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"; + String serverVersion = Bukkit.getVersion(); + int playersOnline = Bukkit.getServer().getOnlinePlayers().size(); + + // END server software specific section -- all code below does not use any code outside of this class / Java + + // Construct the post data + final StringBuilder data = new StringBuilder(); + + // The plugin's description file containg all of the plugin data such as name, version, author, etc + data.append(encode("guid")).append('=').append(encode(guid)); + encodeDataPair(data, "version", pluginVersion); + encodeDataPair(data, "server", serverVersion); + encodeDataPair(data, "players", Integer.toString(playersOnline)); + encodeDataPair(data, "revision", String.valueOf(REVISION)); + + // New data as of R6 + String osname = System.getProperty("os.name"); + String osarch = System.getProperty("os.arch"); + String osversion = System.getProperty("os.version"); + String java_version = System.getProperty("java.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + // normalize os arch .. amd64 -> x86_64 + if (osarch.equals("amd64")) { + osarch = "x86_64"; + } + + encodeDataPair(data, "osname", osname); + encodeDataPair(data, "osarch", osarch); + encodeDataPair(data, "osversion", osversion); + encodeDataPair(data, "cores", Integer.toString(coreCount)); + encodeDataPair(data, "online-mode", Boolean.toString(onlineMode)); + encodeDataPair(data, "java_version", java_version); + + // If we're pinging, append it + if (isPing) { + encodeDataPair(data, "ping", "true"); + } + + // Acquire a lock on the graphs, which lets us make the assumption we also lock everything + // inside of the graph (e.g plotters) + synchronized (graphs) { + final Iterator iter = graphs.iterator(); + + while (iter.hasNext()) { + final Graph graph = iter.next(); + + for (Plotter plotter : graph.getPlotters()) { + // The key name to send to the metrics server + // The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top + // Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME + final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName()); + + // The value to send, which for the foreseeable future is just the string + // value of plotter.getValue() + final String value = Integer.toString(plotter.getValue()); + + // Add it to the http post data :) + encodeDataPair(data, key, value); + } + } + } + + // Create the url + URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(pluginName))); + + // Connect to the website + URLConnection connection; + + // Mineshafter creates a socks proxy, so we can safely bypass it + // It does not reroute POST requests so we need to go around it + if (isMineshafterPresent()) { + connection = url.openConnection(Proxy.NO_PROXY); + } else { + connection = url.openConnection(); + } + + connection.setDoOutput(true); + + // Write the data + final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); + writer.write(data.toString()); + writer.flush(); + + // Now read the response + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + final String response = reader.readLine(); + + // close resources + writer.close(); + reader.close(); + + if (response == null || response.startsWith("ERR")) { + throw new IOException(response); //Throw the exception + } else { + // Is this the first update this hour? + if (response.contains("OK This is your first update this hour")) { + synchronized (graphs) { + final Iterator iter = graphs.iterator(); + + while (iter.hasNext()) { + final Graph graph = iter.next(); + + for (Plotter plotter : graph.getPlotters()) { + plotter.reset(); + } + } + } + } + } + } + + /** + * Check if mineshafter is present. If it is, we need to bypass it to send POST requests + * + * @return true if mineshafter is installed on the server + */ + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } + + /** + *

            Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first key/value pair + * MUST be included manually, e.g:

            + * + * StringBuffer data = new StringBuffer(); + * data.append(encode("guid")).append('=').append(encode(guid)); + * encodeDataPair(data, "version", description.getVersion()); + * + * + * @param buffer the stringbuilder to append the data pair onto + * @param key the key value + * @param value the value + */ + private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { + buffer.append('&').append(encode(key)).append('=').append(encode(value)); + } + + /** + * Encode text as UTF-8 + * + * @param text the text to encode + * @return the encoded text, as UTF-8 + */ + private static String encode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + + /** + * Represents a custom graph on the website + */ + public static class Graph { + + /** + * The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is + * rejected + */ + private final String name; + /** + * The set of plotters that are contained within this graph + */ + private final Set plotters = new LinkedHashSet(); + + private Graph(final String name) { + this.name = name; + } + + /** + * Gets the graph's name + * + * @return the Graph's name + */ + public String getName() { + return name; + } + + /** + * Add a plotter to the graph, which will be used to plot entries + * + * @param plotter the plotter to add to the graph + */ + public void addPlotter(final Plotter plotter) { + plotters.add(plotter); + } + + /** + * Remove a plotter from the graph + * + * @param plotter the plotter to remove from the graph + */ + public void removePlotter(final Plotter plotter) { + plotters.remove(plotter); + } + + /** + * Gets an unmodifiable set of the plotter objects in the graph + * + * @return an unmodifiable {@link java.util.Set} of the plotter objects + */ + public Set getPlotters() { + return Collections.unmodifiableSet(plotters); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(final Object object) { + if (!(object instanceof Graph)) { + return false; + } + + final Graph graph = (Graph) object; + return graph.name.equals(name); + } + + /** + * Called when the server owner decides to opt-out of BukkitMetrics while the server is running. + */ + protected void onOptOut() { + } + } + + /** + * Interface used to collect custom data for a plugin + */ + public static abstract class Plotter { + + /** + * The plot's name + */ + private final String name; + + /** + * Construct a plotter with the default plot name + */ + public Plotter() { + this("Default"); + } + + /** + * Construct a plotter with a specific plot name + * + * @param name the name of the plotter to use, which will show up on the website + */ + public Plotter(final String name) { + this.name = name; + } + + /** + * Get the current value for the plotted point. Since this function defers to an external function it may or may + * not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called + * from any thread so care should be taken when accessing resources that need to be synchronized. + * + * @return the current value for the point to be plotted. + */ + public abstract int getValue(); + + /** + * Get the column name for the plotted point + * + * @return the plotted point's column name + */ + public String getColumnName() { + return name; + } + + /** + * Called after the website graphs have been updated + */ + public void reset() { + } + + @Override + public int hashCode() { + return getColumnName().hashCode(); + } + + @Override + public boolean equals(final Object object) { + if (!(object instanceof Plotter)) { + return false; + } + + final Plotter plotter = (Plotter) object; + return plotter.name.equals(name) && plotter.getValue() == getValue(); + } + } +} \ No newline at end of file diff --git a/vspigot-server/src/main/java/org/spigotmc/ProtocolData.java b/vspigot-server/src/main/java/org/spigotmc/ProtocolData.java new file mode 100644 index 0000000..ff93cbe --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/ProtocolData.java @@ -0,0 +1,190 @@ +package org.spigotmc; + +public class ProtocolData +{ + public static class ByteShort extends Number + { + + private short value; + + public ByteShort(short value) + { + this.value = value; + } + + @Override + public int intValue() + { + return value; + } + + @Override + public long longValue() + { + return value; + } + + @Override + public float floatValue() + { + return value; + } + + @Override + public double doubleValue() + { + return value; + } + } + + public static class DualByte extends Number + { + + public byte value; + public byte value2; + + public DualByte(byte value, byte value2) + { + this.value = value; + this.value2 = value2; + } + + @Override + public int intValue() + { + return value; + } + + @Override + public long longValue() + { + return value; + } + + @Override + public float floatValue() + { + return value; + } + + @Override + public double doubleValue() + { + return value; + } + } + + public static class HiddenByte extends Number + { + + private byte value; + + public HiddenByte(byte value) + { + this.value = value; + } + + @Override + public int intValue() + { + return value; + } + + @Override + public long longValue() + { + return value; + } + + @Override + public float floatValue() + { + return value; + } + + @Override + public double doubleValue() + { + return value; + } + } + public static class IntByte extends Number + { + + public int value; + public byte value2; + + public IntByte(int value, byte value2) + { + this.value = value; + this.value2 = value2; + } + + @Override + public byte byteValue() + { + return value2; + } + + @Override + public int intValue() + { + return value; + } + + @Override + public long longValue() + { + return value; + } + + @Override + public float floatValue() + { + return value; + } + + @Override + public double doubleValue() + { + return value; + } + } + + public static class DualInt extends Number + { + + public int value; + public int value2; + + public DualInt(int value, int value2) + { + this.value = value; + this.value2 = value2; + } + + @Override + public int intValue() + { + return value; + } + + @Override + public long longValue() + { + return value; + } + + @Override + public float floatValue() + { + return value; + } + + @Override + public double doubleValue() + { + return value; + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/ProtocolInjector.java b/vspigot-server/src/main/java/org/spigotmc/ProtocolInjector.java new file mode 100644 index 0000000..3b2df91 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/ProtocolInjector.java @@ -0,0 +1,253 @@ +package org.spigotmc; + +import net.minecraft.server.ChatSerializer; +import net.minecraft.server.EnumProtocol; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.Packet; +import net.minecraft.server.PacketDataSerializer; +import net.minecraft.server.PacketListener; +import net.minecraft.util.com.google.common.collect.BiMap; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Map; + +public class ProtocolInjector +{ + public static void inject() + { + try + { + addPacket( EnumProtocol.LOGIN, true, 0x3, PacketLoginCompression.class ); + + addPacket( EnumProtocol.PLAY, true, 0x45, PacketTitle.class ); + addPacket( EnumProtocol.PLAY, true, 0x47, PacketTabHeader.class ); + addPacket( EnumProtocol.PLAY, true, 0x48, PacketPlayResourcePackSend.class ); + addPacket( EnumProtocol.PLAY, false, 0x19, PacketPlayResourcePackStatus.class ); + } catch ( NoSuchFieldException e ) + { + e.printStackTrace(); + } catch ( IllegalAccessException e ) + { + e.printStackTrace(); + } + } + + private static void addPacket(EnumProtocol protocol, boolean clientbound, int id, Class packet) throws NoSuchFieldException, IllegalAccessException + { + Field packets; + if (!clientbound) { + packets = EnumProtocol.class.getDeclaredField( "h" ); + } else { + packets = EnumProtocol.class.getDeclaredField( "i" ); + } + packets.setAccessible( true ); + BiMap> pMap = (BiMap>) packets.get( protocol ); + pMap.put( id, packet ); + Field map = EnumProtocol.class.getDeclaredField( "f" ); + map.setAccessible( true ); + Map, EnumProtocol> protocolMap = (Map, EnumProtocol>) map.get( null ); + protocolMap.put( packet, protocol ); + } + + public static class PacketPlayResourcePackStatus extends Packet { + + @Override + public void a(PacketDataSerializer packetdataserializer) throws IOException + { + packetdataserializer.c( 255 ); // Hash + packetdataserializer.a(); // Result + } + + @Override + public void b(PacketDataSerializer packetdataserializer) throws IOException + { + + } + + @Override + public void handle(PacketListener packetlistener) + { + + } + } + + public static class PacketPlayResourcePackSend extends Packet { + + private String url; + private String hash; + + public PacketPlayResourcePackSend(String url, String hash) + { + this.url = url; + this.hash = hash; + } + + @Override + public void a(PacketDataSerializer packetdataserializer) throws IOException + { + + } + + @Override + public void b(PacketDataSerializer packetdataserializer) throws IOException + { + packetdataserializer.a( url ); + packetdataserializer.a( hash ); + } + + @Override + public void handle(PacketListener packetlistener) + { + + } + } + + public static class PacketLoginCompression extends Packet { + + private int threshold; + + public PacketLoginCompression(int threshold) + { + this.threshold = threshold; + } + + @Override + public void a(PacketDataSerializer packetdataserializer) throws IOException + { + + } + + @Override + public void b(PacketDataSerializer packetdataserializer) throws IOException + { + packetdataserializer.b( threshold ); + } + + @Override + public void handle(PacketListener packetlistener) + { + + } + } + + public static class PacketTabHeader extends Packet + { + + private IChatBaseComponent header; + private IChatBaseComponent footer; + + public PacketTabHeader() + { + } + + public PacketTabHeader(IChatBaseComponent header, IChatBaseComponent footer) + { + this.header = header; + this.footer = footer; + } + + @Override + public void a(PacketDataSerializer packetdataserializer) throws IOException + { + this.header = ChatSerializer.a( packetdataserializer.c( 32767 ) ); + this.footer = ChatSerializer.a( packetdataserializer.c( 32767 ) ); + } + + @Override + public void b(PacketDataSerializer packetdataserializer) throws IOException + { + packetdataserializer.a( ChatSerializer.a( this.header ) ); + packetdataserializer.a( ChatSerializer.a( this.footer ) ); + } + + @Override + public void handle(PacketListener packetlistener) + { + } + } + + public static class PacketTitle extends Packet + { + private Action action; + + // TITLE & SUBTITLE + private IChatBaseComponent text; + + // TIMES + private int fadeIn = -1; + private int stay = -1; + private int fadeOut = -1; + + public PacketTitle() {} + + public PacketTitle(Action action) + { + this.action = action; + } + + public PacketTitle(Action action, IChatBaseComponent text) + { + this( action ); + this.text = text; + } + + public PacketTitle(Action action, int fadeIn, int stay, int fadeOut) + { + this( action ); + this.fadeIn = fadeIn; + this.stay = stay; + this.fadeOut = fadeOut; + } + + + @Override + public void a(PacketDataSerializer packetdataserializer) throws IOException + { + this.action = Action.values()[packetdataserializer.a()]; + switch ( action ) + { + case TITLE: + case SUBTITLE: + this.text = ChatSerializer.a( packetdataserializer.c(32767) ); + break; + case TIMES: + this.fadeIn = packetdataserializer.readInt(); + this.stay = packetdataserializer.readInt(); + this.fadeOut = packetdataserializer.readInt(); + break; + } + } + + @Override + public void b(PacketDataSerializer packetdataserializer) throws IOException + { + packetdataserializer.b( action.ordinal() ); + switch ( action ) + { + case TITLE: + case SUBTITLE: + packetdataserializer.a( ChatSerializer.a( this.text ) ); + break; + case TIMES: + packetdataserializer.writeInt( this.fadeIn ); + packetdataserializer.writeInt( this.stay ); + packetdataserializer.writeInt( this.fadeOut ); + break; + } + } + + @Override + public void handle(PacketListener packetlistener) + { + } + + public static enum Action { + TITLE, + SUBTITLE, + TIMES, + CLEAR, + RESET + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/RestartCommand.java b/vspigot-server/src/main/java/org/spigotmc/RestartCommand.java new file mode 100644 index 0000000..429c258 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/RestartCommand.java @@ -0,0 +1,124 @@ +package org.spigotmc; + +import java.io.File; +import java.util.List; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public class RestartCommand extends Command +{ + + public RestartCommand(String name) + { + super( name ); + this.description = "Restarts the server"; + this.usageMessage = "/restart"; + this.setPermission( "bukkit.command.restart" ); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) + { + if ( testPermission( sender ) ) + { + MinecraftServer.getServer().processQueue.add( new Runnable() + { + @Override + public void run() + { + restart(); + } + } ); + } + return true; + } + + public static void restart() + { + restart( new File( SpigotConfig.restartScript ) ); + } + + public static void restart(final File script) + { + AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us + try + { + if ( script.isFile() ) + { + System.out.println( "Attempting to restart with " + SpigotConfig.restartScript ); + + // Disable Watchdog + WatchdogThread.doStop(); + + // Kick all players + for ( EntityPlayer p : (List< EntityPlayer>) MinecraftServer.getServer().getPlayerList().players ) + { + p.playerConnection.disconnect(SpigotConfig.restartMessage); + } + // Give the socket a chance to send the packets + try + { + Thread.sleep( 100 ); + } catch ( InterruptedException ex ) + { + } + // Close the socket so we can rebind with the new process + MinecraftServer.getServer().getServerConnection().b(); + + // Give time for it to kick in + try + { + Thread.sleep( 100 ); + } catch ( InterruptedException ex ) + { + } + + // Actually shutdown + try + { + MinecraftServer.getServer().stop(); + } catch ( Throwable t ) + { + } + + // This will be done AFTER the server has completely halted + Thread shutdownHook = new Thread() + { + @Override + public void run() + { + try + { + String os = System.getProperty( "os.name" ).toLowerCase(); + if ( os.contains( "win" ) ) + { + Runtime.getRuntime().exec( "cmd /c start " + script.getPath() ); + } else + { + Runtime.getRuntime().exec( new String[] + { + "sh", script.getPath() + } ); + } + } catch ( Exception e ) + { + e.printStackTrace(); + } + } + }; + + shutdownHook.setDaemon( true ); + Runtime.getRuntime().addShutdownHook( shutdownHook ); + } else + { + System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." ); + } + System.exit( 0 ); + } catch ( Exception ex ) + { + ex.printStackTrace(); + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/SneakyThrow.java b/vspigot-server/src/main/java/org/spigotmc/SneakyThrow.java new file mode 100644 index 0000000..31fc0a9 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/SneakyThrow.java @@ -0,0 +1,15 @@ +package org.spigotmc; + +public class SneakyThrow +{ + + public static void sneaky(Throwable t) + { + throw SneakyThrow.superSneaky( t ); + } + + private static T superSneaky(Throwable t) throws T + { + throw (T) t; + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/SpigotComponentReverter.java b/vspigot-server/src/main/java/org/spigotmc/SpigotComponentReverter.java new file mode 100644 index 0000000..6093d62 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/SpigotComponentReverter.java @@ -0,0 +1,105 @@ +package org.spigotmc; + +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.ChatModifier; +import net.minecraft.server.EnumChatFormat; +import net.minecraft.server.IChatBaseComponent; +import org.bukkit.ChatColor; + +import java.util.Iterator; +import java.util.List; + +public class SpigotComponentReverter +{ + public static String toLegacy(IChatBaseComponent s) + { + StringBuilder builder = new StringBuilder(); + legacy( builder, s ); + return builder.toString(); + } + + private static void legacy(StringBuilder builder, IChatBaseComponent s) + { + ChatModifier modifier = s.getChatModifier(); + colorize( builder, modifier ); + if ( s instanceof ChatComponentText ) + { + builder.append( s.e() ); + } else { + throw new RuntimeException( "Unhandled type: " + s.getClass().getSimpleName() ); + } + + for ( IChatBaseComponent c : getExtra( s ) ) { + legacy( builder, c ); + } + } + + private static void colorize(StringBuilder builder, ChatModifier modifier) + { + if ( modifier == null ) return; + // Color first + EnumChatFormat color = getColor( modifier ); + if ( color == null ) + { + color = EnumChatFormat.BLACK; + } + builder.append( color.toString() ); + + if ( isBold( modifier ) ) + { + builder.append( ChatColor.BOLD ); + } + if ( isItalic( modifier ) ) + { + builder.append( ChatColor.ITALIC ); + } + if ( isRandom( modifier ) ) + { + builder.append( ChatColor.MAGIC ); + } + if ( isStrikethrough( modifier ) ) + { + builder.append( ChatColor.STRIKETHROUGH ); + } + if ( isUnderline( modifier ) ) + { + builder.append( ChatColor.UNDERLINE ); + } + } + + // Helpers + private static List getExtra(IChatBaseComponent c) + { + return c.a(); + } + + private static EnumChatFormat getColor(ChatModifier c) + { + return c.a(); + } + + private static boolean isBold(ChatModifier c) + { + return c.b(); + } + + private static boolean isItalic(ChatModifier c) + { + return c.c(); + } + + private static boolean isStrikethrough(ChatModifier c) + { + return c.d(); + } + + private static boolean isUnderline(ChatModifier c) + { + return c.e(); + } + + private static boolean isRandom(ChatModifier c) + { + return c.f(); + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/SpigotCompressor.java b/vspigot-server/src/main/java/org/spigotmc/SpigotCompressor.java new file mode 100644 index 0000000..c3e2374 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/SpigotCompressor.java @@ -0,0 +1,53 @@ +package org.spigotmc; + +import net.valorhcf.ReusableByteArray; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.channel.ChannelHandlerContext; +import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder; + +import java.util.zip.Deflater; + +public class SpigotCompressor extends MessageToByteEncoder +{ + + private final byte[] buffer = new byte[8192]; + private final Deflater deflater = new Deflater(); + private static final ReusableByteArray reusableData = new ReusableByteArray(0x70000); // rough size estimate from a quick play test + + @Override + protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception + { + ByteBuf in = (ByteBuf) msg; + int origSize = in.readableBytes(); + + if ( origSize < 256 ) + { + writeVarInt( 0, out ); + out.writeBytes( in ); + } else + { + byte[] data = reusableData.get(origSize); + in.readBytes( data, 0, origSize ); + + writeVarInt( origSize, out ); + + deflater.setInput( data, 0, origSize ); + deflater.finish(); + while (!deflater.finished()) { + int count = deflater.deflate( buffer ); + out.writeBytes( buffer, 0, count ); + } + deflater.reset(); + } + } + + public static void writeVarInt(int val, ByteBuf out) { + while ((val & -128) != 0) { + out.writeByte(val & 127 | 128); + val >>>= 7; + } + + out.writeByte(val); + } + +} diff --git a/vspigot-server/src/main/java/org/spigotmc/SpigotConfig.java b/vspigot-server/src/main/java/org/spigotmc/SpigotConfig.java new file mode 100644 index 0000000..5e04e9b --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/SpigotConfig.java @@ -0,0 +1,657 @@ +package org.spigotmc; + +import com.google.common.base.Throwables; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import net.valorhcf.command.KnockbackCommand; +import net.valorhcf.json.JsonConfig; +import net.valorhcf.knockback.CraftKnockback; +import net.valorhcf.knockback.Knockback; +import net.minecraft.server.AttributeRanged; +import net.minecraft.server.GenericAttributes; +import net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap; +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +public class SpigotConfig +{ + + // joeleoli start + private static JsonConfig knockbackConfig; + public static Knockback defaultKnockback; + public static final List knockbacks = new ArrayList<>(); + + public static boolean hcf = true; + public static boolean wtap = false; + + public static Knockback getKnockbackByName(String name) { + for (Knockback profile : knockbacks) { + if (profile.getName().equalsIgnoreCase(name)) { + return profile; + } + } + return null; + } + + public static void loadKnockbackSettings() { + knockbackConfig = new JsonConfig(new File("config/server", "knockback.json")).load(); + + Map profilesMap = (Map) knockbackConfig.get("profiles"); + + for (String profileName : profilesMap.keySet()) { + Knockback profile = getKnockbackByName(profileName); + + if (profile == null) { + profile = new CraftKnockback(profileName); + } + + profile.setFriction(Double.parseDouble(knockbackConfig.getString("profiles." + profileName + ".friction"))); + profile.setHorizontal(Double.parseDouble(knockbackConfig.getString("profiles." + profileName + ".horizontal"))); + profile.setVertical(Double.parseDouble(knockbackConfig.getString("profiles." + profileName + ".vertical"))); + profile.setVerticalLimit(Double.parseDouble(knockbackConfig.getString("profiles." + profileName + ".verticalLimit"))); + profile.setExtraHorizontal(Double.parseDouble(knockbackConfig.getString("profiles." + profileName + ".extraHorizontal"))); + profile.setExtraVertical(Double.parseDouble(knockbackConfig.getString("profiles." + profileName + ".extraVertical"))); + + knockbacks.add(profile); + } + + if (knockbacks.isEmpty()) { + knockbacks.add(new CraftKnockback("Default")); + } + + defaultKnockback = getKnockbackByName(knockbackConfig.getString("global-profile", "Default")); + + if (defaultKnockback == null) { + defaultKnockback = knockbacks.get(0); + } + + hcf = Boolean.parseBoolean(knockbackConfig.getString("settings.hcf", "true")); + wtap = Boolean.parseBoolean(knockbackConfig.getString("settings.wtap", "false")); + } + + public static void saveKnockbackProfiles() { + knockbackConfig.clear(); + + for (Knockback profile : knockbacks) { + knockbackConfig.set("profiles." + profile.getName() + ".friction", profile.getFriction()); + knockbackConfig.set("profiles." + profile.getName() + ".horizontal", profile.getHorizontal()); + knockbackConfig.set("profiles." + profile.getName() + ".vertical", profile.getVertical()); + knockbackConfig.set("profiles." + profile.getName() + ".verticalLimit", profile.getVerticalLimit()); + knockbackConfig.set("profiles." + profile.getName() + ".extraHorizontal", profile.getExtraHorizontal()); + knockbackConfig.set("profiles." + profile.getName() + ".extraVertical", profile.getExtraVertical()); + } + + knockbackConfig.set("settings.hcf", hcf); + knockbackConfig.set("settings.wtap", wtap); + + knockbackConfig.save(); + } + + public static void sendKnockbackInfo(CommandSender sender) { + String hcf = SpigotConfig.hcf ? (ChatColor.GREEN + "yes") : (ChatColor.RED + "no"); + String wtap = SpigotConfig.wtap ? (ChatColor.GREEN + "yes") : (ChatColor.RED + "no"); + + sender.sendMessage(ChatColor.BLUE + ChatColor.STRIKETHROUGH.toString() + StringUtils.repeat("-", 35)); + sender.sendMessage(ChatColor.GOLD + "HCF: " + hcf + ChatColor.GRAY + " | " + ChatColor.GOLD + "WTap: " + wtap); + sender.sendMessage(ChatColor.BLUE + ChatColor.STRIKETHROUGH.toString() + StringUtils.repeat("-", 35)); + + for (Knockback profile : knockbacks) { + boolean current = defaultKnockback.getName().equals(profile.getName()); + + sender.sendMessage((current ? ChatColor.GREEN.toString() : ChatColor.RED.toString()) + ChatColor.BOLD + profile.getName()); + sender.sendMessage(ChatColor.GOLD + "-> " + ChatColor.YELLOW + "Friction: " + ChatColor.RED + profile.getFriction()); + sender.sendMessage(ChatColor.GOLD + "-> " + ChatColor.YELLOW + "Horizontal: " + ChatColor.RED + profile.getHorizontal()); + sender.sendMessage(ChatColor.GOLD + "-> " + ChatColor.YELLOW + "Vertical: " + ChatColor.RED + profile.getVertical()); + sender.sendMessage(ChatColor.GOLD + "-> " + ChatColor.YELLOW + "Vertical Limit: " + ChatColor.RED + profile.getVerticalLimit()); + sender.sendMessage(ChatColor.GOLD + "-> " + ChatColor.YELLOW + "Extra Horizontal: " + ChatColor.RED + profile.getExtraHorizontal()); + sender.sendMessage(ChatColor.GOLD + "-> " + ChatColor.YELLOW + "Extra Vertical: " + ChatColor.RED + profile.getExtraVertical()); + } + + sender.sendMessage(ChatColor.BLUE + ChatColor.STRIKETHROUGH.toString() + StringUtils.repeat("-", 35)); + } + + private static final File CONFIG_FILE = new File( "config/server", "spigot.yml" ); // MineHQ - Dedicated config directory + private static final String HEADER = "This is the main configuration file for Spigot.\n" + + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" + + "with caution, and make sure you know what each option does before configuring.\n" + + "For a reference for any variable inside this file, check out the Spigot wiki at\n" + + "http://www.spigotmc.org/wiki/spigot-configuration/\n" + + "\n" + + "If you need help with the configuration or have any questions related to Spigot,\n" + + "join us at the IRC or drop by our forums and leave a post.\n" + + "\n" + + "IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )\n" + + "Forums: http://www.spigotmc.org/\n"; + /*========================================================================*/ + public static YamlConfiguration config; + static int version; + static Map commands; + /*========================================================================*/ + private static Metrics metrics; + + public static void init() + { + config = new YamlConfiguration(); + try + { + config.load( CONFIG_FILE ); + } catch ( IOException ex ) + { + } catch ( InvalidConfigurationException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not load spigot.yml, please correct your syntax errors", ex ); + throw Throwables.propagate( ex ); + } + + config.options().header( HEADER ); + config.options().copyDefaults( true ); + + commands = new HashMap(); + + version = getInt( "config-version", 8 ); + set( "config-version", 8 ); + readConfig( SpigotConfig.class, null ); + + loadKnockbackSettings(); + commands.put("beefwellington", new KnockbackCommand()); + } + + public static void registerCommands() + { + for ( Map.Entry entry : commands.entrySet() ) + { + MinecraftServer.getServer().server.getCommandMap().register( entry.getKey(), "Spigot", entry.getValue() ); + } + + if ( metrics == null ) + { + try + { + metrics = new Metrics(); + metrics.start(); + } catch ( IOException ex ) + { + Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start metrics service", ex ); + } + } + } + + static void readConfig(Class clazz, Object instance) + { + for ( Method method : clazz.getDeclaredMethods() ) + { + if ( Modifier.isPrivate( method.getModifiers() ) ) + { + if ( method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE ) + { + try + { + method.setAccessible( true ); + method.invoke( instance ); + } catch ( InvocationTargetException ex ) + { + throw Throwables.propagate( ex.getCause() ); + } catch ( Exception ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Error invoking " + method, ex ); + } + } + } + } + + try + { + config.save( CONFIG_FILE ); + } catch ( IOException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not save " + CONFIG_FILE, ex ); + } + } + + private static void set(String path, Object val) + { + config.set( path, val ); + } + + private static boolean getBoolean(String path, boolean def) + { + config.addDefault( path, def ); + return config.getBoolean( path, config.getBoolean( path ) ); + } + + private static int getInt(String path, int def) + { + config.addDefault( path, def ); + return config.getInt( path, config.getInt( path ) ); + } + + private static List getList(String path, T def) + { + config.addDefault( path, def ); + return (List) config.getList( path, config.getList( path ) ); + } + + private static String getString(String path, String def) + { + config.addDefault( path, def ); + return config.getString( path, config.getString( path ) ); + } + + private static double getDouble(String path, double def) + { + config.addDefault( path, def ); + return config.getDouble( path, config.getDouble( path ) ); + } + + public static boolean logCommands; + private static void logCommands() + { + logCommands = getBoolean( "commands.log", true ); + } + + public static int tabComplete; + private static void tabComplete() + { + if ( version < 6 ) + { + boolean oldValue = getBoolean( "commands.tab-complete", true ); + if ( oldValue ) + { + set( "commands.tab-complete", 0 ); + } else + { + set( "commands.tab-complete", -1 ); + } + } + tabComplete = getInt( "commands.tab-complete", 0 ); + } + + public static String whitelistMessage; + public static String unknownCommandMessage; + public static String serverFullMessage; + public static String outdatedClientMessage = "Outdated client! Please use {0}"; + public static String outdatedServerMessage = "Outdated server! I\'m still on {0}"; + private static String transform(String s) + { + return ChatColor.translateAlternateColorCodes( '&', s ).replaceAll( "\\n", "\n" ); + } + private static void messages() + { + if (version < 8) + { + set( "messages.outdated-client", outdatedClientMessage ); + set( "messages.outdated-server", outdatedServerMessage ); + } + + whitelistMessage = transform( getString( "messages.whitelist", "You are not whitelisted on this server!" ) ); + unknownCommandMessage = transform( getString( "messages.unknown-command", "Unknown command. Type \"/help\" for help." ) ); + serverFullMessage = transform( getString( "messages.server-full", "The server is full!" ) ); + outdatedClientMessage = transform( getString( "messages.outdated-client", outdatedClientMessage ) ); + outdatedServerMessage = transform( getString( "messages.outdated-server", outdatedServerMessage ) ); + } + + public static int timeoutTime = 10; + public static boolean restartOnCrash = true; + public static String restartScript = "./start.sh"; + public static String restartMessage; + private static void watchdog() + { + timeoutTime = getInt( "settings.timeout-time", timeoutTime ); + restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash ); + restartScript = getString( "settings.restart-script", restartScript ); + restartMessage = transform( getString( "messages.restart", "Server is restarting" ) ); + commands.put( "restart", new RestartCommand( "restart" ) ); + WatchdogThread.doStart( timeoutTime, restartOnCrash ); + } + + public static boolean bungee; + private static void bungee() { + if ( version < 4 ) + { + set( "settings.bungeecord", false ); + System.out.println( "Oudated config, disabling BungeeCord support!" ); + } + bungee = getBoolean( "settings.bungeecord", false ); + } + + private static void nettyThreads() + { + int count = getInt( "settings.netty-threads", 4 ); + System.setProperty( "io.netty.eventLoopThreads", Integer.toString( count ) ); + Bukkit.getLogger().log( Level.INFO, "Using {0} threads for Netty based IO", count ); + } + + public static boolean lateBind; + private static void lateBind() { + lateBind = getBoolean( "settings.late-bind", false ); + } + + public static boolean disableStatSaving = true; + public static TObjectIntHashMap forcedStats = new TObjectIntHashMap(); + private static void stats() + { + if ( !config.contains( "stats.forced-stats" ) ) { + config.createSection( "stats.forced-stats" ); + } + + ConfigurationSection section = config.getConfigurationSection( "stats.forced-stats" ); + for ( String name : section.getKeys( true ) ) + { + if ( section.isInt( name ) ) + { + forcedStats.put( name, section.getInt( name ) ); + } + } + + if ( disableStatSaving && section.getInt( "achievement.openInventory", 0 ) < 1 ) + { + forcedStats.put("achievement.openInventory", 1); + } + } + + private static void tpsCommand() + { + commands.put( "tps", new TicksPerSecondCommand( "tps" ) ); + } + + public static int playerSample; + private static void playerSample() + { + playerSample = getInt( "settings.sample-count", 12 ); + System.out.println( "Server Ping Player Sample Count: " + playerSample ); + } + + public static int playerShuffle; + private static void playerShuffle() + { + playerShuffle = getInt( "settings.player-shuffle", 0 ); + } + + public static List spamExclusions; + private static void spamExclusions() + { + spamExclusions = getList( "commands.spam-exclusions", Arrays.asList( new String[] + { + "/skill" + } ) ); + } + + public static boolean silentCommandBlocks; + private static void silentCommandBlocks() + { + silentCommandBlocks = getBoolean( "commands.silent-commandblock-console", false ); + } + + public static boolean filterCreativeItems; + private static void filterCreativeItems() + { + filterCreativeItems = getBoolean( "settings.filter-creative-items", true ); + } + + public static Set replaceCommands; + private static void replaceCommands() + { + if ( config.contains( "replace-commands" ) ) + { + set( "commands.replace-commands", config.getStringList( "replace-commands" ) ); + config.set( "replace-commands", null ); + } + replaceCommands = new HashSet( (List) getList( "commands.replace-commands", + Arrays.asList( "setblock", "summon", "testforblock", "tellraw" ) ) ); + } + + public static int userCacheCap; + private static void userCacheCap() + { + userCacheCap = getInt( "settings.user-cache-size", 1000 ); + } + + public static boolean saveUserCacheOnStopOnly; + private static void saveUserCacheOnStopOnly() + { + saveUserCacheOnStopOnly = getBoolean( "settings.save-user-cache-on-stop-only", false ); + } + + public static int intCacheLimit; + private static void intCacheLimit() + { + intCacheLimit = getInt( "settings.int-cache-limit", 1024 ); + } + + public static double movedWronglyThreshold; + private static void movedWronglyThreshold() + { + movedWronglyThreshold = getDouble( "settings.moved-wrongly-threshold", 0.0625D ); + } + + public static double movedTooQuicklyThreshold; + private static void movedTooQuicklyThreshold() + { + movedTooQuicklyThreshold = getDouble( "settings.moved-too-quickly-threshold", 100.0D ); + } + + public static double maxHealth = 2048; + public static double movementSpeed = 2048; + public static double attackDamage = 2048; + private static void attributeMaxes() + { + maxHealth = getDouble( "settings.attribute.maxHealth.max", maxHealth ); + ( (AttributeRanged) GenericAttributes.maxHealth ).b = maxHealth; + movementSpeed = getDouble( "settings.attribute.movementSpeed.max", movementSpeed ); + ( (AttributeRanged) GenericAttributes.d ).b = movementSpeed; + attackDamage = getDouble( "settings.attribute.attackDamage.max", attackDamage ); + ( (AttributeRanged) GenericAttributes.e ).b = attackDamage; + } + + private static void globalAPICache() + { + if ( getBoolean( "settings.global-api-cache", false ) && !CachedStreamHandlerFactory.isSet ) + { + Bukkit.getLogger().info( "Global API cache enabled - All requests to Mojang's API will be " + + "handled by Spigot" ); + CachedStreamHandlerFactory.isSet = true; + URL.setURLStreamHandlerFactory(new CachedStreamHandlerFactory()); + } + } + + public static boolean debug; + private static void debug() + { + debug = getBoolean( "settings.debug", false ); + + if ( debug && !LogManager.getRootLogger().isTraceEnabled() ) + { + // Enable debug logging + LoggerContext ctx = (LoggerContext) LogManager.getContext( false ); + Configuration conf = ctx.getConfiguration(); + conf.getLoggerConfig( LogManager.ROOT_LOGGER_NAME ).setLevel( org.apache.logging.log4j.Level.ALL ); + ctx.updateLoggers( conf ); + } + + if ( LogManager.getRootLogger().isTraceEnabled() ) + { + Bukkit.getLogger().info( "Debug logging is enabled" ); + } else + { + Bukkit.getLogger().info( "Debug logging is disabled" ); + } + } + + // Poweruser start + public static boolean disablePlayerFileSaving; + private static void playerFiles() { + disablePlayerFileSaving = getBoolean( "settings.disable-player-file-saving", false ); + if (disablePlayerFileSaving) { + disableStatSaving = true; + } + } + + public static boolean logRemainingAsyncThreadsDuringShutdown; + private static void logRemainingAsyncThreadsDuringShutdown() { + logRemainingAsyncThreadsDuringShutdown = getBoolean( "settings.logRemainingAsyncThreadsDuringShutdown" , true); + } + + private static void powertpsCommand() + { + commands.put( "tpsgraph", new net.valorhcf.command.TPSCommand( "tpsgraph" ) ); + } + + private static void worldstatsCommand() { + commands.put( "worlddata", new net.valorhcf.command.WorldStatsCommand( "worlddata" ) ); + } + + private static void setviewdistanceCommand() { + commands.put( "viewdistance", new net.valorhcf.command.SetViewDistanceCommand( "viewdistance" ) ); + } + + public static int playersPerChunkIOThread; + private static void playersPerChunkIOThread() { + playersPerChunkIOThread = Math.max(1, getInt( "settings.chunkio.players-per-thread", 150) ); + } + + public static int autoSaveChunksPerTick; + private static void autoSaveChunksPerTick() { + autoSaveChunksPerTick = getInt( "settings.autosave.chunks-per-tick" , 200 ); + } + + public static boolean autoSaveFireWorldSaveEvent; + private static void autoSaveFireWorldSaveEvent() { + autoSaveFireWorldSaveEvent = getBoolean ( "settings.autosave.fire-WorldSaveEvent", false); + } + + public static boolean autoSaveClearRegionFileCache; + private static void autoSaveClearRegionFileCache() { + autoSaveClearRegionFileCache = getBoolean ( "settings.autosave.clear-RegionFileCache", false); + } + + public static boolean lagSpikeLoggerEnabled; + private static void lagSpikeLoggerEnabled() { + lagSpikeLoggerEnabled = getBoolean ( "settings.lagSpikeLogger.enabled", true); + } + + public static long lagSpikeLoggerTickLimitNanos; + private static void lagSpikeLoggerTickLimitNanos() { + lagSpikeLoggerTickLimitNanos = ((long) getInt( "settings.lagSpikeLogger.tickLimitInMilliseconds", 100)) * 1000000L; + } + // Poweruser end + + // Griffin start + public static int brewingMultiplier; + private static void brewingMultiplier() { + brewingMultiplier = getInt("settings.brewingMultiplier", 1); + } + + public static int smeltingMultiplier; + private static void smeltingMultiplier() { + smeltingMultiplier = getInt("settings.smeltingMultiplier", 1); + } + + public static boolean instantRespawn; + private static void instantRespawn() { + instantRespawn = getBoolean("settings.instantRespawn", false); + } + // Griffin end + + // Guardian start + public static boolean guardianEnabled; + private static void guardianEnabled() { + guardianEnabled = getBoolean("settings.guardian.enabled", true); + } + + public static boolean guardianTesting; + private static void guardianTesting() { + guardianTesting = getBoolean("settings.guardian.testing", false); + } + // Guardian end + + // MineHQ start + private static void noTrackCommand() { + commands.put( "hideplayers", new net.valorhcf.command.NoTrackCommand( "hideplayers" ) ); + } + + public static boolean disableTracking; + private static void disableTracking() { + disableTracking = getBoolean("settings.disable.entityTracking", false); + } + + public static boolean disableBlockTicking; + private static void disableBlockTicking() { + disableBlockTicking = getBoolean("settings.disable.ticking.blocks", false); + } + + public static boolean disableVillageTicking; + private static void disableVillageTicking() { + disableVillageTicking = getBoolean("settings.disable.ticking.villages", false); + } + + public static boolean disableWeatherTicking; + private static void disableWeatherTicking() { + disableWeatherTicking = getBoolean("settings.disable.ticking.weather", false); + } + + public static boolean disableSleepCheck; + private static void disableSleepCheck() { + disableSleepCheck = getBoolean("settings.disable.general.sleepcheck", false); + } + + public static boolean disableEntityCollisions; + private static void disableEntityCollisions() { + disableEntityCollisions = getBoolean("settings.disable.general.entity-collisions", false); + } + + public static boolean cacheChunkMaps; + private static void cacheChunkMaps() { + cacheChunkMaps = getBoolean("settings.cache-chunk-maps", false); + } + + public static boolean disableSaving; + private static void disableSaving() { + disableSaving = getBoolean("settings.disableSaving", false); + } + + public static boolean playerListPackets; + public static boolean updatePingOnTablist; + public static boolean onlyCustomTab; + private static void packets() { + onlyCustomTab = getBoolean("settings.only-custom-tab", false); + + if (!onlyCustomTab) { + playerListPackets = !getBoolean("settings.disable.player-list-packets", false); + updatePingOnTablist = getBoolean("settings.disable.ping-update-packets", false); + } + } + // MineHQ end + + public static boolean reduceArmorDamage; + private static void reduceArmorDamage() { + reduceArmorDamage = getBoolean("settings.reduce-armor-damage", false); + } + + public static boolean pearlThroughGatesAndTripwire = false; + private static void pearls() { + pearlThroughGatesAndTripwire = getBoolean("settings.pearl-through-gates-and-tripwire", false); + } + +} diff --git a/vspigot-server/src/main/java/org/spigotmc/SpigotDebreakifier.java b/vspigot-server/src/main/java/org/spigotmc/SpigotDebreakifier.java new file mode 100644 index 0000000..d811f55 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/SpigotDebreakifier.java @@ -0,0 +1,102 @@ +package org.spigotmc; + +import com.google.common.base.Charsets; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import net.minecraft.server.Block; +import net.minecraft.server.Blocks; +import net.minecraft.server.Item; +import net.minecraft.server.Items; +import net.minecraft.util.gnu.trove.map.hash.TIntIntHashMap; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; + +public class SpigotDebreakifier +{ + + private static final boolean[] validBlocks = new boolean[ 198 << 4 ]; + private static final int[] correctedValues = new int[ 198 ]; + + static + { + Arrays.fill( correctedValues, -1 ); + InputStream in = SpigotDebreakifier.class.getResourceAsStream( "/blocks.json" ); + try + { + JsonArray e = new JsonParser().parse( new InputStreamReader( in, Charsets.UTF_8 ) ).getAsJsonArray(); + for ( JsonElement entry : e ) + { + String[] parts = entry.getAsString().split( ":" ); + int id = Integer.parseInt( parts[ 0 ] ); + int data = Integer.parseInt( parts[ 1 ] ); + validBlocks[ ( id << 4 ) | data ] = true; + if ( correctedValues[ id ] == -1 || data < correctedValues[ id ] ) + { + correctedValues[ id ] = data; + } + } + } finally + { + try + { + in.close(); + } catch ( IOException e ) + { + throw new RuntimeException( e ); + } + } + } + + public static int getCorrectedData(int id, int data) + { + if ( id > 197 ) return data; + if ( id == 175 && data > 8 ) + { + data = 8; + } + if ( validBlocks[ ( id << 4 ) | data ] ) + { + return data; + } else + { + return correctedValues[ id ] & 0xF; + } + } + + private static TIntIntHashMap invalidItems = new TIntIntHashMap(); + static { + replace( Blocks.WATER, Items.WATER_BUCKET ); + replace( Blocks.STATIONARY_WATER, Items.WATER_BUCKET ); + replace( Blocks.LAVA, Items.LAVA_BUCKET ); + replace( Blocks.STATIONARY_LAVA, Items.LAVA_BUCKET ); + replace( Blocks.PORTAL, Items.NETHER_BRICK ); + replace( Blocks.DOUBLE_STEP, Blocks.STEP ); + replace( Blocks.FIRE, Items.FLINT_AND_STEEL ); + replace( Blocks.ENDER_PORTAL, Blocks.ENDER_PORTAL_FRAME ); + replace( Blocks.WOOD_DOUBLE_STEP, Blocks.WOOD_STEP ); + replace( Blocks.COCOA, Items.SEEDS ); + replace( Blocks.CARROTS, Items.CARROT ); + replace( Blocks.POTATOES, Items.POTATO ); + } + + public static int getItemId(int id) + { + return invalidItems.containsKey( id ) ? invalidItems.get( id ) : id; + } + + private static void replace(Block block, Block other) { + replace( Block.getId( block ), Block.getId( other ) ); + } + + private static void replace(Block block, Item other) { + replace( Block.getId( block ), Item.getId( other ) ); + } + + private static void replace(int block, int other) { + invalidItems.put( block, other ); + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/SpigotDecompressor.java b/vspigot-server/src/main/java/org/spigotmc/SpigotDecompressor.java new file mode 100644 index 0000000..b9f6c15 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/SpigotDecompressor.java @@ -0,0 +1,45 @@ +package org.spigotmc; + +import net.valorhcf.ReusableByteArray; +import net.minecraft.server.PacketDataSerializer; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.buffer.Unpooled; +import net.minecraft.util.io.netty.channel.ChannelHandlerContext; +import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; +import java.util.zip.Inflater; + +public class SpigotDecompressor extends ByteToMessageDecoder +{ + + private final Inflater inflater = new Inflater(); + private static final ReusableByteArray reusableCompressedData = new ReusableByteArray(8192); + + @Override + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List objects) throws Exception + { + if ( byteBuf.readableBytes() == 0 ) + { + return; + } + + PacketDataSerializer serializer = new PacketDataSerializer( byteBuf ); + int size = serializer.a(); + if ( size == 0 ) + { + objects.add( serializer.readBytes( serializer.readableBytes() ) ); + } else + { + int compressedSize = serializer.readableBytes(); + byte[] compressedData = reusableCompressedData.get(compressedSize); + serializer.readBytes( compressedData, 0, compressedSize ); + inflater.setInput( compressedData, 0, compressedSize ); + + byte[] data = new byte[ size ]; + inflater.inflate( data ); + objects.add( Unpooled.wrappedBuffer( data ) ); + inflater.reset(); + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/SpigotWorldConfig.java b/vspigot-server/src/main/java/org/spigotmc/SpigotWorldConfig.java new file mode 100644 index 0000000..db532f7 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/SpigotWorldConfig.java @@ -0,0 +1,390 @@ +package org.spigotmc; + +import java.util.Arrays; +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; + +public class SpigotWorldConfig +{ + + private final String worldName; + private final YamlConfiguration config; + private boolean verbose; + + public SpigotWorldConfig(String worldName) + { + this.worldName = worldName; + this.config = SpigotConfig.config; + init(); + } + + public void init() + { + this.verbose = getBoolean( "verbose", true ); + + log( "-------- World Settings For [" + worldName + "] --------" ); + SpigotConfig.readConfig( SpigotWorldConfig.class, this ); + } + + private void log(String s) + { + if ( verbose ) + { + Bukkit.getLogger().info( s ); + } + } + + private void set(String path, Object val) + { + config.set( "world-settings.default." + path, val ); + } + + private boolean getBoolean(String path, boolean def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getBoolean( "world-settings." + worldName + "." + path, config.getBoolean( "world-settings.default." + path ) ); + } + + private double getDouble(String path, double def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getDouble( "world-settings." + worldName + "." + path, config.getDouble( "world-settings.default." + path ) ); + } + + private int getInt(String path, int def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getInt( "world-settings." + worldName + "." + path, config.getInt( "world-settings.default." + path ) ); + } + + private List getList(String path, T def) + { + config.addDefault( "world-settings.default." + path, def ); + return (List) config.getList( "world-settings." + worldName + "." + path, config.getList( "world-settings.default." + path ) ); + } + + private String getString(String path, String def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getString( "world-settings." + worldName + "." + path, config.getString( "world-settings.default." + path ) ); + } + + public int chunksPerTick; + public boolean clearChunksOnTick; + private void chunksPerTick() + { + chunksPerTick = getInt( "chunks-per-tick", 650 ); + log( "Chunks to Grow per Tick: " + chunksPerTick ); + + clearChunksOnTick = getBoolean( "clear-tick-list", false ); + log( "Clear tick list: " + clearChunksOnTick ); + } + + // Crop growth rates + public int cactusModifier; + public int caneModifier; + public int melonModifier; + public int mushroomModifier; + public int pumpkinModifier; + public int saplingModifier; + public int wheatModifier; + private int getAndValidateGrowth(String crop) + { + int modifier = getInt( "growth." + crop.toLowerCase() + "-modifier", 100 ); + if ( modifier == 0 ) + { + log( "Cannot set " + crop + " growth to zero, defaulting to 100" ); + modifier = 100; + } + log( crop + " Growth Modifier: " + modifier + "%" ); + + return modifier; + } + private void growthModifiers() + { + cactusModifier = getAndValidateGrowth( "Cactus" ); + caneModifier = getAndValidateGrowth( "Cane" ); + melonModifier = getAndValidateGrowth( "Melon" ); + mushroomModifier = getAndValidateGrowth( "Mushroom" ); + pumpkinModifier = getAndValidateGrowth( "Pumpkin" ); + saplingModifier = getAndValidateGrowth( "Sapling" ); + wheatModifier = getAndValidateGrowth( "Wheat" ); + } + + public double itemMerge; + private void itemMerge() + { + itemMerge = getDouble("merge-radius.item", 2.5 ); + log( "Item Merge Radius: " + itemMerge ); + } + + public double expMerge; + private void expMerge() + { + expMerge = getDouble("merge-radius.exp", 3.0 ); + log( "Experience Merge Radius: " + expMerge ); + } + + public int viewDistance; + private void viewDistance() + { + viewDistance = getInt( "view-distance", Bukkit.getViewDistance() ); + log( "View Distance: " + viewDistance ); + } + + public byte mobSpawnRange; + private void mobSpawnRange() + { + mobSpawnRange = (byte) getInt( "mob-spawn-range", 4 ); + log( "Mob Spawn Range: " + mobSpawnRange ); + } + + public int animalActivationRange = 32; + public int monsterActivationRange = 32; + public int miscActivationRange = 16; + private void activationRange() + { + animalActivationRange = getInt( "entity-activation-range.animals", animalActivationRange ); + monsterActivationRange = getInt( "entity-activation-range.monsters", monsterActivationRange ); + miscActivationRange = getInt( "entity-activation-range.misc", miscActivationRange ); + log( "Entity Activation Range: An " + animalActivationRange + " / Mo " + monsterActivationRange + " / Mi " + miscActivationRange ); + } + + public int playerTrackingRange = 48; + public int animalTrackingRange = 48; + public int monsterTrackingRange = 48; + public int miscTrackingRange = 32; + public int otherTrackingRange = 64; + private void trackingRange() + { + playerTrackingRange = getInt( "entity-tracking-range.players", playerTrackingRange ); + animalTrackingRange = getInt( "entity-tracking-range.animals", animalTrackingRange ); + monsterTrackingRange = getInt( "entity-tracking-range.monsters", monsterTrackingRange ); + miscTrackingRange = getInt( "entity-tracking-range.misc", miscTrackingRange ); + otherTrackingRange = getInt( "entity-tracking-range.other", otherTrackingRange ); + log( "Entity Tracking Range: Pl " + playerTrackingRange + " / An " + animalTrackingRange + " / Mo " + monsterTrackingRange + " / Mi " + miscTrackingRange + " / Other " + otherTrackingRange ); + } + + public boolean altHopperTicking; + public int hopperTransfer; + public int hopperCheck; + public int hopperAmount; + private void hoppers() + { + // Alternate ticking method. Uses inventory changes, redstone updates etc. + // to update hoppers. Hopper-check is disabled when this is true. + boolean prev = altHopperTicking; + altHopperTicking = getBoolean( "hopper-alt-ticking", false ); + // Necessary for the reload command + if (prev != altHopperTicking) { + net.minecraft.server.World world = (net.minecraft.server.World) Bukkit.getWorld(this.worldName); + if (world != null) { + if (altHopperTicking) { + for (Object o : world.tileEntityList) { + if (o instanceof net.minecraft.server.TileEntityHopper) { + ((net.minecraft.server.TileEntityHopper) o).convertToScheduling(); + } + } + } else { + for (Object o : world.tileEntityList) { + if (o instanceof net.minecraft.server.TileEntityHopper) { + ((net.minecraft.server.TileEntityHopper) o).convertToPolling(); + } + } + } + } + } + // Set the tick delay between hopper item movements + hopperTransfer = getInt( "ticks-per.hopper-transfer", 8 ); + // Set the tick delay between checking for items after the associated + // container is empty. Default to the hopperTransfer value to prevent + // hopper sorting machines from becoming out of sync. + hopperCheck = getInt( "ticks-per.hopper-check", hopperTransfer ); + hopperAmount = getInt( "hopper-amount", 1 ); + log( "Alternative Hopper Ticking: " + altHopperTicking ); + log( "Hopper Transfer: " + hopperTransfer + " Hopper Check: " + hopperCheck + " Hopper Amount: " + hopperAmount ); + } + + public boolean randomLightUpdates; + private void lightUpdates() + { + randomLightUpdates = getBoolean( "random-light-updates", false ); + log( "Random Lighting Updates: " + randomLightUpdates ); + } + + public boolean saveStructureInfo; + private void structureInfo() + { + saveStructureInfo = getBoolean( "save-structure-info", true ); + log( "Structure Info Saving: " + saveStructureInfo ); + if ( !saveStructureInfo ) + { + log( "*** WARNING *** You have selected to NOT save structure info. This may cause structures such as fortresses to not spawn mobs when updating to 1.7!" ); + log( "*** WARNING *** Please use this option with caution, SpigotMC is not responsible for any issues this option may cause in the future!" ); + } + } + + public int itemDespawnRate; + private void itemDespawnRate() + { + itemDespawnRate = getInt( "item-despawn-rate", 6000 ); + log( "Item Despawn Rate: " + itemDespawnRate ); + } + + public int arrowDespawnRate; + private void arrowDespawnRate() + { + arrowDespawnRate = getInt( "arrow-despawn-rate", 1200 ); + log( "Arrow Despawn Rate: " + arrowDespawnRate ); + } + + public boolean antiXray; + public int engineMode; + public List hiddenBlocks; + public List replaceBlocks; + public AntiXray antiXrayInstance; + private void antiXray() + { + antiXray = getBoolean( "anti-xray.enabled", true ); + log( "Anti X-Ray: " + antiXray ); + + engineMode = getInt( "anti-xray.engine-mode", 1 ); + log( "\tEngine Mode: " + engineMode ); + + if ( SpigotConfig.version < 5 ) + { + set( "anti-xray.blocks", null ); + } + hiddenBlocks = getList( "anti-xray.hide-blocks", Arrays.asList( new Integer[] + { + 14, 15, 16, 21, 48, 49, 54, 56, 73, 74, 82, 129, 130 + } ) ); + log( "\tHidden Blocks: " + hiddenBlocks ); + + replaceBlocks = getList( "anti-xray.replace-blocks", Arrays.asList( new Integer[] + { + 1, 5 + } ) ); + log( "\tReplace Blocks: " + replaceBlocks ); + + antiXrayInstance = new AntiXray( this ); + } + + public boolean zombieAggressiveTowardsVillager; + private void zombieAggressiveTowardsVillager() + { + zombieAggressiveTowardsVillager = getBoolean( "zombie-aggressive-towards-villager", true ); + log( "Zombie Aggressive Towards Villager: " + zombieAggressiveTowardsVillager ); + } + + public boolean nerfSpawnerMobs; + private void nerfSpawnerMobs() + { + nerfSpawnerMobs = getBoolean( "nerf-spawner-mobs", false ); + log( "Nerfing mobs spawned from spawners: " + nerfSpawnerMobs ); + } + + public boolean enableZombiePigmenPortalSpawns; + private void enableZombiePigmenPortalSpawns() + { + enableZombiePigmenPortalSpawns = getBoolean( "enable-zombie-pigmen-portal-spawns", true ); + log( "Allow Zombie Pigmen to spawn from portal blocks: " + enableZombiePigmenPortalSpawns ); + } + + public int maxBulkChunk; + private void bulkChunkCount() + { + maxBulkChunk = getInt( "max-bulk-chunks", 5 ); + log( "Sending up to " + maxBulkChunk + " chunks per packet" ); + } + + public int maxCollisionsPerEntity; + private void maxEntityCollision() + { + maxCollisionsPerEntity = getInt( "max-entity-collisions", 8 ); + log( "Max Entity Collisions: " + maxCollisionsPerEntity ); + } + + public int dragonDeathSoundRadius; + private void keepDragonDeathPerWorld() + { + dragonDeathSoundRadius = getInt( "dragon-death-sound-radius", 0 ); + } + + public int witherSpawnSoundRadius; + private void witherSpawnSoundRadius() + { + witherSpawnSoundRadius = getInt( "wither-spawn-sound-radius", 0 ); + } + + public int villageSeed; + public int largeFeatureSeed; + private void initWorldGenSeeds() + { + villageSeed = getInt( "seed-village", 10387312 ); + largeFeatureSeed = getInt( "seed-feature", 14357617 ); + log( "Custom Map Seeds: Village: " + villageSeed + " Feature: " + largeFeatureSeed ); + } + + public float walkExhaustion; + public float sprintExhaustion; + public float combatExhaustion; + public float regenExhaustion; + private void initHunger() + { + walkExhaustion = (float) getDouble( "hunger.walk-exhaustion", 0.2 ); + sprintExhaustion = (float) getDouble( "hunger.sprint-exhaustion", 0.8 ); + combatExhaustion = (float) getDouble( "hunger.combat-exhaustion", 0.3 ); + regenExhaustion = (float) getDouble( "hunger.regen-exhaustion", 3 ); + } + + public int currentPrimedTnt = 0; + public int maxTntTicksPerTick; + private void maxTntPerTick() { + if ( SpigotConfig.version < 7 ) + { + set( "max-tnt-per-tick", 100 ); + } + maxTntTicksPerTick = getInt( "max-tnt-per-tick", 100 ); + log( "Max TNT Explosions: " + maxTntTicksPerTick ); + } + + public int hangingTickFrequency; + private void hangingTickFrequency() + { + hangingTickFrequency = getInt( "hanging-tick-frequency", 100 ); + } + + public int expDespawnRate; + + private void expDespawnRate() { + expDespawnRate = getInt( "exp-despawn-rate", 6000 ); + log( "Experience Orb Despawn Rate: " + expDespawnRate ); + } + + public boolean mobsEnabled; + + private void mobsEnabled() { + mobsEnabled = getBoolean("mobs-enabled", true); + log("Mobs enabled: " + mobsEnabled); + } + + // Poweruser start + public boolean enderPearlsCanPassNonSolidBlocks; + private void enderPearlsCanPassNonSolidBlocks() { + enderPearlsCanPassNonSolidBlocks = getBoolean("enderPearlsCanPassNonSolidBlocks", false); + log("Enderpearls can pass non-solid blocks: " + enderPearlsCanPassNonSolidBlocks); + } + + public boolean updateMapItemsInPlayerInventory; + private void dontUpdateMapItemsInPlayerInventory() { + updateMapItemsInPlayerInventory = getBoolean( "updateMapItemsInPlayerInventory" , false); + } + + public boolean useAlternateEndSpawn; + private void useAlternateEndSpawn() { + useAlternateEndSpawn = getBoolean( "useAlternateEndSpawn", false); + } + // Poweruser end +} diff --git a/vspigot-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java b/vspigot-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java new file mode 100644 index 0000000..69aa098 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java @@ -0,0 +1,111 @@ +package org.spigotmc; + +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.scheduler.BukkitWorker; + +import java.lang.management.ManagementFactory; +import java.util.HashMap; +import java.util.Map; + +public class TicksPerSecondCommand extends Command { + + public TicksPerSecondCommand(String name) { + super(name); + this.description = "Gets the current ticks per second for the server"; + this.usageMessage = "/tps"; + this.setPermission("bukkit.command.tps"); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) { + return true; + } + + if (sender.hasPermission("bukkit.command.tps.advanced")) { + double[] tps = Bukkit.spigot().getTPS(); + String[] tpsAvg = new String[tps.length]; + + for (int i = 0; i < tps.length; i++) { + tpsAvg[i] = formatAdvancedTps(tps[i]); + } + + int entities = MinecraftServer.getServer().entities; + int activeEntities = MinecraftServer.getServer().activeEntities; + double activePercent = Math.round(10000.0 * activeEntities / entities) / 100.0; + + Map workers = new HashMap<>(), tasks = new HashMap<>(); + + for (BukkitWorker worker : Bukkit.getScheduler().getActiveWorkers()) { + workers.merge(worker.getOwner().getName(), 1, Integer::sum); + } + + for (BukkitTask task : Bukkit.getScheduler().getPendingTasks()) { + tasks.merge(task.getOwner().getName(), 1, Integer::sum); + } + + sender.sendMessage(ChatColor.GOLD + "TPS from last 1m, 5m, 15m: " + StringUtils.join(tpsAvg, ", ")); + sender.sendMessage(ChatColor.GOLD + "Full tick: " + formatTickTime(MinecraftServer.getServer().lastTickTime) + " ms"); + sender.sendMessage(ChatColor.GOLD + "Active entities: " + ChatColor.GREEN + activeEntities + "/" + entities + " (" + activePercent + "%)"); + sender.sendMessage(ChatColor.GOLD + "Online players: " + ChatColor.GREEN + Bukkit.getOnlinePlayers().size() + "/" + Bukkit.getMaxPlayers()); + sender.sendMessage(ChatColor.GOLD + "Thread count: " + ChatColor.GREEN + ManagementFactory.getThreadMXBean().getThreadCount()); + sender.sendMessage(""); + sender.sendMessage(ChatColor.GOLD + "Active Bukkit workers: " + ChatColor.GREEN + Bukkit.getScheduler().getActiveWorkers().size()); + + workers.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .forEach(e -> sender.sendMessage(ChatColor.YELLOW + e.getKey() + ": " + ChatColor.GREEN + e.getValue())); + + sender.sendMessage(""); + sender.sendMessage(ChatColor.GOLD + "Pending Bukkit tasks: " + ChatColor.GREEN + Bukkit.getScheduler().getPendingTasks().size()); + + tasks.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .forEach(e -> sender.sendMessage(ChatColor.YELLOW + e.getKey() + ": " + ChatColor.GREEN + e.getValue())); + } else { + double tps = Bukkit.spigot().getTPS()[1]; + StringBuilder tpsBuilder = new StringBuilder(); + + tpsBuilder.append(ChatColor.GOLD).append("Server performance: "); + tpsBuilder.append(formatBasicTps(tps)).append(ChatColor.GOLD).append("/20.0"); + tpsBuilder.append(" [").append(tps > 18.0 ? ChatColor.GREEN : tps > 16.0 ? ChatColor.YELLOW : ChatColor.RED); + + int i = 0; + + for (; i < Math.round(tps); i++) { + tpsBuilder.append("|"); + } + + tpsBuilder.append(ChatColor.DARK_GRAY); + + for (; i < 20; i++) { + tpsBuilder.append("|"); + } + + tpsBuilder.append(ChatColor.GOLD).append("]"); + sender.sendMessage(tpsBuilder.toString()); + } + + return true; + } + + private static String formatTickTime(double time) { + return (time < 40.0D ? ChatColor.GREEN : time < 60.0D ? ChatColor.YELLOW : ChatColor.RED).toString() + Math.round(time * 10.0D) / 10.0D; + } + + private static String formatAdvancedTps(double tps) { + return (tps > 18.0 ? ChatColor.GREEN : tps > 16.0 ? ChatColor.YELLOW : ChatColor.RED).toString() + Math.min(Math.round(tps * 100.0D) / 100.0, 20.0); + } + + private String formatBasicTps(double tps) { + return (tps > 18.0 ? ChatColor.GREEN : tps > 16.0 ? ChatColor.YELLOW : ChatColor.RED).toString() + Math.min(Math.round(tps * 10.0D) / 10.0D, 20.0D); + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/TrackingRange.java b/vspigot-server/src/main/java/org/spigotmc/TrackingRange.java new file mode 100644 index 0000000..fd56866 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/TrackingRange.java @@ -0,0 +1,66 @@ +package org.spigotmc; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityArrow; +import net.minecraft.server.EntityEnderPearl; +import net.minecraft.server.EntityExperienceOrb; +import net.minecraft.server.EntityFishingHook; +import net.minecraft.server.EntityGhast; +import net.minecraft.server.EntityItem; +import net.minecraft.server.EntityItemFrame; +import net.minecraft.server.EntityPainting; +import net.minecraft.server.EntityPlayer; + +public class TrackingRange +{ + + /** + * Gets the range an entity should be 'tracked' by players and visible in + * the client. + * + * @param entity + * @param defaultRange Default range defined by Mojang + * @return + */ + public static int getEntityTrackingRange(Entity entity, int defaultRange) + { + SpigotWorldConfig config = entity.world.spigotConfig; + if ( entity instanceof EntityPlayer ) + { + return config.playerTrackingRange; + // MineHQ start + } else if ( entity instanceof EntityArrow ) + { + return config.playerTrackingRange; + } else if (entity instanceof EntityEnderPearl) + { + return config.playerTrackingRange; + } else if (entity instanceof EntityFishingHook) + { + return config.playerTrackingRange; + } + // MineHQ end + else if ( entity.activationType == 1 ) + { + return config.monsterTrackingRange; + } else if ( entity instanceof EntityGhast ) + { + if ( config.monsterTrackingRange > config.monsterActivationRange ) + { + return config.monsterTrackingRange; + } else + { + return config.monsterActivationRange; + } + } else if ( entity.activationType == 2 ) + { + return config.animalTrackingRange; + } else if ( entity instanceof EntityItemFrame || entity instanceof EntityPainting || entity instanceof EntityItem || entity instanceof EntityExperienceOrb ) + { + return config.miscTrackingRange; + } else + { + return config.otherTrackingRange; + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/ValidateUtils.java b/vspigot-server/src/main/java/org/spigotmc/ValidateUtils.java new file mode 100644 index 0000000..58a9534 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/ValidateUtils.java @@ -0,0 +1,14 @@ +package org.spigotmc; + +public class ValidateUtils +{ + + public static String limit(String str, int limit) + { + if ( str.length() > limit ) + { + return str.substring( 0, limit ); + } + return str; + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/WatchdogThread.java b/vspigot-server/src/main/java/org/spigotmc/WatchdogThread.java new file mode 100644 index 0000000..0a588ac --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/WatchdogThread.java @@ -0,0 +1,127 @@ +package org.spigotmc; + +import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; +import java.lang.management.ThreadInfo; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; + +public class WatchdogThread extends Thread +{ + + private static WatchdogThread instance; + private final long timeoutTime; + private final boolean restart; + private volatile long lastTick; + private volatile boolean stopping; + + private WatchdogThread(long timeoutTime, boolean restart) + { + super( "Spigot Watchdog Thread" ); + this.timeoutTime = timeoutTime; + this.restart = restart; + } + + public static void doStart(int timeoutTime, boolean restart) + { + if ( instance == null ) + { + instance = new WatchdogThread( timeoutTime * 1000L, restart ); + instance.start(); + } + } + + public static void tick() + { + instance.lastTick = System.currentTimeMillis(); + } + + public static void doStop() + { + if ( instance != null ) + { + instance.stopping = true; + } + } + + @Override + public void run() + { + while ( !stopping ) + { + // + if ( lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime ) + { + Logger log = Bukkit.getServer().getLogger(); + log.log( Level.SEVERE, "The server has stopped responding!" ); + log.log( Level.SEVERE, "Please report this to http://www.spigotmc.org/" ); + log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" ); + log.log( Level.SEVERE, "Spigot version: " + Bukkit.getServer().getVersion() ); + // + if(net.minecraft.server.World.haveWeSilencedAPhysicsCrash) + { + log.log( Level.SEVERE, "------------------------------" ); + log.log( Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed" ); + log.log( Level.SEVERE, "near " + net.minecraft.server.World.blockLocation); + } + // + log.log( Level.SEVERE, "------------------------------" ); + log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" ); + dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE ), log ); + log.log( Level.SEVERE, "------------------------------" ); + // + log.log( Level.SEVERE, "Entire Thread Dump:" ); + ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true ); + for ( ThreadInfo thread : threads ) + { + dumpThread( thread, log ); + } + log.log( Level.SEVERE, "------------------------------" ); + + /* + * Let's try and interrupt the main thread + * + * */ + + MinecraftServer.getServer().primaryThread.interrupt(); + + break; + } + + try + { + sleep( 1000 ); + } catch ( InterruptedException ex ) + { + interrupt(); + } + } + } + + private static void dumpThread(ThreadInfo thread, Logger log) + { + log.log( Level.SEVERE, "------------------------------" ); + // + log.log( Level.SEVERE, "Current Thread: " + thread.getThreadName() ); + log.log( Level.SEVERE, "\tPID: " + thread.getThreadId() + + " | Suspended: " + thread.isSuspended() + + " | Native: " + thread.isInNative() + + " | State: " + thread.getThreadState() ); + if ( thread.getLockedMonitors().length != 0 ) + { + log.log( Level.SEVERE, "\tThread is waiting on monitor(s):" ); + for ( MonitorInfo monitor : thread.getLockedMonitors() ) + { + log.log( Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame() ); + } + } + log.log( Level.SEVERE, "\tStack:" ); + // + for ( StackTraceElement stack : thread.getStackTrace() ) + { + log.log( Level.SEVERE, "\t\t" + stack ); + } + } +} diff --git a/vspigot-server/src/main/java/org/spigotmc/WorldTileEntityList.java b/vspigot-server/src/main/java/org/spigotmc/WorldTileEntityList.java new file mode 100644 index 0000000..e7fd3d3 --- /dev/null +++ b/vspigot-server/src/main/java/org/spigotmc/WorldTileEntityList.java @@ -0,0 +1,174 @@ +package org.spigotmc; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import net.minecraft.server.*; +import net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +public class WorldTileEntityList extends HashSet { + private static final TObjectIntHashMap> tileEntityTickIntervals = + new TObjectIntHashMap>() {{ + // Use -1 for no ticking + // These TE's have empty tick methods, doing nothing. Never bother ticking them. + for (Class ignored : new Class[]{ + TileEntityChest.class, // PaperSpigot - Don't tick chests either + TileEntityEnderChest.class, // PaperSpigot - Don't tick chests either + TileEntityRecordPlayer.class, + TileEntityDispenser.class, + TileEntityDropper.class, + TileEntitySign.class, + TileEntityNote.class, + TileEntityEnderPortal.class, + TileEntityCommand.class, + TileEntitySkull.class, + TileEntityComparator.class, + TileEntityFlowerPot.class + }) { + put(ignored, -1); + } + + // does findPlayer lookup, so this helps performance to slow down + put(TileEntityEnchantTable.class, 20); + + // Slow things down that players won't notice due to craftbukkit "wall time" patches. + // These need to be investigated further before they can be safely used here + //put(TileEntityFurnace.class, 20); + //put(TileEntityBrewingStand.class, 10); + + // Vanilla controlled values - These are checks already done in vanilla, so don't tick on ticks we know + // won't do anything anyways + put(TileEntityBeacon.class, 80); + put(TileEntityLightDetector.class, 20); + }}; + private static int getInterval(Class cls) { + int tickInterval = tileEntityTickIntervals.get(cls); + return tickInterval != 0 ? tickInterval : 1; + } + + private static int getBucketId(TileEntity entity, Integer interval) { + return entity.tileId % interval; + } + + private final Map> tickList = Maps.newHashMap(); + private final WorldServer world; + + public WorldTileEntityList(World world) { + this.world = (WorldServer) world; + } + + + private Multimap getBucket(int interval) { + Multimap intervalBucket = tickList.get(interval); + if (intervalBucket == null) { + intervalBucket = ArrayListMultimap.create(); + tickList.put(interval, intervalBucket); + } + return intervalBucket; + } + + /** + * Adds the TileEntity to the tick list only if it is expected to tick + */ + @Override + public boolean add(TileEntity entity) { + if (entity.isAdded) { + return false; + } + + int interval = getInterval(entity.getClass()); + if (interval > 0) { + entity.isAdded = true; + int bucket = getBucketId(entity, interval); + Multimap typeBucket = getBucket(interval); + return typeBucket.put(bucket, entity); + } + return false; + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof TileEntity)) { + return false; + } + TileEntity entity = (TileEntity) o; + if (!entity.isAdded) { + return false; + } + entity.isAdded = false; + int interval = getInterval(entity.getClass()); + int bucket = getBucketId(entity, interval); + Multimap typeBucket = getBucket(interval); + return typeBucket.remove(bucket, entity); + } + + @Override + public Iterator iterator() { + return new WorldTileEntityIterator(); + } + + @Override + public boolean contains(Object o) { + return o instanceof TileEntity && ((TileEntity) o).isAdded; + } + + private class WorldTileEntityIterator implements Iterator { + private final Iterator>> intervalIterator; + private Map.Entry> intervalMap = null; + private Iterator listIterator = null; + + protected WorldTileEntityIterator() { + intervalIterator = tickList.entrySet().iterator(); + nextInterval(); + } + + private boolean nextInterval() { + listIterator = null; + if (intervalIterator.hasNext()) { + intervalMap = intervalIterator.next(); + + final Integer interval = intervalMap.getKey(); + final Multimap buckets = intervalMap.getValue(); + + int bucket = (int) (world.getTime() % interval); + + if (!buckets.isEmpty() && buckets.containsKey(bucket)) { + final Collection tileList = buckets.get(bucket); + + if (tileList != null && !tileList.isEmpty()) { + listIterator = tileList.iterator(); + return true; + } + } + } + + return false; + + } + + @Override + public boolean hasNext() { + do { + if (listIterator != null && listIterator.hasNext()) { + return true; + } + } while (nextInterval()); + return false; + } + + @Override + public TileEntity next() { + return listIterator.next(); + } + + @Override + public void remove() { + listIterator.remove(); + } + } +} diff --git a/vspigot-server/src/main/resources/blocks.json b/vspigot-server/src/main/resources/blocks.json new file mode 100644 index 0000000..8dd484d --- /dev/null +++ b/vspigot-server/src/main/resources/blocks.json @@ -0,0 +1 @@ +["0:0","1:0","1:1","1:2","1:3","1:4","1:5","1:6","2:0","3:0","3:1","3:2","4:0","5:0","5:1","5:2","5:3","5:4","5:5","6:0","6:1","6:2","6:3","6:4","6:5","6:8","6:9","6:10","6:11","6:12","6:13","7:0","8:0","8:1","8:2","8:3","8:4","8:5","8:6","8:7","8:8","8:9","8:10","8:11","8:12","8:13","8:14","8:15","9:0","9:1","9:2","9:3","9:4","9:5","9:6","9:7","9:8","9:9","9:10","9:11","9:12","9:13","9:14","9:15","10:0","10:1","10:2","10:3","10:4","10:5","10:6","10:7","10:8","10:9","10:10","10:11","10:12","10:13","10:14","10:15","11:0","11:1","11:2","11:3","11:4","11:5","11:6","11:7","11:8","11:9","11:10","11:11","11:12","11:13","11:14","11:15","12:0","12:1","13:0","14:0","15:0","16:0","17:0","17:1","17:2","17:3","17:4","17:5","17:6","17:7","17:8","17:9","17:10","17:11","17:12","17:13","17:14","17:15","18:0","18:1","18:2","18:3","18:4","18:5","18:6","18:7","18:8","18:9","18:10","18:11","18:12","18:13","18:14","18:15","19:0","19:1","20:0","21:0","22:0","23:0","23:1","23:2","23:3","23:4","23:5","23:8","23:9","23:10","23:11","23:12","23:13","24:0","24:1","24:2","25:0","26:0","26:1","26:2","26:3","26:8","26:9","26:10","26:11","26:12","26:13","26:14","26:15","27:0","27:1","27:2","27:3","27:4","27:5","27:8","27:9","27:10","27:11","27:12","27:13","28:0","28:1","28:2","28:3","28:4","28:5","28:8","28:9","28:10","28:11","28:12","28:13","29:0","29:1","29:2","29:3","29:4","29:5","29:8","29:9","29:10","29:11","29:12","29:13","30:0","31:0","31:1","31:2","32:0","33:0","33:1","33:2","33:3","33:4","33:5","33:8","33:9","33:10","33:11","33:12","33:13","34:0","34:1","34:2","34:3","34:4","34:5","34:8","34:9","34:10","34:11","34:12","34:13","35:0","35:1","35:2","35:3","35:4","35:5","35:6","35:7","35:8","35:9","35:10","35:11","35:12","35:13","35:14","35:15","36:0","36:1","36:2","36:3","36:4","36:5","36:8","36:9","36:10","36:11","36:12","36:13","37:0","38:0","38:1","38:2","38:3","38:4","38:5","38:6","38:7","38:8","39:0","40:0","41:0","42:0","43:0","43:1","43:2","43:3","43:4","43:5","43:6","43:7","43:8","43:9","43:10","43:11","43:12","43:13","43:14","43:15","44:0","44:1","44:2","44:3","44:4","44:5","44:6","44:7","44:8","44:9","44:10","44:11","44:12","44:13","44:14","44:15","45:0","46:0","46:1","47:0","48:0","49:0","50:1","50:2","50:3","50:4","50:5","51:0","51:1","51:2","51:3","51:4","51:5","51:6","51:7","51:8","51:9","51:10","51:11","51:12","51:13","51:14","51:15","52:0","53:0","53:1","53:2","53:3","53:4","53:5","53:6","53:7","54:2","54:3","54:4","54:5","55:0","55:1","55:2","55:3","55:4","55:5","55:6","55:7","55:8","55:9","55:10","55:11","55:12","55:13","55:14","55:15","56:0","57:0","58:0","59:0","59:1","59:2","59:3","59:4","59:5","59:6","59:7","60:0","60:1","60:2","60:3","60:4","60:5","60:6","60:7","61:2","61:3","61:4","61:5","62:2","62:3","62:4","62:5","63:0","63:1","63:2","63:3","63:4","63:5","63:6","63:7","63:8","63:9","63:10","63:11","63:12","63:13","63:14","63:15","64:0","64:1","64:2","64:3","64:4","64:5","64:6","64:7","64:8","64:9","64:10","64:11","65:2","65:3","65:4","65:5","66:0","66:1","66:2","66:3","66:4","66:5","66:6","66:7","66:8","66:9","67:0","67:1","67:2","67:3","67:4","67:5","67:6","67:7","68:2","68:3","68:4","68:5","69:0","69:1","69:2","69:3","69:4","69:5","69:6","69:7","69:8","69:9","69:10","69:11","69:12","69:13","69:14","69:15","70:0","70:1","71:0","71:1","71:2","71:3","71:4","71:5","71:6","71:7","71:8","71:9","71:10","71:11","72:0","72:1","73:0","74:0","75:1","75:2","75:3","75:4","75:5","76:1","76:2","76:3","76:4","76:5","77:0","77:1","77:2","77:3","77:4","77:5","77:8","77:9","77:10","77:11","77:12","77:13","78:0","78:1","78:2","78:3","78:4","78:5","78:6","78:7","79:0","80:0","81:0","81:1","81:2","81:3","81:4","81:5","81:6","81:7","81:8","81:9","81:10","81:11","81:12","81:13","81:14","81:15","82:0","83:0","83:1","83:2","83:3","83:4","83:5","83:6","83:7","83:8","83:9","83:10","83:11","83:12","83:13","83:14","83:15","84:0","84:1","85:0","86:0","86:1","86:2","86:3","87:0","88:0","89:0","90:1","90:2","91:0","91:1","91:2","91:3","92:0","92:1","92:2","92:3","92:4","92:5","92:6","93:0","93:1","93:2","93:3","93:4","93:5","93:6","93:7","93:8","93:9","93:10","93:11","93:12","93:13","93:14","93:15","94:0","94:1","94:2","94:3","94:4","94:5","94:6","94:7","94:8","94:9","94:10","94:11","94:12","94:13","94:14","94:15","95:0","95:1","95:2","95:3","95:4","95:5","95:6","95:7","95:8","95:9","95:10","95:11","95:12","95:13","95:14","95:15","96:0","96:1","96:2","96:3","96:4","96:5","96:6","96:7","96:8","96:9","96:10","96:11","96:12","96:13","96:14","96:15","97:0","97:1","97:2","97:3","97:4","97:5","98:0","98:1","98:2","98:3","99:0","99:1","99:2","99:3","99:4","99:5","99:6","99:7","99:8","99:9","99:10","99:14","99:15","100:0","100:1","100:2","100:3","100:4","100:5","100:6","100:7","100:8","100:9","100:10","100:14","100:15","101:0","102:0","103:0","104:0","104:1","104:2","104:3","104:4","104:5","104:6","104:7","105:0","105:1","105:2","105:3","105:4","105:5","105:6","105:7","106:0","106:1","106:2","106:3","106:4","106:5","106:6","106:7","106:8","106:9","106:10","106:11","106:12","106:13","106:14","106:15","107:0","107:1","107:2","107:3","107:4","107:5","107:6","107:7","107:8","107:9","107:10","107:11","107:12","107:13","107:14","107:15","108:0","108:1","108:2","108:3","108:4","108:5","108:6","108:7","109:0","109:1","109:2","109:3","109:4","109:5","109:6","109:7","110:0","111:0","112:0","113:0","114:0","114:1","114:2","114:3","114:4","114:5","114:6","114:7","115:0","115:1","115:2","115:3","116:0","117:0","117:1","117:2","117:3","117:4","117:5","117:6","117:7","118:0","118:1","118:2","118:3","119:0","120:0","120:1","120:2","120:3","120:4","120:5","120:6","120:7","121:0","122:0","123:0","124:0","125:0","125:1","125:2","125:3","125:4","125:5","126:0","126:1","126:2","126:3","126:4","126:5","126:8","126:9","126:10","126:11","126:12","126:13","127:0","127:1","127:2","127:3","127:4","127:5","127:6","127:7","127:8","127:9","127:10","127:11","128:0","128:1","128:2","128:3","128:4","128:5","128:6","128:7","129:0","130:2","130:3","130:4","130:5","131:0","131:1","131:2","131:3","131:4","131:5","131:6","131:7","131:8","131:9","131:10","131:11","131:12","131:13","131:14","131:15","132:0","132:1","132:2","132:3","132:4","132:5","132:6","132:7","132:8","132:9","132:10","132:11","132:12","132:13","132:14","132:15","133:0","134:0","134:1","134:2","134:3","134:4","134:5","134:6","134:7","135:0","135:1","135:2","135:3","135:4","135:5","135:6","135:7","136:0","136:1","136:2","136:3","136:4","136:5","136:6","136:7","137:0","137:1","138:0","139:0","139:1","140:0","140:1","140:2","140:3","140:4","140:5","140:6","140:7","140:8","140:9","140:10","140:11","140:12","140:13","140:14","140:15","141:0","141:1","141:2","141:3","141:4","141:5","141:6","141:7","142:0","142:1","142:2","142:3","142:4","142:5","142:6","142:7","143:0","143:1","143:2","143:3","143:4","143:5","143:8","143:9","143:10","143:11","143:12","143:13","144:0","144:1","144:2","144:3","144:4","144:5","144:8","144:9","144:10","144:11","144:12","144:13","145:0","145:1","145:2","145:3","145:4","145:5","145:6","145:7","145:8","145:9","145:10","145:11","146:2","146:3","146:4","146:5","147:0","147:1","147:2","147:3","147:4","147:5","147:6","147:7","147:8","147:9","147:10","147:11","147:12","147:13","147:14","147:15","148:0","148:1","148:2","148:3","148:4","148:5","148:6","148:7","148:8","148:9","148:10","148:11","148:12","148:13","148:14","148:15","149:0","149:1","149:2","149:3","149:4","149:5","149:6","149:7","149:8","149:9","149:10","149:11","149:12","149:13","149:14","149:15","150:0","150:1","150:2","150:3","150:4","150:5","150:6","150:7","150:8","150:9","150:10","150:11","150:12","150:13","150:14","150:15","151:0","151:1","151:2","151:3","151:4","151:5","151:6","151:7","151:8","151:9","151:10","151:11","151:12","151:13","151:14","151:15","152:0","153:0","154:0","154:2","154:3","154:4","154:5","154:8","154:10","154:11","154:12","154:13","155:0","155:1","155:2","155:3","155:4","156:0","156:1","156:2","156:3","156:4","156:5","156:6","156:7","157:0","157:1","157:2","157:3","157:4","157:5","157:8","157:9","157:10","157:11","157:12","157:13","158:0","158:1","158:2","158:3","158:4","158:5","158:8","158:9","158:10","158:11","158:12","158:13","159:0","159:1","159:2","159:3","159:4","159:5","159:6","159:7","159:8","159:9","159:10","159:11","159:12","159:13","159:14","159:15","160:0","160:1","160:2","160:3","160:4","160:5","160:6","160:7","160:8","160:9","160:10","160:11","160:12","160:13","160:14","160:15","161:0","161:1","161:4","161:5","161:8","161:9","161:12","161:13","162:0","162:1","162:4","162:5","162:8","162:9","162:12","162:13","163:0","163:1","163:2","163:3","163:4","163:5","163:6","163:7","164:0","164:1","164:2","164:3","164:4","164:5","164:6","164:7","165:0","166:0","167:0","167:1","167:2","167:3","167:4","167:5","167:6","167:7","167:8","167:9","167:10","167:11","167:12","167:13","167:14","167:15","168:0","168:1","168:2","169:0","170:0","170:4","170:8","171:0","171:1","171:2","171:3","171:4","171:5","171:6","171:7","171:8","171:9","171:10","171:11","171:12","171:13","171:14","171:15","172:0","173:0","174:0","175:0","175:1","175:2","175:3","175:4","175:5","175:8","176:0","176:1","176:2","176:3","176:4","176:5","176:6","176:7","176:8","176:9","176:10","176:11","176:12","176:13","176:14","176:15","177:2","177:3","177:4","177:5","178:0","178:1","178:2","178:3","178:4","178:5","178:6","178:7","178:8","178:9","178:10","178:11","178:12","178:13","178:14","178:15","179:0","179:1","179:2","180:0","180:1","180:2","180:3","180:4","180:5","180:6","180:7","181:0","181:8","182:0","182:8","183:0","183:1","183:2","183:3","183:4","183:5","183:6","183:7","183:8","183:9","183:10","183:11","183:12","183:13","183:14","183:15","184:0","184:1","184:2","184:3","184:4","184:5","184:6","184:7","184:8","184:9","184:10","184:11","184:12","184:13","184:14","184:15","185:0","185:1","185:2","185:3","185:4","185:5","185:6","185:7","185:8","185:9","185:10","185:11","185:12","185:13","185:14","185:15","186:0","186:1","186:2","186:3","186:4","186:5","186:6","186:7","186:8","186:9","186:10","186:11","186:12","186:13","186:14","186:15","187:0","187:1","187:2","187:3","187:4","187:5","187:6","187:7","187:8","187:9","187:10","187:11","187:12","187:13","187:14","187:15","188:0","189:0","190:0","191:0","192:0","193:0","193:1","193:2","193:3","193:4","193:5","193:6","193:7","193:8","193:9","193:10","193:11","194:0","194:1","194:2","194:3","194:4","194:5","194:6","194:7","194:8","194:9","194:10","194:11","195:0","195:1","195:2","195:3","195:4","195:5","195:6","195:7","195:8","195:9","195:10","195:11","196:0","196:1","196:2","196:3","196:4","196:5","196:6","196:7","196:8","196:9","196:10","196:11","197:0","197:1","197:2","197:3","197:4","197:5","197:6","197:7","197:8","197:9","197:10","197:11"] diff --git a/vspigot-server/src/main/resources/configurations/bukkit.yml b/vspigot-server/src/main/resources/configurations/bukkit.yml new file mode 100644 index 0000000..096e01e --- /dev/null +++ b/vspigot-server/src/main/resources/configurations/bukkit.yml @@ -0,0 +1,53 @@ +# This is the main configuration file for Bukkit. +# As you can see, there's actually not that much to configure without any plugins. +# For a reference for any variable inside this file, check out the Bukkit Wiki at +# http://wiki.bukkit.org/Bukkit.yml +# +# If you need help on this file, feel free to join us on irc or leave a message +# on the forums asking for advice. +# +# IRC: #spigot @ irc.spi.gt +# (If this means nothing to you, just go to http://irc.spi.gt/iris/?nick=&channels=spigot ) +# Forums: http://www.spigotmc.org/forums/help.40/ +# Bug tracker: http://www.spigotmc.org/forums/bugs-feature-requests.8/ + + +settings: + allow-end: true + warn-on-overload: true + permissions-file: permissions.yml + update-folder: update + ping-packet-limit: 100 + use-exact-login-location: false + plugin-profiling: false + connection-throttle: 4000 + query-plugins: true + require-all-plugins: true + deprecated-verbose: default + shutdown-message: Server closed +spawn-limits: + monsters: 70 + animals: 15 + water-animals: 5 + ambient: 15 +chunk-gc: + period-in-ticks: 600 + load-threshold: 0 +ticks-per: + animal-spawns: 400 + monster-spawns: 1 + autosave: 6000 +auto-updater: + enabled: true + on-broken: [warn-console, warn-ops] + on-update: [warn-console, warn-ops] + preferred-channel: rb + host: dl.bukkit.org + suggest-channels: true +aliases: now-in-commands.yml +database: + username: bukkit + isolation: SERIALIZABLE + driver: org.sqlite.JDBC + password: walrus + url: jdbc:sqlite:{DIR}{NAME}.db diff --git a/vspigot-server/src/main/resources/configurations/commands.yml b/vspigot-server/src/main/resources/configurations/commands.yml new file mode 100644 index 0000000..d6bcf5c --- /dev/null +++ b/vspigot-server/src/main/resources/configurations/commands.yml @@ -0,0 +1,16 @@ +# This is the commands configuration file for Bukkit. +# For documentation on how to make use of this file, check out the Bukkit Wiki at +# http://wiki.bukkit.org/Commands.yml +# +# If you need help on this file, feel free to join us on irc or leave a message +# on the forums asking for advice. +# +# IRC: #spigot @ irc.spi.gt +# (If this means nothing to you, just go to http://irc.spi.gt/iris/?nick=&channels=spigot ) +# Forums: http://www.spigotmc.org/forums/help.40/ +# Bug tracker: http://www.spigotmc.org/forums/bugs-feature-requests.8/ + +command-block-overrides: [] +aliases: + icanhasbukkit: + - "version $1-" diff --git a/vspigot-server/src/main/resources/configurations/help.yml b/vspigot-server/src/main/resources/configurations/help.yml new file mode 100644 index 0000000..15c3d07 --- /dev/null +++ b/vspigot-server/src/main/resources/configurations/help.yml @@ -0,0 +1,55 @@ +# This is the help configuration file for Bukkit. +# +# By default you do not need to modify this file. Help topics for all plugin commands are automatically provided by +# or extracted from your installed plugins. You only need to modify this file if you wish to add new help pages to +# your server or override the help pages of existing plugin commands. +# +# This file is divided up into the following parts: +# -- general-topics: lists admin defined help topics +# -- index-topics: lists admin defined index topics +# -- amend-topics: lists topic amendments to apply to existing help topics +# -- ignore-plugins: lists any plugins that should be excluded from help +# +# Examples are given below. When amending command topic, the string will be replaced with the existing value +# in the help topic. Color codes can be used in topic text. The color code character is & followed by 0-F. +# ================================================================ +# +# Set this to true to list the individual command help topics in the master help. +# command-topics-in-master-index: true +# +# Each general topic will show up as a separate topic in the help index along with all the plugin command topics. +# general-topics: +# Rules: +# shortText: Rules of the server +# fullText: | +# &61. Be kind to your fellow players. +# &B2. No griefing. +# &D3. No swearing. +# permission: topics.rules +# +# Each index topic will show up as a separate sub-index in the help index along with all the plugin command topics. +# To override the default help index (displayed when the user executes /help), name the index topic "Default". +# index-topics: +# Ban Commands: +# shortText: Player banning commands +# preamble: Moderator - do not abuse these commands +# permission: op +# commands: +# - /ban +# - /ban-ip +# - /banlist +# +# Topic amendments are used to change the content of automatically generated plugin command topics. +# amended-topics: +# /stop: +# shortText: Stops the server cold....in its tracks! +# fullText: - This kills the server. +# permission: you.dont.have +# +# Any plugin in the ignored plugins list will be excluded from help. The name must match the name displayed by +# the /plugins command. Ignore "Bukkit" to remove the standard bukkit commands from the index. Ignore "All" +# to completely disable automatic help topic generation. +# ignore-plugins: +# - PluginNameOne +# - PluginNameTwo +# - PluginNameThree diff --git a/vspigot-server/src/main/resources/knockback.json b/vspigot-server/src/main/resources/knockback.json new file mode 100644 index 0000000..4887c18 --- /dev/null +++ b/vspigot-server/src/main/resources/knockback.json @@ -0,0 +1 @@ +{"global-profile":"Default","profiles":{"Default":{"horizontal":"0.35","extraHorizontal":"0.425","extraVertical":"0.085","vertical":"0.35","verticalLimit":"0.4","friction":"2.0"}}} \ No newline at end of file diff --git a/vspigot-server/src/main/resources/log4j2.xml b/vspigot-server/src/main/resources/log4j2.xml new file mode 100644 index 0000000..abb6131 --- /dev/null +++ b/vspigot-server/src/main/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vspigot-server/src/main/resources/org/spigotmc/SneakyThrow.class b/vspigot-server/src/main/resources/org/spigotmc/SneakyThrow.class new file mode 100644 index 0000000..6355186 Binary files /dev/null and b/vspigot-server/src/main/resources/org/spigotmc/SneakyThrow.class differ